Skip to content

Commit

Permalink
Merge pull request #12564 from woocommerce/issue/12539-blaze-push-not…
Browse files Browse the repository at this point in the history
…ification-navigation

Issue/12539 blaze push notification navigation
  • Loading branch information
JorgeMucientes authored Sep 11, 2024
2 parents 114603f + 318fe1f commit 589df25
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ data class Notification(
@IgnoredOnParcel
val isReviewNotification = noteType == WooNotificationType.PRODUCT_REVIEW

@IgnoredOnParcel
val isBlazeNotification = noteType == WooNotificationType.BLAZE_APPROVED_NOTE ||
noteType == WooNotificationType.BLAZE_REJECTED_NOTE ||
noteType == WooNotificationType.BLAZE_CANCELLED_NOTE ||
noteType == WooNotificationType.BLAZE_PERFORMED_NOTE

/**
* Notifications are grouped based on the notification type and the store the notification belongs to.
*
Expand Down Expand Up @@ -74,6 +80,11 @@ fun NotificationModel.getUniqueId(): Long {
return when (this.type) {
NotificationModel.Kind.STORE_ORDER -> this.meta?.ids?.order ?: 0L
NotificationModel.Kind.COMMENT -> this.meta?.ids?.comment ?: 0L
NotificationModel.Kind.BLAZE_APPROVED_NOTE,
NotificationModel.Kind.BLAZE_REJECTED_NOTE,
NotificationModel.Kind.BLAZE_CANCELLED_NOTE,
NotificationModel.Kind.BLAZE_PERFORMED_NOTE -> this.meta?.ids?.campaignId ?: 0L

else -> 0L
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,20 @@ enum class WooNotificationType {
NEW_ORDER,
PRODUCT_REVIEW,
LOCAL_REMINDER,
BLAZE
BLAZE_APPROVED_NOTE,
BLAZE_REJECTED_NOTE,
BLAZE_CANCELLED_NOTE,
BLAZE_PERFORMED_NOTE,
}

fun NotificationModel.getWooType(): WooNotificationType {
return when (this.type) {
NotificationModel.Kind.STORE_ORDER -> WooNotificationType.NEW_ORDER
NotificationModel.Kind.COMMENT -> WooNotificationType.PRODUCT_REVIEW
NotificationModel.Kind.BLAZE_APPROVED_NOTE -> WooNotificationType.BLAZE_APPROVED_NOTE
NotificationModel.Kind.BLAZE_REJECTED_NOTE -> WooNotificationType.BLAZE_REJECTED_NOTE
NotificationModel.Kind.BLAZE_CANCELLED_NOTE -> WooNotificationType.BLAZE_CANCELLED_NOTE
NotificationModel.Kind.BLAZE_PERFORMED_NOTE -> WooNotificationType.BLAZE_PERFORMED_NOTE
else -> WooNotificationType.LOCAL_REMINDER
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ class BlazeCampaignListViewModel @Inject constructor(
if (navArgs.isPostCampaignCreation) {
showCampaignCelebrationIfNeeded()
}
if (navArgs.campaignId != null) {
triggerEvent(
ShowCampaignDetails(
url = blazeUrlsHelper.buildCampaignDetailsUrl(navArgs.campaignId!!),
urlToTriggerExit = blazeUrlsHelper.buildCampaignsListUrl()
)
)
}
launch {
loadCampaigns(offset = 0)
}
Expand Down Expand Up @@ -109,14 +117,13 @@ class BlazeCampaignListViewModel @Inject constructor(
}

private fun onCampaignClicked(campaignId: String) {
val url = blazeUrlsHelper.buildCampaignDetailsUrl(campaignId)
analyticsTrackerWrapper.track(
stat = BLAZE_CAMPAIGN_DETAIL_SELECTED,
properties = mapOf(AnalyticsTracker.KEY_BLAZE_SOURCE to BlazeFlowSource.CAMPAIGN_LIST.trackingName)
)
triggerEvent(
ShowCampaignDetails(
url = url,
url = blazeUrlsHelper.buildCampaignDetailsUrl(campaignId),
urlToTriggerExit = blazeUrlsHelper.buildCampaignsListUrl()
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ import com.woocommerce.android.ui.main.MainActivityViewModel.RestartActivityForP
import com.woocommerce.android.ui.main.MainActivityViewModel.ShortcutOpenOrderCreation
import com.woocommerce.android.ui.main.MainActivityViewModel.ShortcutOpenPayments
import com.woocommerce.android.ui.main.MainActivityViewModel.ShowFeatureAnnouncement
import com.woocommerce.android.ui.main.MainActivityViewModel.ViewBlazeCampaignDetail
import com.woocommerce.android.ui.main.MainActivityViewModel.ViewBlazeCampaignList
import com.woocommerce.android.ui.main.MainActivityViewModel.ViewMyStoreStats
import com.woocommerce.android.ui.main.MainActivityViewModel.ViewOrderDetail
import com.woocommerce.android.ui.main.MainActivityViewModel.ViewOrderList
Expand Down Expand Up @@ -786,7 +788,7 @@ class MainActivity :
intent.removeExtra(FIELD_REMOTE_NOTIFICATION)
intent.removeExtra(FIELD_PUSH_ID)

viewModel.handleIncomingNotification(localPushId, notification)
viewModel.onPushNotificationTapped(localPushId, notification)
} else if (localNotification != null) {
intent.removeExtra(FIELD_LOCAL_NOTIFICATION)
viewModel.onLocalNotificationTapped(localNotification)
Expand All @@ -803,6 +805,8 @@ class MainActivity :
is ViewOrderDetail -> showOrderDetail(event)
is ViewReviewDetail -> showReviewDetail(event.uniqueId, launchedFromNotification = true)
is ViewReviewList -> showReviewList()
is ViewBlazeCampaignDetail -> showBlazeCampaignList(event.campaignId, event.isOpenedFromPush)
ViewBlazeCampaignList -> showBlazeCampaignList(campaignId = null)
is RestartActivityEvent -> onRestartActivityEvent(event)
is ShowFeatureAnnouncement -> navigateToFeatureAnnouncement(event)
is ViewUrlInWebView -> navigateToWebView(event)
Expand Down Expand Up @@ -843,6 +847,18 @@ class MainActivity :
observeBottomBarState()
}

private fun showBlazeCampaignList(campaignId: String?, isOpenedFromPush: Boolean = false) {
binding.bottomNav.currentPosition = MORE
binding.bottomNav.active(MORE.position)

navController.navigateSafely(
MoreMenuFragmentDirections.actionMoreMenuToBlazeCampaignListFragment(
campaignId = campaignId
),
skipThrottling = isOpenedFromPush
)
}

private fun observeNotificationsPermissionBarVisibility() {
viewModel.isNotificationsPermissionCardVisible.observe(this) { isVisible ->
if (isVisible) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ import com.woocommerce.android.model.FeatureAnnouncement
import com.woocommerce.android.model.Notification
import com.woocommerce.android.notifications.NotificationChannelType
import com.woocommerce.android.notifications.UnseenReviewsCountHandler
import com.woocommerce.android.notifications.WooNotificationType.BLAZE_APPROVED_NOTE
import com.woocommerce.android.notifications.WooNotificationType.BLAZE_CANCELLED_NOTE
import com.woocommerce.android.notifications.WooNotificationType.BLAZE_PERFORMED_NOTE
import com.woocommerce.android.notifications.WooNotificationType.BLAZE_REJECTED_NOTE
import com.woocommerce.android.notifications.WooNotificationType.LOCAL_REMINDER
import com.woocommerce.android.notifications.WooNotificationType.NEW_ORDER
import com.woocommerce.android.notifications.WooNotificationType.PRODUCT_REVIEW
import com.woocommerce.android.notifications.local.LocalNotificationType
import com.woocommerce.android.notifications.local.LocalNotificationType.BLAZE_ABANDONED_CAMPAIGN_REMINDER
import com.woocommerce.android.notifications.local.LocalNotificationType.BLAZE_NO_CAMPAIGN_REMINDER
Expand Down Expand Up @@ -110,7 +117,7 @@ class MainActivityViewModel @Inject constructor(
)
}

fun handleIncomingNotification(localPushId: Int, notification: Notification?) {
fun onPushNotificationTapped(localPushId: Int, notification: Notification?) {
notification?.let {
// update current selectSite based on the current notification
val currentSite = selectedSite.get()
Expand All @@ -119,8 +126,8 @@ class MainActivityViewModel @Inject constructor(
changeSiteAndRestart(it.remoteSiteId, RestartActivityForPushNotification(localPushId, notification))
} else {
when (localPushId) {
it.getGroupPushId() -> onGroupMessageOpened(it.channelType, it.remoteSiteId)
else -> onSingleNotificationOpened(localPushId, it)
it.getGroupPushId() -> onGroupMessageOpened(it)
else -> onSinglePushNotificationOpened(localPushId, it)
}
}
} ?: run {
Expand Down Expand Up @@ -171,29 +178,53 @@ class MainActivityViewModel @Inject constructor(
}
}

private fun onGroupMessageOpened(notificationChannelType: NotificationChannelType, remoteSiteId: Long) {
notificationHandler.markNotificationsOfTypeTapped(notificationChannelType)
notificationHandler.removeNotificationsOfTypeFromSystemsBar(notificationChannelType, remoteSiteId)
when (notificationChannelType) {
private fun onGroupMessageOpened(notification: Notification) {
notificationHandler.markNotificationsOfTypeTapped(notification.channelType)
notificationHandler.removeNotificationsOfTypeFromSystemsBar(notification.channelType, notification.remoteSiteId)
when (notification.channelType) {
NotificationChannelType.NEW_ORDER -> triggerEvent(ViewOrderList)
NotificationChannelType.REVIEW -> triggerEvent(ViewReviewList)
else -> triggerEvent(ViewMyStoreStats)
NotificationChannelType.OTHER -> if (notification.isBlazeNotification) {
triggerEvent(ViewBlazeCampaignList)
} else {
triggerEvent(ViewMyStoreStats)
}
}
}

private fun onSingleNotificationOpened(localPushId: Int, notification: Notification) {
private fun onSinglePushNotificationOpened(localPushId: Int, notification: Notification) {
notificationHandler.markNotificationTapped(notification.remoteNoteId)
notificationHandler.removeNotificationByNotificationIdFromSystemsBar(localPushId)
if (notification.channelType == NotificationChannelType.REVIEW) {
analyticsTrackerWrapper.track(REVIEW_OPEN)
triggerEvent(ViewReviewDetail(notification.uniqueId))
} else if (notification.channelType == NotificationChannelType.NEW_ORDER) {
if (siteStore.getSiteBySiteId(notification.remoteSiteId) != null) {
triggerEvent(ViewOrderDetail(notification.uniqueId, notification.remoteNoteId))
} else {
// the site does not exist locally, open order list
triggerEvent(ViewOrderList)
when (notification.noteType) {
NEW_ORDER -> {
when {
siteStore.getSiteBySiteId(notification.remoteSiteId) != null -> triggerEvent(
ViewOrderDetail(
notification.uniqueId,
notification.remoteNoteId
)
)

else -> triggerEvent(ViewOrderList)
}
}

PRODUCT_REVIEW -> {
analyticsTrackerWrapper.track(REVIEW_OPEN)
triggerEvent(ViewReviewDetail(notification.uniqueId))
}

BLAZE_APPROVED_NOTE,
BLAZE_REJECTED_NOTE,
BLAZE_CANCELLED_NOTE,
BLAZE_PERFORMED_NOTE -> triggerEvent(
ViewBlazeCampaignDetail(
campaignId = notification.uniqueId.toString(),
isOpenedFromPush = true
)
)

LOCAL_REMINDER -> error("Local reminder notification should not be handled here")
}
}

Expand Down Expand Up @@ -314,6 +345,7 @@ class MainActivityViewModel @Inject constructor(
data class ViewUrlInWebView(
val url: String,
) : Event()

object ShortcutOpenPayments : Event()
object ShortcutOpenOrderCreation : Event()
object LaunchBlazeCampaignCreation : Event()
Expand All @@ -330,6 +362,8 @@ class MainActivityViewModel @Inject constructor(
data class ShowFeatureAnnouncement(val announcement: FeatureAnnouncement) : Event()
data class ViewReviewDetail(val uniqueId: Long) : Event()
data class ViewOrderDetail(val uniqueId: Long, val remoteNoteId: Long) : Event()
data class ViewBlazeCampaignDetail(val campaignId: String, val isOpenedFromPush: Boolean) : Event()
object ViewBlazeCampaignList : Event()
data class ShowPrivacyPreferenceUpdatedFailed(val analyticsEnabled: Boolean) : Event()
object ShowPrivacySettings : Event()
data class ShowPrivacySettingsWithError(val requestedAnalyticsValue: RequestedAnalyticsValue) : Event()
Expand Down
5 changes: 5 additions & 0 deletions WooCommerce/src/main/res/navigation/nav_graph_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,11 @@
android:name="isPostCampaignCreation"
android:defaultValue="false"
app:argType="boolean" />
<argument
android:name="campaignId"
android:defaultValue="@null"
app:argType="string"
app:nullable="true" />
</fragment>
<fragment
android:id="@+id/orderConnectivityToolFragment"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.woocommerce.android.extensions.NumberExtensionsWrapper
import com.woocommerce.android.tools.SelectedSite
import com.woocommerce.android.ui.blaze.BlazeRepository
import com.woocommerce.android.ui.blaze.BlazeUrlsHelper
import com.woocommerce.android.ui.blaze.campaigs.BlazeCampaignListViewModel.ShowCampaignDetails
import com.woocommerce.android.util.CurrencyFormatter
import com.woocommerce.android.util.captureValues
import com.woocommerce.android.util.runAndCaptureValues
Expand All @@ -15,6 +16,7 @@ import com.woocommerce.android.viewmodel.MultiLiveEvent.Event
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flow
import org.assertj.core.api.Assertions
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.any
Expand Down Expand Up @@ -54,6 +56,8 @@ class BlazeCampaignListViewModelTest : BaseUnitTest() {
whenever(blazeCampaignsStore.observeBlazeCampaigns(selectedSite.get())).thenReturn(campaignsEntityFlow)
whenever(blazeCampaignsStore.fetchBlazeCampaigns(any(), any(), any(), any(), eq(null)))
.thenReturn(BlazeCampaignsResult(EMPTY_BLAZE_CAMPAIGN_MODEL))
whenever(blazeUrlsHelper.buildCampaignDetailsUrl(CAMPAIGN_ID)).thenReturn(CAMPAIGN_URL)
whenever(blazeUrlsHelper.buildCampaignsListUrl()).thenReturn(CAMPAIGN_LIST_URL)
}

@Test
Expand Down Expand Up @@ -146,9 +150,24 @@ class BlazeCampaignListViewModelTest : BaseUnitTest() {
Assertions.assertThat(state.isCampaignCelebrationShown).isFalse()
}

private fun createViewModel(isPostCampaignCreation: Boolean = false) {
@Test
fun `when screen opened from Blaze campaign status push notification, navigate to campaign detail `() =
testBlocking {
createViewModel(campaignId = CAMPAIGN_ID)
var event: ShowCampaignDetails? = null
viewModel.event.observeForever {
if (it is ShowCampaignDetails) event = it
}

assertThat(event).isEqualTo(ShowCampaignDetails(CAMPAIGN_URL, CAMPAIGN_LIST_URL))
}

private fun createViewModel(isPostCampaignCreation: Boolean = false, campaignId: String? = null) {
viewModel = BlazeCampaignListViewModel(
savedStateHandle = BlazeCampaignListFragmentArgs(isPostCampaignCreation).toSavedStateHandle(),
savedStateHandle = BlazeCampaignListFragmentArgs(
isPostCampaignCreation,
campaignId
).toSavedStateHandle(),
blazeCampaignsStore = blazeCampaignsStore,
selectedSite = selectedSite,
blazeUrlsHelper = blazeUrlsHelper,
Expand All @@ -171,6 +190,8 @@ class BlazeCampaignListViewModelTest : BaseUnitTest() {
const val TOTAL_BUDGET = 100.0
const val SPENT_BUDGET = 0.0
const val TARGET_URN = "urn:wpcom:post:199247490:9"
const val CAMPAIGN_URL = "https://wordpress.com/campaigns"
const val CAMPAIGN_LIST_URL = "https://wordpress.com/campaigns/list"

val BLAZE_CAMPAIGN_MODEL = BlazeCampaignModel(
campaignId = CAMPAIGN_ID,
Expand Down
Loading

0 comments on commit 589df25

Please sign in to comment.