diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/MySiteFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/MySiteFragment.kt index 8be71e0f32f1..0ce7ee9421ca 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/MySiteFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/MySiteFragment.kt @@ -55,6 +55,7 @@ import org.wordpress.android.ui.posts.PostUtils import org.wordpress.android.ui.reader.ReaderActivityLauncher import org.wordpress.android.ui.reader.tracker.ReaderTracker import org.wordpress.android.ui.newstats.NewStatsActivity +import org.wordpress.android.ui.newstats.StatsPeriod import org.wordpress.android.ui.stats.StatsTimeframe import org.wordpress.android.ui.stats.refresh.utils.StatsLaunchedFrom import org.wordpress.android.ui.uploads.UploadService @@ -648,6 +649,8 @@ class MySiteFragment : Fragment(R.layout.my_site_fragment), StatsLaunchedFrom.TODAY_STATS_CARD ) + is SiteNavigationAction.OpenNewStatsForToday -> NewStatsActivity.start(requireContext(), StatsPeriod.Today) + is SiteNavigationAction.OpenExternalUrl -> ActivityLauncher.openUrlExternal(requireActivity(), action.url) is SiteNavigationAction.OpenUrlInWebView -> diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/MySiteViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/MySiteViewModel.kt index 7fe427ddd878..8848391b8905 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/MySiteViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/MySiteViewModel.kt @@ -28,6 +28,7 @@ import org.wordpress.android.ui.mysite.MySiteViewModel.State.SiteSelected import org.wordpress.android.ui.mysite.cards.DashboardCardsViewModelSlice import org.wordpress.android.ui.mysite.cards.siteinfo.SiteInfoHeaderCardViewModelSlice import org.wordpress.android.ui.mysite.items.DashboardItemsViewModelSlice +import org.wordpress.android.ui.newstats.NewStatsRouting import org.wordpress.android.ui.pages.SnackbarMessageHolder import org.wordpress.android.ui.mediapicker.MediaPickerActivity import org.wordpress.android.ui.posts.BasicDialogViewModel @@ -70,6 +71,7 @@ class MySiteViewModel @Inject constructor( private val gutenbergEditorPreloader: GutenbergEditorPreloader, private val siteConnectivityBannerViewModelSlice: SiteConnectivityBannerViewModelSlice, private val gutenbergKitAnnouncementController: GutenbergKitAnnouncementController, + private val newStatsRouting: NewStatsRouting, ) : ScopedViewModel(mainDispatcher) { private val _onSnackbarMessage = MutableLiveData>() private val _onNavigation = MutableLiveData>() @@ -243,7 +245,11 @@ class MySiteViewModel @Inject constructor( fun handleSuccessfulLoginResult() { selectedSiteRepository.getSelectedSite()?.let { site -> _onNavigation.value = Event( - SiteNavigationAction.OpenStats(site) + if (newStatsRouting.isNewStatsEnabled()) { + SiteNavigationAction.OpenNewStats + } else { + SiteNavigationAction.OpenStats(site) + } ) } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/SiteNavigationAction.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/SiteNavigationAction.kt index 37dcedf13f3a..4bc60d2c37f6 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/SiteNavigationAction.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/SiteNavigationAction.kt @@ -52,6 +52,7 @@ sealed class SiteNavigationAction { data class EditDraftPost(val site: SiteModel, val postId: Int) : SiteNavigationAction() data class EditScheduledPost(val site: SiteModel, val postId: Int) : SiteNavigationAction() data class OpenStatsByDay(val site: SiteModel) : SiteNavigationAction() + object OpenNewStatsForToday : SiteNavigationAction() data class OpenExternalUrl(val url: String) : SiteNavigationAction() data class OpenUrlInWebView(val url: String) : SiteNavigationAction() data class OpenDeepLink(val url: String) : SiteNavigationAction() diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/todaysstats/TodaysStatsViewModelSlice.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/todaysstats/TodaysStatsViewModelSlice.kt index 068f35d7160b..8ce631295681 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/todaysstats/TodaysStatsViewModelSlice.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/todaysstats/TodaysStatsViewModelSlice.kt @@ -9,6 +9,7 @@ import org.wordpress.android.ui.mysite.MySiteCardAndItemBuilderParams.TodaysStat import org.wordpress.android.ui.mysite.SelectedSiteRepository import org.wordpress.android.ui.mysite.SiteNavigationAction import org.wordpress.android.ui.mysite.cards.dashboard.CardsTracker +import org.wordpress.android.ui.newstats.NewStatsRouting import org.wordpress.android.ui.prefs.AppPrefsWrapper import org.wordpress.android.viewmodel.Event import javax.inject.Inject @@ -18,7 +19,8 @@ class TodaysStatsViewModelSlice @Inject constructor( private val selectedSiteRepository: SelectedSiteRepository, private val jetpackFeatureRemovalPhaseHelper: JetpackFeatureRemovalPhaseHelper, private val appPrefsWrapper: AppPrefsWrapper, - private val todaysStatsCardBuilder: TodaysStatsCardBuilder + private val todaysStatsCardBuilder: TodaysStatsCardBuilder, + private val newStatsRouting: NewStatsRouting ) { private val _uiModel = MutableLiveData() val uiModel = _uiModel as LiveData @@ -86,12 +88,15 @@ class TodaysStatsViewModelSlice @Inject constructor( } private fun navigateToTodaysStats() { - val selectedSite = requireNotNull(selectedSiteRepository.getSelectedSite()) - if (jetpackFeatureRemovalPhaseHelper.shouldShowStaticPage()) { - _onNavigation.value = Event(SiteNavigationAction.ShowJetpackRemovalStaticPostersView) - } else { - _onNavigation.value = Event(SiteNavigationAction.OpenStatsByDay(selectedSite)) + val navigationAction = when { + jetpackFeatureRemovalPhaseHelper.shouldShowStaticPage() -> + SiteNavigationAction.ShowJetpackRemovalStaticPostersView + + newStatsRouting.isNewStatsEnabled() -> SiteNavigationAction.OpenNewStatsForToday + + else -> SiteNavigationAction.OpenStatsByDay(requireNotNull(selectedSiteRepository.getSelectedSite())) } + _onNavigation.value = Event(navigationAction) } fun clearValue() { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/newstats/NewStatsActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/newstats/NewStatsActivity.kt index 94e9d633c2bd..02f229bccab1 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/newstats/NewStatsActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/newstats/NewStatsActivity.kt @@ -238,8 +238,21 @@ class NewStatsActivity : BaseAppCompatActivity() { companion object { private const val FEEDBACK_PREFIX_STATS = "Stats" - fun start(context: Context) { - context.startActivity(Intent(context, NewStatsActivity::class.java)) + /** + * Opens New Stats, optionally on a specific [period]. The period is passed as an Intent + * extra that seeds [ViewsStatsViewModel]'s SavedStateHandle, so it takes precedence over the + * persisted period without overwriting it. + */ + fun start(context: Context, period: StatsPeriod? = null) { + val intent = Intent(context, NewStatsActivity::class.java) + period?.let { + intent.putExtra(ViewsStatsViewModel.KEY_PERIOD_TYPE, it.toTypeString()) + if (it is StatsPeriod.Custom) { + intent.putExtra(ViewsStatsViewModel.KEY_CUSTOM_START_DATE, it.startDate.toEpochDay()) + intent.putExtra(ViewsStatsViewModel.KEY_CUSTOM_END_DATE, it.endDate.toEpochDay()) + } + } + context.startActivity(intent) } } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/newstats/viewsstats/ViewsStatsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/newstats/viewsstats/ViewsStatsViewModel.kt index ea865eedb653..c5f5a0362eb8 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/newstats/viewsstats/ViewsStatsViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/newstats/viewsstats/ViewsStatsViewModel.kt @@ -42,9 +42,6 @@ private val HOURLY_FORMAT_REGEX = Regex("""\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}"" private val DAILY_FORMAT_REGEX = Regex("""\d{4}-\d{2}-\d{2}""") private val MONTHLY_FORMAT_REGEX = Regex("""\d{4}-\d{2}""") -private const val KEY_PERIOD_TYPE = "period_type" -private const val KEY_CUSTOM_START_DATE = "custom_start_date" -private const val KEY_CUSTOM_END_DATE = "custom_end_date" private const val KEY_CHART_TYPE = "chart_type" private const val PERIOD_TODAY = "today" @@ -761,4 +758,15 @@ class ViewsStatsViewModel @Inject constructor( fun onRetry() { loadData() } + + companion object { + /** + * Keys used to restore the selected period. They double as Intent extras: Hilt seeds the + * [SavedStateHandle] from the launching Intent, so an entry point can open New Stats on a + * specific period without persisting it as the user's preference. + */ + const val KEY_PERIOD_TYPE = "period_type" + const val KEY_CUSTOM_START_DATE = "custom_start_date" + const val KEY_CUSTOM_END_DATE = "custom_end_date" + } } diff --git a/WordPress/src/test/java/org/wordpress/android/ui/mysite/MySiteViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/mysite/MySiteViewModelTest.kt index 92a9a5aa90ab..705082bbf021 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/mysite/MySiteViewModelTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/mysite/MySiteViewModelTest.kt @@ -38,6 +38,7 @@ import org.wordpress.android.ui.mysite.cards.DashboardCardsViewModelSlice import org.wordpress.android.ui.mysite.cards.applicationpassword.ApplicationPasswordViewModelSlice import org.wordpress.android.ui.mysite.cards.siteinfo.SiteInfoHeaderCardViewModelSlice import org.wordpress.android.ui.mysite.items.DashboardItemsViewModelSlice +import org.wordpress.android.ui.newstats.NewStatsRouting import org.wordpress.android.ui.mysite.items.listitem.SiteCapabilityChecker import org.wordpress.android.ui.mysite.cards.connectivity.SiteConnectivityBannerViewModelSlice import org.wordpress.android.ui.pages.SnackbarMessageHolder @@ -111,6 +112,9 @@ class MySiteViewModelTest : BaseUnitTest() { lateinit var gutenbergKitAnnouncementController: org.wordpress.android.ui.posts.GutenbergKitAnnouncementController + @Mock + lateinit var newStatsRouting: NewStatsRouting + private lateinit var viewModel: MySiteViewModel private lateinit var uiModels: MutableList private lateinit var snackbars: MutableList @@ -169,6 +173,7 @@ class MySiteViewModelTest : BaseUnitTest() { gutenbergEditorPreloader, siteConnectivityBannerViewModelSlice, gutenbergKitAnnouncementController, + newStatsRouting, ) uiModels = mutableListOf() snackbars = mutableListOf() diff --git a/WordPress/src/test/java/org/wordpress/android/ui/mysite/cards/dashboard/todaysstats/TodaysStatsViewModelSliceTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/mysite/cards/dashboard/todaysstats/TodaysStatsViewModelSliceTest.kt index f6a18d0043cf..e2a03a5365f6 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/mysite/cards/dashboard/todaysstats/TodaysStatsViewModelSliceTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/mysite/cards/dashboard/todaysstats/TodaysStatsViewModelSliceTest.kt @@ -17,6 +17,7 @@ import org.wordpress.android.ui.mysite.MySiteCardAndItem import org.wordpress.android.ui.mysite.SelectedSiteRepository import org.wordpress.android.ui.mysite.SiteNavigationAction import org.wordpress.android.ui.mysite.cards.dashboard.CardsTracker +import org.wordpress.android.ui.newstats.NewStatsRouting import org.wordpress.android.ui.prefs.AppPrefsWrapper @ExperimentalCoroutinesApi @@ -37,6 +38,9 @@ class TodaysStatsViewModelSliceTest : BaseUnitTest() { @Mock lateinit var todaysStatsCardBuilder: TodaysStatsCardBuilder + @Mock + lateinit var newStatsRouting: NewStatsRouting + private lateinit var todaysStatsViewModelSlice: TodaysStatsViewModelSlice private lateinit var navigationActions: MutableList @@ -52,7 +56,8 @@ class TodaysStatsViewModelSliceTest : BaseUnitTest() { selectedSiteRepository, jetpackFeatureRemovalPhaseHelper, appPrefsWrapper, - todaysStatsCardBuilder + todaysStatsCardBuilder, + newStatsRouting ) navigationActions = mutableListOf() todaysStatsViewModelSlice.onNavigation.observeForever { event -> @@ -81,6 +86,46 @@ class TodaysStatsViewModelSliceTest : BaseUnitTest() { ) } + @Test + fun `given new stats enabled, when card item is clicked, then new stats is opened for today`() = + test { + whenever(newStatsRouting.isNewStatsEnabled()).thenReturn(true) + + val params = todaysStatsViewModelSlice.getTodaysStatsBuilderParams(null) + + params.onTodaysStatsCardClick() + + assertThat(navigationActions).containsOnly(SiteNavigationAction.OpenNewStatsForToday) + verify(cardsTracker).trackCardItemClicked( + CardsTracker.Type.STATS.label, + CardsTracker.StatsSubtype.TODAYS_STATS.label + ) + } + + @Test + fun `given new stats enabled, when more menu item view stats is clicked, then new stats is opened for today`() = + test { + whenever(newStatsRouting.isNewStatsEnabled()).thenReturn(true) + + val params = todaysStatsViewModelSlice.getTodaysStatsBuilderParams(null) + + params.moreMenuClickParams.onViewStatsMenuItemClick.invoke() + + assertThat(navigationActions).containsOnly(SiteNavigationAction.OpenNewStatsForToday) + } + + @Test + fun `given static posters phase, when card item is clicked, then static posters view is shown`() = + test { + whenever(jetpackFeatureRemovalPhaseHelper.shouldShowStaticPage()).thenReturn(true) + + val params = todaysStatsViewModelSlice.getTodaysStatsBuilderParams(null) + + params.onTodaysStatsCardClick() + + assertThat(navigationActions).containsOnly(SiteNavigationAction.ShowJetpackRemovalStaticPostersView) + } + @Test fun `given today's stat card, when get more views url is clicked, then external link is opened`() = test {