Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.NewStatsFeatureUtils
import org.wordpress.android.ui.pages.SnackbarMessageHolder
import org.wordpress.android.ui.mediapicker.MediaPickerActivity
import org.wordpress.android.ui.posts.BasicDialogViewModel
Expand Down Expand Up @@ -70,6 +71,7 @@ class MySiteViewModel @Inject constructor(
private val gutenbergEditorPreloader: GutenbergEditorPreloader,
private val siteConnectivityBannerViewModelSlice: SiteConnectivityBannerViewModelSlice,
private val gutenbergKitAnnouncementController: GutenbergKitAnnouncementController,
private val newStatsFeatureUtils: NewStatsFeatureUtils,
) : ScopedViewModel(mainDispatcher) {
private val _onSnackbarMessage = MutableLiveData<Event<SnackbarMessageHolder>>()
private val _onNavigation = MutableLiveData<Event<SiteNavigationAction>>()
Expand Down Expand Up @@ -243,7 +245,11 @@ class MySiteViewModel @Inject constructor(
fun handleSuccessfulLoginResult() {
selectedSiteRepository.getSelectedSite()?.let { site ->
_onNavigation.value = Event(
SiteNavigationAction.OpenStats(site)
if (newStatsFeatureUtils.isNewStatsEnabled()) {
SiteNavigationAction.OpenNewStats
} else {
SiteNavigationAction.OpenStats(site)
}
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,14 @@ import org.wordpress.android.ui.blaze.blazecampaigns.campaignlisting.CampaignLis
import org.wordpress.android.ui.jetpackoverlay.JetpackFeatureRemovalPhaseHelper
import org.wordpress.android.ui.mysite.SiteNavigationAction
import org.wordpress.android.ui.mysite.items.listitem.ListItemAction
import org.wordpress.android.ui.prefs.AppPrefsWrapper
import org.wordpress.android.util.config.NewStatsFeatureConfig
import org.wordpress.android.ui.newstats.NewStatsFeatureUtils
import javax.inject.Inject

class ListItemActionHandler @Inject constructor(
private val accountStore: AccountStore,
private val jetpackFeatureRemovalPhaseHelper: JetpackFeatureRemovalPhaseHelper,
private val blazeFeatureUtils: BlazeFeatureUtils,
private val newStatsFeatureConfig: NewStatsFeatureConfig,
private val appPrefsWrapper: AppPrefsWrapper
private val newStatsFeatureUtils: NewStatsFeatureUtils
) {
fun handleAction(
action: ListItemAction,
Expand Down Expand Up @@ -61,8 +59,7 @@ class ListItemActionHandler @Inject constructor(

// If it's a WordPress.com or Jetpack site, show the Stats screen.
site.isWPCom || site.isJetpackInstalled && site.isJetpackConnected -> {
// Show New Stats when the remote flag is on (rollout) or the user has opted in locally.
if (newStatsFeatureConfig.isEnabled() || appPrefsWrapper.getNewStatsUserOptedIn()) {
if (newStatsFeatureUtils.isNewStatsEnabled()) {
SiteNavigationAction.OpenNewStats
} else {
SiteNavigationAction.OpenStats(site)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.NewStatsFeatureUtils
import org.wordpress.android.ui.prefs.AppPrefsWrapper
import org.wordpress.android.viewmodel.Event
import javax.inject.Inject
Expand All @@ -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 newStatsFeatureUtils: NewStatsFeatureUtils
) {
private val _uiModel = MutableLiveData<MySiteCardAndItem.Card.TodaysStatsCard?>()
val uiModel = _uiModel as LiveData<MySiteCardAndItem.Card.TodaysStatsCard?>
Expand Down Expand Up @@ -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

newStatsFeatureUtils.isNewStatsEnabled() -> SiteNavigationAction.OpenNewStatsForToday

else -> SiteNavigationAction.OpenStatsByDay(requireNotNull(selectedSiteRepository.getSelectedSite()))
}
_onNavigation.value = Event(navigationAction)
}

fun clearValue() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,21 @@ class NewStatsActivity : BaseAppCompatActivity() {
}

companion object {
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)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.wordpress.android.ui.newstats

import org.wordpress.android.ui.prefs.AppPrefsWrapper
import org.wordpress.android.util.config.NewStatsFeatureConfig
import javax.inject.Inject

/**
* Single source of truth for deciding whether Stats entry points should open New Stats.
*/
class NewStatsFeatureUtils @Inject constructor(
private val newStatsFeatureConfig: NewStatsFeatureConfig,
private val appPrefsWrapper: AppPrefsWrapper
) {
/**
* Returns true when the remote flag is on (rollout) or the user has opted in locally.
*/
fun isNewStatsEnabled(): Boolean =
newStatsFeatureConfig.isEnabled() || appPrefsWrapper.getNewStatsUserOptedIn()
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import org.wordpress.android.fluxc.model.stats.time.VisitsAndViewsModel.PeriodDa
import org.wordpress.android.fluxc.network.utils.StatsGranularity
import org.wordpress.android.modules.UI_THREAD
import org.wordpress.android.ui.newstats.NewStatsActivity
import org.wordpress.android.ui.prefs.AppPrefsWrapper
import org.wordpress.android.ui.newstats.NewStatsFeatureUtils
import org.wordpress.android.ui.stats.StatsTimeframe
import org.wordpress.android.ui.stats.refresh.StatsActivity
import org.wordpress.android.ui.stats.refresh.lists.widget.IS_WIDE_VIEW_KEY
Expand All @@ -32,7 +32,6 @@ import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsCo
import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsColorSelectionViewModel.Color.LIGHT
import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsWidgetConfigureFragment.WidgetType
import org.wordpress.android.ui.stats.refresh.utils.StatsLaunchedFrom
import org.wordpress.android.util.config.NewStatsFeatureConfig
import org.wordpress.android.util.image.ImageManager
import org.wordpress.android.util.image.ImageType.ICON
import org.wordpress.android.viewmodel.ResourceProvider
Expand All @@ -51,8 +50,7 @@ class WidgetUtils
@Inject constructor(
@Named(UI_THREAD) private val mainDispatcher: CoroutineDispatcher,
val imageManager: ImageManager,
private val newStatsFeatureConfig: NewStatsFeatureConfig,
private val appPrefsWrapper: AppPrefsWrapper
private val newStatsFeatureUtils: NewStatsFeatureUtils
) {
private val coroutineScope = CoroutineScope(mainDispatcher)
fun isWidgetWiderThanLimit(
Expand Down Expand Up @@ -181,7 +179,7 @@ class WidgetUtils
statsTimeframe: StatsTimeframe,
granularity: StatsGranularity? = null
): PendingIntent {
val intent = if (isNewStatsEnabled()) {
val intent = if (newStatsFeatureUtils.isNewStatsEnabled()) {
Intent(context, NewStatsActivity::class.java).apply {
putExtra(WordPress.LOCAL_SITE_ID, localSiteId)
// Mirror the row-tap (template) path: CLEAR_TOP forces an already-running
Expand Down Expand Up @@ -210,7 +208,7 @@ class WidgetUtils
private fun getPendingTemplate(context: Context): PendingIntent {
// The per-row fill-in intents already carry the LOCAL_SITE_ID extra, which is merged into
// whichever component this template targets, so New Stats receives the tapped row's site.
val targetActivity = if (isNewStatsEnabled()) {
val targetActivity = if (newStatsFeatureUtils.isNewStatsEnabled()) {
NewStatsActivity::class.java
} else {
StatsActivity::class.java
Expand All @@ -235,11 +233,6 @@ class WidgetUtils
return Random(Date().time).nextInt()
}

// Mirrors the routing used by the My Site menu (ListItemActionHandler): open New Stats when the
// remote flag is on (rollout) or the user has opted in locally.
private fun isNewStatsEnabled(): Boolean =
newStatsFeatureConfig.isEnabled() || appPrefsWrapper.getNewStatsUserOptedIn()

fun getLastWeekPeriodData(visitsAndViewsModel: VisitsAndViewsModel): PeriodData? {
val currentDateForSite = WeeklyRoundupUtils.parseStandardDate(visitsAndViewsModel.period) ?: return null
val lastWeekStartDate = currentDateForSite.minusWeeks(1).with(TemporalAdjusters.previousOrSame(MONDAY))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.NewStatsFeatureUtils
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
Expand Down Expand Up @@ -111,6 +112,9 @@ class MySiteViewModelTest : BaseUnitTest() {
lateinit var gutenbergKitAnnouncementController:
org.wordpress.android.ui.posts.GutenbergKitAnnouncementController

@Mock
lateinit var newStatsFeatureUtils: NewStatsFeatureUtils

private lateinit var viewModel: MySiteViewModel
private lateinit var uiModels: MutableList<MySiteViewModel.State>
private lateinit var snackbars: MutableList<SnackbarMessageHolder>
Expand Down Expand Up @@ -169,6 +173,7 @@ class MySiteViewModelTest : BaseUnitTest() {
gutenbergEditorPreloader,
siteConnectivityBannerViewModelSlice,
gutenbergKitAnnouncementController,
newStatsFeatureUtils,
)
uiModels = mutableListOf()
snackbars = mutableListOf()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ import org.wordpress.android.ui.blaze.blazecampaigns.campaignlisting.CampaignLis
import org.wordpress.android.ui.jetpackoverlay.JetpackFeatureRemovalPhaseHelper
import org.wordpress.android.ui.mysite.SiteNavigationAction
import org.wordpress.android.ui.mysite.items.listitem.ListItemAction
import org.wordpress.android.ui.prefs.AppPrefsWrapper
import org.wordpress.android.util.config.NewStatsFeatureConfig
import org.wordpress.android.ui.newstats.NewStatsFeatureUtils
import kotlin.test.assertEquals

@ExperimentalCoroutinesApi
Expand All @@ -33,10 +32,7 @@ class ListItemActionHandlerTest: BaseUnitTest() {
lateinit var blazeFeatureUtils: BlazeFeatureUtils

@Mock
lateinit var newStatsFeatureConfig: NewStatsFeatureConfig

@Mock
lateinit var appPrefsWrapper: AppPrefsWrapper
lateinit var newStatsFeatureUtils: NewStatsFeatureUtils

private val site = SiteModel()

Expand All @@ -48,8 +44,7 @@ class ListItemActionHandlerTest: BaseUnitTest() {
accountStore,
jetpackFeatureRemovalPhaseHelper,
blazeFeatureUtils,
newStatsFeatureConfig,
appPrefsWrapper
newStatsFeatureUtils
)
}

Expand Down Expand Up @@ -167,43 +162,31 @@ class ListItemActionHandlerTest: BaseUnitTest() {
}

@Test
fun `stats item click emits OpenNewStats when NEW_STATS feature is enabled and site is WPCom`() {
fun `stats item click emits OpenNewStats when new stats is enabled and site is WPCom`() {
site.setIsWPCom(true)
whenever(newStatsFeatureConfig.isEnabled()).thenReturn(true)
whenever(newStatsFeatureUtils.isNewStatsEnabled()).thenReturn(true)

val navigationAction = invokeItemClickAction(action = ListItemAction.STATS)

assertEquals(SiteNavigationAction.OpenNewStats, navigationAction)
}

@Test
fun `stats item click emits OpenNewStats when NEW_STATS feature is enabled and site is Jetpack`() {
fun `stats item click emits OpenNewStats when new stats is enabled and site is Jetpack`() {
site.setIsJetpackConnected(true)
site.setIsWPCom(true)
whenever(accountStore.hasAccessToken()).thenReturn(true)
whenever(newStatsFeatureConfig.isEnabled()).thenReturn(true)

val navigationAction = invokeItemClickAction(action = ListItemAction.STATS)

assertEquals(SiteNavigationAction.OpenNewStats, navigationAction)
}

@Test
fun `stats item click emits OpenNewStats when user opted in even if NEW_STATS feature is disabled`() {
site.setIsWPCom(true)
whenever(newStatsFeatureConfig.isEnabled()).thenReturn(false)
whenever(appPrefsWrapper.getNewStatsUserOptedIn()).thenReturn(true)
whenever(newStatsFeatureUtils.isNewStatsEnabled()).thenReturn(true)

val navigationAction = invokeItemClickAction(action = ListItemAction.STATS)

assertEquals(SiteNavigationAction.OpenNewStats, navigationAction)
}

@Test
fun `stats item click emits OpenStats when NEW_STATS feature is disabled and user has not opted in`() {
fun `stats item click emits OpenStats when new stats is disabled`() {
site.setIsWPCom(true)
whenever(newStatsFeatureConfig.isEnabled()).thenReturn(false)
whenever(appPrefsWrapper.getNewStatsUserOptedIn()).thenReturn(false)
whenever(newStatsFeatureUtils.isNewStatsEnabled()).thenReturn(false)

val navigationAction = invokeItemClickAction(action = ListItemAction.STATS)

Expand Down
Loading
Loading