diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 0e1b80833633..e2e517ec71ba 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -2,7 +2,8 @@
24.2
-----
-
+* [**] Fix editor crash occurring on large posts [https://github.com/wordpress-mobile/WordPress-Android/pull/20046]
+* [*] [Jetpack-only] Site Monitoring: Add Metrics, PHP Logs, and Web Server Logs under Site Monitoring [https://github.com/wordpress-mobile/WordPress-Android/issues/20067]
24.1
-----
diff --git a/WordPress/build.gradle b/WordPress/build.gradle
index ac98ada035c5..c911d5d2c816 100644
--- a/WordPress/build.gradle
+++ b/WordPress/build.gradle
@@ -387,10 +387,6 @@ dependencies {
}
implementation "org.wordpress:persistentedittext:$wordPressPersistentEditTextVersion"
- // To enable Stetho, a debug bridge that enables the Chrome Developer Tools for debug purposes.
- debugImplementation "com.facebook.stetho:stetho:$stethoVersion"
- debugImplementation "com.facebook.stetho:stetho-okhttp3:$stethoVersion"
-
implementation "androidx.arch.core:core-common:$androidxArchCoreVersion"
implementation "androidx.arch.core:core-runtime:$androidxArchCoreVersion"
implementation "com.google.code.gson:gson:$googleGsonVersion"
@@ -558,6 +554,16 @@ dependencies {
// - Jetpack Compose - UI Tests
androidTestImplementation "androidx.compose.ui:ui-test-junit4"
implementation "androidx.compose.material3:material3:$androidxComposeMaterial3Version"
+
+ // - Flipper
+ debugImplementation ("com.facebook.flipper:flipper:$flipperVersion") {
+ exclude group:'org.jetbrains.kotlinx', module:'kotlinx-serialization-json-jvm'
+ }
+ debugImplementation "com.facebook.soloader:soloader:$soLoaderVersion"
+ debugImplementation ("com.facebook.flipper:flipper-network-plugin:$flipperVersion"){
+ exclude group:'org.jetbrains.kotlinx', module:'kotlinx-serialization-json-jvm'
+ }
+ releaseImplementation "com.facebook.flipper:flipper-noop:$flipperVersion"
}
configurations.all {
diff --git a/WordPress/src/debug/AndroidManifest.xml b/WordPress/src/debug/AndroidManifest.xml
index e961d134552f..f8c9e84b0392 100644
--- a/WordPress/src/debug/AndroidManifest.xml
+++ b/WordPress/src/debug/AndroidManifest.xml
@@ -38,5 +38,7 @@
android:name=".ui.debug.preferences.DebugSharedPreferenceFlagsActivity"
android:label="@string/debug_settings_debug_flags_screen"
android:theme="@style/WordPress.NoActionBar" />
+
diff --git a/WordPress/src/debug/java/org/wordpress/android/WordPressDebug.java b/WordPress/src/debug/java/org/wordpress/android/WordPressDebug.java
index 18c86dd485b9..8672794c9767 100644
--- a/WordPress/src/debug/java/org/wordpress/android/WordPressDebug.java
+++ b/WordPress/src/debug/java/org/wordpress/android/WordPressDebug.java
@@ -2,7 +2,14 @@
import android.os.StrictMode;
-import com.facebook.stetho.Stetho;
+import com.facebook.flipper.android.AndroidFlipperClient;
+import com.facebook.flipper.android.utils.FlipperUtils;
+import com.facebook.flipper.core.FlipperClient;
+import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
+import com.facebook.flipper.plugins.inspector.DescriptorMapping;
+import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
+import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
+import com.facebook.soloader.SoLoader;
import org.wordpress.android.util.AppLog;
import org.wordpress.android.util.AppLog.T;
@@ -11,14 +18,23 @@
@HiltAndroidApp
public class WordPressDebug extends WordPressApp {
+ public static final NetworkFlipperPlugin NETWORK_FLIPPER_PLUGIN = new NetworkFlipperPlugin();
@Override
public void onCreate() {
super.onCreate();
// enableStrictMode()
- // Init Stetho
- Stetho.initializeWithDefaults(this);
+ // init Flipper
+ SoLoader.init(this, false);
+
+ if (BuildConfig.DEBUG && FlipperUtils.shouldEnableFlipper(this)) {
+ final FlipperClient client = AndroidFlipperClient.getInstance(this);
+ client.addPlugin(new InspectorFlipperPlugin(this, DescriptorMapping.withDefaults()));
+ client.addPlugin(NETWORK_FLIPPER_PLUGIN);
+ client.addPlugin(new DatabasesFlipperPlugin(this));
+ client.start();
+ }
}
/**
diff --git a/WordPress/src/debug/java/org/wordpress/android/modules/InterceptorModule.java b/WordPress/src/debug/java/org/wordpress/android/modules/InterceptorModule.java
index cba40b8795c0..a3fff589ff72 100644
--- a/WordPress/src/debug/java/org/wordpress/android/modules/InterceptorModule.java
+++ b/WordPress/src/debug/java/org/wordpress/android/modules/InterceptorModule.java
@@ -1,6 +1,8 @@
package org.wordpress.android.modules;
-import com.facebook.stetho.okhttp3.StethoInterceptor;
+import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
+
+import org.wordpress.android.WordPressDebug;
import javax.inject.Named;
@@ -11,11 +13,12 @@
import dagger.multibindings.IntoSet;
import okhttp3.Interceptor;
+
@InstallIn(SingletonComponent.class)
@Module
public class InterceptorModule {
@Provides @IntoSet @Named("network-interceptors")
- public Interceptor provideStethoInterceptor() {
- return new StethoInterceptor();
+ public Interceptor provideFlipperInterceptor() {
+ return new FlipperOkhttpInterceptor(WordPressDebug.NETWORK_FLIPPER_PLUGIN);
}
}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/personalization/PersonalizationActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/personalization/PersonalizationActivity.kt
index 94e60930694d..ac9ab5de738d 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/personalization/PersonalizationActivity.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/personalization/PersonalizationActivity.kt
@@ -107,7 +107,7 @@ class PersonalizationActivity : AppCompatActivity() {
contentColor = MaterialTheme.colors.onSurface,
) {
tabs.forEachIndexed { index, title ->
- Tab(text = { Text(stringResource(id = title)) },
+ Tab(text = { Text(stringResource(id = title).uppercase()) },
selected = tabIndex == index,
onClick = { tabIndex = index }
)
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorFragmentContainer.kt b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorFragmentContainer.kt
deleted file mode 100644
index eb5eea65c8a1..000000000000
--- a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorFragmentContainer.kt
+++ /dev/null
@@ -1,76 +0,0 @@
-package org.wordpress.android.ui.sitemonitor
-
-import android.content.Context
-import android.view.View
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalView
-import androidx.compose.ui.viewinterop.AndroidView
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.FragmentActivity
-import androidx.fragment.app.FragmentContainerView
-import androidx.fragment.app.FragmentTransaction
-import androidx.fragment.app.commit
-import androidx.fragment.app.findFragment
-
-@Suppress("SwallowedException")
-@Composable
-fun SiteMonitorFragmentContainer(
- modifier: Modifier = Modifier,
- commit: FragmentTransaction.(containerId: Int) -> Unit
-) {
- val currentLocalView = LocalView.current
- // Using the current view, check if a parent fragment exists.
- // This will help ensure that the fragment are nested correctly.
- // This assists in saving/restoring the fragments to their proper state
- val parentFragment = remember(currentLocalView) {
- try {
- currentLocalView.findFragment()
- } catch (e: IllegalStateException) {
- null
- }
- }
- val viewId by rememberSaveable { mutableIntStateOf(View.generateViewId()) }
- val container = remember { mutableStateOf(null) }
- val viewSection: (Context) -> View = remember(currentLocalView) {
- { context ->
- FragmentContainerView(context)
- .apply { id = viewId }
- .also {
- val fragmentManager = parentFragment?.childFragmentManager
- ?: (context as? FragmentActivity)?.supportFragmentManager
- fragmentManager?.commit { commit(it.id) }
- container.value = it
- }
- }
- }
- AndroidView(
- modifier = modifier,
- factory = viewSection,
- update = {}
- )
-
- // Be sure to clean up the fragments when the FragmentContainer is disposed
- val localContext = LocalContext.current
- DisposableEffect(currentLocalView, localContext, container) {
- onDispose {
- val fragmentManager = parentFragment?.childFragmentManager
- ?: (localContext as? FragmentActivity)?.supportFragmentManager
- // Use the FragmentContainerView to find the inflated fragment
- val existingFragment = fragmentManager?.findFragmentById(container.value?.id ?: 0)
- if (existingFragment != null && !fragmentManager.isStateSaved) {
- // A composable has been removed from the hierarchy if the state isn't saved
- fragmentManager.commit {
- remove(existingFragment)
- }
- }
- }
- }
-}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorParentActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorParentActivity.kt
index 9a9c52b4920b..c1773d48c83e 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorParentActivity.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorParentActivity.kt
@@ -1,62 +1,121 @@
package org.wordpress.android.ui.sitemonitor
import android.annotation.SuppressLint
+import android.os.Build
import android.os.Bundle
import android.util.SparseArray
+import android.view.View
+import android.view.ViewGroup
+import android.webkit.WebView
import androidx.activity.compose.setContent
+import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.material.Button
+import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
+import androidx.compose.material.Text
+import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Surface
+import androidx.compose.material3.Tab
+import androidx.compose.material3.TabRow
+import androidx.compose.material3.TabRowDefaults
+import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.viewinterop.AndroidView
import androidx.fragment.app.Fragment
-import androidx.fragment.app.FragmentManager
-import androidx.fragment.app.FragmentTransaction
import dagger.hilt.android.AndroidEntryPoint
import org.wordpress.android.R
import org.wordpress.android.WordPress
import org.wordpress.android.fluxc.model.SiteModel
+import org.wordpress.android.ui.WPWebViewActivity
import org.wordpress.android.ui.compose.components.MainTopAppBar
import org.wordpress.android.ui.compose.components.NavigationIcons
import org.wordpress.android.ui.compose.theme.AppTheme
+import org.wordpress.android.ui.compose.utils.uiStringText
import org.wordpress.android.util.extensions.getSerializableExtraCompat
import javax.inject.Inject
+@SuppressLint("SetJavaScriptEnabled")
@AndroidEntryPoint
-class SiteMonitorParentActivity: AppCompatActivity() {
+class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient.SiteMonitorWebViewClientListener {
@Inject
lateinit var siteMonitorUtils: SiteMonitorUtils
private var savedStateSparseArray = SparseArray()
private var currentSelectItemId = 0
+ private val siteMonitorParentViewModel: SiteMonitorParentViewModel by viewModels()
+
+ private val metricsWebView by lazy {
+ commonWebView(SiteMonitorType.METRICS)
+ }
+
+ private val phpLogsWebView by lazy {
+ commonWebView(SiteMonitorType.PHP_LOGS)
+ }
+
+ private val webServerLogsWebView by lazy {
+ commonWebView(SiteMonitorType.WEB_SERVER_LOGS)
+ }
+
+ private fun commonWebView(
+ siteMonitorType: SiteMonitorType
+ ) = WebView(this@SiteMonitorParentActivity).apply {
+ layoutParams = ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
+ scrollBarStyle = View.SCROLLBARS_INSIDE_OVERLAY
+ settings.userAgentString = siteMonitorUtils.getUserAgent()
+ settings.javaScriptEnabled = true
+ settings.domStorageEnabled = true
+ webViewClient = SiteMonitorWebViewClient(this@SiteMonitorParentActivity, siteMonitorType)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ // not sure about this one, double check if this works as expected
+ settings.isAlgorithmicDarkeningAllowed = true
+ }
+ }
+
@Suppress("DEPRECATION")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- siteMonitorUtils.trackActivityLaunched()
-
if (savedInstanceState != null) {
savedStateSparseArray = savedInstanceState.getSparseParcelableArray(
SAVED_STATE_CONTAINER_KEY
)
?: savedStateSparseArray
currentSelectItemId = savedInstanceState.getInt(SAVED_STATE_CURRENT_TAB_KEY)
+ siteMonitorParentViewModel.loadData()
+ } else {
+ siteMonitorParentViewModel.start(getSite())
+ currentSelectItemId = getInitialTab()
}
setContent {
AppTheme {
Surface(
modifier = Modifier.fillMaxSize(),
) {
- SiteMonitorScreen()
+ SiteMonitorScreen(initialTab = currentSelectItemId)
}
}
}
@@ -72,9 +131,14 @@ class SiteMonitorParentActivity: AppCompatActivity() {
return requireNotNull(intent.getSerializableExtraCompat(WordPress.SITE)) as SiteModel
}
- private fun getInitialTab(): SiteMonitorType {
- return intent?.getSerializableExtraCompat(ARG_SITE_MONITOR_TYPE_KEY) as SiteMonitorType?
+ private fun getInitialTab(): Int {
+ val tab = intent?.getSerializableExtraCompat(ARG_SITE_MONITOR_TYPE_KEY) as SiteMonitorType?
?: SiteMonitorType.METRICS
+ return when (tab) {
+ SiteMonitorType.METRICS -> 0
+ SiteMonitorType.PHP_LOGS -> 1
+ SiteMonitorType.WEB_SERVER_LOGS -> 2
+ }
}
companion object {
@@ -85,8 +149,7 @@ class SiteMonitorParentActivity: AppCompatActivity() {
@Composable
@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
- fun SiteMonitorScreen() {
- var selectedTab by rememberSaveable { mutableStateOf(SiteMonitorTabItem.Metrics.route) }
+ fun SiteMonitorScreen(initialTab: Int, modifier: Modifier = Modifier) {
Scaffold(
topBar = {
MainTopAppBar(
@@ -94,59 +157,171 @@ class SiteMonitorParentActivity: AppCompatActivity() {
navigationIcon = NavigationIcons.BackIcon,
onNavigationIconClick = onBackPressedDispatcher::onBackPressed,
)
+ },
+ content = {
+ SiteMonitorHeader(initialTab, modifier = modifier)
}
- ) { padding ->
- Column(modifier = Modifier.padding(padding)) {
- SiteMonitorTabHeader { clickTab ->
- selectedTab = clickTab
+ )
+ }
+
+ @Composable
+ @SuppressLint("UnusedMaterialScaffoldPaddingParameter")
+ fun SiteMonitorHeader(initialTab: Int, modifier: Modifier = Modifier) {
+ var tabIndex by remember { mutableStateOf(initialTab) }
+
+ val tabs = SiteMonitorTabItem.entries
+
+ LaunchedEffect(true) {
+ siteMonitorUtils.trackTabLoaded(tabs[initialTab].siteMonitorType)
+ }
+
+ Column(modifier = modifier.fillMaxWidth()) {
+ TabRow(
+ selectedTabIndex = tabIndex,
+ containerColor = MaterialTheme.colors.surface,
+ contentColor = MaterialTheme.colors.onSurface,
+ indicator = { tabPositions ->
+ // Customizing the indicator color and style
+ TabRowDefaults.Indicator(
+ Modifier.tabIndicatorOffset(tabPositions[tabIndex]),
+ color = MaterialTheme.colors.onSurface,
+ height = 2.0.dp
+ )
}
- SiteMonitorTabNavigation(selectedTab) { selectedTab ->
- val item = enumValues().find {
- it.route == selectedTab
- } ?: initialItem(getInitialTab())
-
- siteMonitorUtils.trackTabLoaded(item.siteMonitorType)
-
- SiteMonitorFragmentContainer(
- modifier = Modifier.fillMaxSize(),
- commit = getCommitFunction(
- SiteMonitorTabFragment.newInstance(item.urlTemplate, item.siteMonitorType, getSite()),
- item.route
- )
+ ) {
+ tabs.forEachIndexed { index, item ->
+ Tab(
+ text = {
+ Column(horizontalAlignment = Alignment.CenterHorizontally) {
+ Text(
+ text = stringResource(item.title).uppercase(),
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ }
+ },
+ selected = tabIndex == index,
+ onClick = {
+ siteMonitorUtils.trackTabLoaded(tabs[index].siteMonitorType)
+ tabIndex = index
+ },
)
}
}
+ when (tabIndex) {
+ 0 -> SiteMonitorTabContent(SiteMonitorType.METRICS)
+ 1 -> SiteMonitorTabContent(SiteMonitorType.PHP_LOGS)
+ 2 -> SiteMonitorTabContent(SiteMonitorType.WEB_SERVER_LOGS)
+ }
}
}
- private fun initialItem(type: SiteMonitorType): SiteMonitorTabItem {
- return enumValues().find {
- it.siteMonitorType == type
- } ?: SiteMonitorTabItem.Metrics
+ @Composable
+ private fun SiteMonitorTabContent(tabType: SiteMonitorType, modifier: Modifier = Modifier) {
+ val uiState by remember(key1 = tabType) {
+ siteMonitorParentViewModel.getUiState(tabType)
+ }
+ LazyColumn {
+ item {
+ when (uiState) {
+ is SiteMonitorUiState.Preparing -> LoadingState(modifier)
+ is SiteMonitorUiState.Prepared, is SiteMonitorUiState.Loaded ->
+ SiteMonitorWebView(uiState, tabType, modifier)
+ is SiteMonitorUiState.Error -> SiteMonitorError(uiState as SiteMonitorUiState.Error, modifier)
+ }
+ }
+ }
+ }
+ @Composable
+ fun LoadingState(modifier: Modifier = Modifier) {
+ Box(
+ contentAlignment = Alignment.Center,
+ modifier = modifier.fillMaxSize()
+ ) {
+ CircularProgressIndicator()
+ }
}
- private fun getCommitFunction(
- fragment : Fragment,
- tag: String
- ): FragmentTransaction.(containerId: Int) -> Unit =
- {
- saveAndRetrieveFragment(supportFragmentManager, it, fragment)
- replace(it, fragment, tag)
+ @Composable
+ fun SiteMonitorError(error: SiteMonitorUiState.Error, modifier: Modifier = Modifier) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center,
+ modifier = modifier
+ .padding(20.dp)
+ .fillMaxWidth()
+ .fillMaxHeight(),
+ ) {
+ androidx.compose.material.Text(
+ text = uiStringText(uiString = error.title),
+ style = androidx.compose.material.MaterialTheme.typography.h5,
+ textAlign = TextAlign.Center
+ )
+ androidx.compose.material.Text(
+ text = uiStringText(uiString = error.description),
+ style = androidx.compose.material.MaterialTheme.typography.body1,
+ textAlign = TextAlign.Center,
+ modifier = Modifier.padding(top = 8.dp)
+ )
+ if (error.button != null) {
+ Button(
+ modifier = Modifier.padding(top = 8.dp),
+ onClick = error.button.click
+ ) {
+ Text(text = uiStringText(uiString = error.button.text))
+ }
+ }
}
+ }
- private fun saveAndRetrieveFragment(
- supportFragmentManager: FragmentManager,
- tabId: Int,
- fragment: Fragment
+ @SuppressLint("SetJavaScriptEnabled")
+ @Composable
+ private fun SiteMonitorWebView(
+ uiState: SiteMonitorUiState,
+ tabType: SiteMonitorType,
+ modifier: Modifier = Modifier
) {
- val currentFragment = supportFragmentManager.findFragmentById(currentSelectItemId)
- if (currentFragment != null) {
- savedStateSparseArray.put(
- currentSelectItemId,
- supportFragmentManager.saveFragmentInstanceState(currentFragment)
- )
+ // retrieve the webview from the actvity
+ var webView = when (tabType) {
+ SiteMonitorType.METRICS -> metricsWebView
+ SiteMonitorType.PHP_LOGS -> phpLogsWebView
+ SiteMonitorType.WEB_SERVER_LOGS -> webServerLogsWebView
}
- currentSelectItemId = tabId
- fragment.setInitialSavedState(savedStateSparseArray[currentSelectItemId])
+
+ if (uiState is SiteMonitorUiState.Prepared) {
+ webView.postUrl(WPWebViewActivity.WPCOM_LOGIN_URL, uiState.model.addressToLoad.toByteArray())
+ }
+
+ Box(
+ modifier = modifier.fillMaxSize(),
+ contentAlignment = Alignment.Center
+ ) {
+ if (uiState is SiteMonitorUiState.Prepared) {
+ LoadingState()
+ } else {
+ webView.let { theWebView ->
+ AndroidView(
+ factory = { theWebView },
+ update = { webView = it },
+ modifier = Modifier.fillMaxWidth()
+ )
+ }
+ }
+ }
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ metricsWebView.destroy()
+ phpLogsWebView.destroy()
+ webServerLogsWebView.destroy()
+ }
+
+ override fun onWebViewPageLoaded(url: String, tabType: SiteMonitorType) =
+ siteMonitorParentViewModel.onUrlLoaded(tabType)
+
+ override fun onWebViewReceivedError(url: String, tabType: SiteMonitorType) {
+ siteMonitorParentViewModel.onWebViewError(tabType)
+ siteMonitorUtils.trackTabLoadingError(tabType)
}
}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorParentViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorParentViewModel.kt
new file mode 100644
index 000000000000..2a18f07e1fb2
--- /dev/null
+++ b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorParentViewModel.kt
@@ -0,0 +1,95 @@
+package org.wordpress.android.ui.sitemonitor
+
+import androidx.compose.runtime.State
+import androidx.lifecycle.viewModelScope
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.CoroutineDispatcher
+import org.wordpress.android.fluxc.model.SiteModel
+import org.wordpress.android.modules.BG_THREAD
+import org.wordpress.android.viewmodel.ScopedViewModel
+import javax.inject.Inject
+import javax.inject.Named
+
+@HiltViewModel
+class SiteMonitorParentViewModel @Inject constructor(
+ @param:Named(BG_THREAD) private val bgDispatcher: CoroutineDispatcher,
+ private val siteMonitorUtils: SiteMonitorUtils,
+ private val metricsViewModel: SiteMonitorTabViewModelSlice,
+ private val phpLogViewModel: SiteMonitorTabViewModelSlice,
+ private val webServerViewModel: SiteMonitorTabViewModelSlice
+) : ScopedViewModel(bgDispatcher) {
+ private lateinit var site: SiteModel
+
+ init {
+ metricsViewModel.initialize(viewModelScope)
+ phpLogViewModel.initialize(viewModelScope)
+ webServerViewModel.initialize(viewModelScope)
+ }
+
+ fun start(site: SiteModel) {
+ this.site = site
+ siteMonitorUtils.trackActivityLaunched()
+ loadData()
+ }
+
+ fun loadData() {
+ metricsViewModel.start(SiteMonitorType.METRICS, SiteMonitorTabItem.Metrics.urlTemplate, site)
+ phpLogViewModel.start(SiteMonitorType.PHP_LOGS, SiteMonitorTabItem.PHPLogs.urlTemplate, site)
+ webServerViewModel.start(SiteMonitorType.WEB_SERVER_LOGS, SiteMonitorTabItem.WebServerLogs.urlTemplate, site)
+ }
+
+ fun getUiState(siteMonitorType: SiteMonitorType): State {
+ return when (siteMonitorType) {
+ SiteMonitorType.METRICS -> {
+ metricsViewModel.uiState
+ }
+
+ SiteMonitorType.PHP_LOGS -> {
+ phpLogViewModel.uiState
+ }
+
+ SiteMonitorType.WEB_SERVER_LOGS -> {
+ webServerViewModel.uiState
+ }
+ }
+ }
+
+ fun onUrlLoaded(siteMonitorType: SiteMonitorType) {
+ when (siteMonitorType) {
+ SiteMonitorType.METRICS -> {
+ metricsViewModel.onUrlLoaded()
+ }
+
+ SiteMonitorType.PHP_LOGS -> {
+ phpLogViewModel.onUrlLoaded()
+ }
+
+ SiteMonitorType.WEB_SERVER_LOGS -> {
+ webServerViewModel.onUrlLoaded()
+ }
+ }
+ }
+
+ fun onWebViewError(siteMonitorType: SiteMonitorType) {
+ when (siteMonitorType) {
+ SiteMonitorType.METRICS -> {
+ metricsViewModel.onWebViewError()
+ }
+
+ SiteMonitorType.PHP_LOGS -> {
+ phpLogViewModel.onWebViewError()
+ }
+
+ SiteMonitorType.WEB_SERVER_LOGS -> {
+ webServerViewModel.onWebViewError()
+ }
+ }
+ }
+
+ override fun onCleared() {
+ super.onCleared()
+ metricsViewModel.onCleared()
+ phpLogViewModel.onCleared()
+ webServerViewModel.onCleared()
+ }
+}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabFragment.kt
deleted file mode 100644
index 8fb61cc8ed03..000000000000
--- a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabFragment.kt
+++ /dev/null
@@ -1,184 +0,0 @@
-package org.wordpress.android.ui.sitemonitor
-
-import android.annotation.SuppressLint
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.webkit.WebView
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.material.Button
-import androidx.compose.material3.CircularProgressIndicator
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.ComposeView
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.viewinterop.AndroidView
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.viewModels
-import dagger.hilt.android.AndroidEntryPoint
-import org.wordpress.android.WordPress
-import org.wordpress.android.fluxc.model.SiteModel
-import org.wordpress.android.ui.WPWebViewActivity
-import org.wordpress.android.ui.compose.utils.uiStringText
-
-@AndroidEntryPoint
-class SiteMonitorTabFragment : Fragment(), SiteMonitorWebViewClient.SiteMonitorWebViewClientListener {
- override fun onCreateView(
- inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View = ComposeView(requireContext()).apply {
- setContent {
- SiteMonitorTabContent()
- }
- }
-
- private val viewModel: SiteMonitorTabViewModel by viewModels()
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- initViewModel(getSiteMonitorType(), getUrlTemplate(), getSite())
- }
-
- @Suppress("DEPRECATION")
- private fun getSite(): SiteModel {
- return requireNotNull(arguments?.getSerializable(WordPress.SITE)) as SiteModel
- }
-
- private fun getUrlTemplate(): String {
- return requireNotNull(arguments?.getString(KEY_URL_TEMPLATE))
- }
-
- @Suppress("DEPRECATION")
- private fun getSiteMonitorType(): SiteMonitorType {
- return requireNotNull(arguments?.getSerializable(KEY_SITE_MONITOR_TYPE)) as SiteMonitorType
- }
-
- private fun initViewModel(type: SiteMonitorType, urlTemplate: String, site: SiteModel) {
- viewModel.start(type, urlTemplate, site)
- }
-
- override fun onWebViewPageLoaded(url: String) = viewModel.onUrlLoaded()
-
- override fun onWebViewReceivedError(url: String) = viewModel.onWebViewError()
-
- companion object {
- const val KEY_URL_TEMPLATE = "KEY_URL"
- const val KEY_SITE_MONITOR_TYPE = "KEY_SITE_MONITOR_TYPE"
- fun newInstance(url: String, type: SiteMonitorType, site: SiteModel): Fragment {
- val fragment = SiteMonitorTabFragment()
- val argument = Bundle()
- argument.putString(KEY_URL_TEMPLATE, url)
- argument.putSerializable(KEY_SITE_MONITOR_TYPE, type)
- argument.putSerializable(WordPress.SITE, site)
- fragment.arguments = argument
- return fragment
- }
- }
-
- @Composable
- private fun SiteMonitorTabContent() {
- val uiState by viewModel.uiState.collectAsState()
- when (uiState) {
- is SiteMonitorUiState.Preparing -> LoadingState()
- is SiteMonitorUiState.Prepared, is SiteMonitorUiState.Loaded -> SiteMonitorWebView(uiState)
- is SiteMonitorUiState.Error -> SiteMonitorError(uiState as SiteMonitorUiState.Error)
- }
- }
-
- @Composable
- fun SiteMonitorError(error: SiteMonitorUiState.Error) {
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Center,
- modifier = Modifier
- .padding(20.dp)
- .fillMaxWidth()
- .fillMaxHeight(),
- ) {
- androidx.compose.material.Text(
- text = uiStringText(uiString = error.title),
- style = androidx.compose.material.MaterialTheme.typography.h5,
- textAlign = TextAlign.Center
- )
- androidx.compose.material.Text(
- text = uiStringText(uiString = error.description),
- style = androidx.compose.material.MaterialTheme.typography.body1,
- textAlign = TextAlign.Center,
- modifier = Modifier.padding(top = 8.dp)
- )
- if (error.button != null) {
- Button(
- modifier = Modifier.padding(top = 8.dp),
- onClick = error.button.click
- ) {
- androidx.compose.material.Text(text = uiStringText(uiString = error.button.text))
- }
- }
- }
- }
-
- @SuppressLint("SetJavaScriptEnabled")
- @Composable
- private fun SiteMonitorWebView(uiState: SiteMonitorUiState) {
- var webView: WebView? by remember { mutableStateOf(null) }
-
- if (uiState is SiteMonitorUiState.Prepared) {
- val model = uiState.model
- LaunchedEffect(true) {
- webView = WebView(requireContext()).apply {
- layoutParams = ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT
- )
- scrollBarStyle = View.SCROLLBARS_INSIDE_OVERLAY
- settings.userAgentString = model.userAgent
- settings.javaScriptEnabled = true
- settings.domStorageEnabled = true
- webViewClient = SiteMonitorWebViewClient(this@SiteMonitorTabFragment)
- postUrl(WPWebViewActivity.WPCOM_LOGIN_URL, model.addressToLoad.toByteArray())
- }
- }
- }
-
- Box(
- modifier = Modifier.fillMaxSize(),
- contentAlignment = Alignment.Center
- ) {
- if (uiState is SiteMonitorUiState.Prepared) {
- LoadingState()
- } else {
- webView?.let { theWebView ->
- AndroidView(
- factory = { theWebView },
- modifier = Modifier.fillMaxSize()
- )
- }
- }
- }
- }
-}
-
-@Composable
-fun LoadingState(modifier: Modifier = Modifier) {
- Box(
- contentAlignment = Alignment.Center,
- modifier = modifier.fillMaxSize()
- ) {
- CircularProgressIndicator()
- }
-}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabHeader.kt b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabHeader.kt
deleted file mode 100644
index 7a15059a31fc..000000000000
--- a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabHeader.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-package org.wordpress.android.ui.sitemonitor
-
-import androidx.compose.foundation.layout.Column
-import androidx.compose.material.MaterialTheme
-import androidx.compose.material3.Tab
-import androidx.compose.material3.TabRow
-import androidx.compose.material3.TabRowDefaults
-import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.unit.dp
-
-@Composable
-fun SiteMonitorTabHeader(navController: (String) -> Unit) {
- var selectedTabIndex by rememberSaveable { mutableIntStateOf(0) }
- val tabs = listOf(
- SiteMonitorTabItem.Metrics,
- SiteMonitorTabItem.PHPLogs,
- SiteMonitorTabItem.WebServerLogs
- )
- TabRow(
- selectedTabIndex = selectedTabIndex,
- containerColor = MaterialTheme.colors.surface,
- contentColor = MaterialTheme.colors.onSurface,
- indicator = { tabPositions ->
- // Customizing the indicator color and style
- TabRowDefaults.Indicator(
- Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex]),
- color = MaterialTheme.colors.onSurface,
- height = 2.0.dp
- )
- }
- ) {
- tabs.forEachIndexed { index, item ->
- Tab(
- text = {
- Column (horizontalAlignment = Alignment.CenterHorizontally) {
- Text(
- text = stringResource(item.title),
- maxLines = 1,
- overflow = TextOverflow.Ellipsis
- )
- }
- },
- selected = selectedTabIndex == index,
- onClick = {
- selectedTabIndex = index
- navController(item.route)
- },
- )
- }
- }
-}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabNavigation.kt b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabNavigation.kt
deleted file mode 100644
index 023e651a66d5..000000000000
--- a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabNavigation.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.wordpress.android.ui.sitemonitor
-
-import androidx.compose.foundation.layout.Box
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.saveable.rememberSaveableStateHolder
-import androidx.compose.ui.Modifier
-
-@Composable
-fun SiteMonitorTabNavigation (
- currentScreen: T,
- modifier: Modifier = Modifier,
- content: @Composable (T) -> Unit
-) {
- val saveableStateHolder = rememberSaveableStateHolder()
- Box(modifier) {
- saveableStateHolder.SaveableStateProvider(currentScreen) {
- content(currentScreen)
- }
- }
-}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModelSlice.kt
similarity index 77%
rename from WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModel.kt
rename to WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModelSlice.kt
index 275c65087676..45ec9a56d76f 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModel.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModelSlice.kt
@@ -1,34 +1,36 @@
package org.wordpress.android.ui.sitemonitor
import android.text.TextUtils
-import dagger.hilt.android.lifecycle.HiltViewModel
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
+import androidx.compose.runtime.State
+import androidx.compose.runtime.mutableStateOf
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.launch
import org.wordpress.android.fluxc.model.SiteModel
import org.wordpress.android.fluxc.store.AccountStore
import org.wordpress.android.fluxc.store.SiteStore
-import org.wordpress.android.modules.BG_THREAD
import org.wordpress.android.util.NetworkUtilsWrapper
-import org.wordpress.android.viewmodel.ScopedViewModel
import javax.inject.Inject
-import javax.inject.Named
-@HiltViewModel
-class SiteMonitorTabViewModel @Inject constructor(
- @param:Named(BG_THREAD) private val bgDispatcher: CoroutineDispatcher,
+class SiteMonitorTabViewModelSlice @Inject constructor(
private val networkUtilsWrapper: NetworkUtilsWrapper,
private val accountStore: AccountStore,
private val mapper: SiteMonitorMapper,
private val siteMonitorUtils: SiteMonitorUtils,
private val siteStore: SiteStore,
-) : ScopedViewModel(bgDispatcher) {
+){
+ private lateinit var scope: CoroutineScope
+
private lateinit var site: SiteModel
private lateinit var siteMonitorType: SiteMonitorType
private lateinit var urlTemplate: String
- private val _uiState = MutableStateFlow(SiteMonitorUiState.Preparing)
- val uiState: StateFlow = _uiState
+ private val _uiState = mutableStateOf(SiteMonitorUiState.Preparing)
+ val uiState: State = _uiState
+
+ fun initialize(scope: CoroutineScope) {
+ this.scope = scope
+ }
fun start(type: SiteMonitorType, urlTemplate: String, site: SiteModel) {
this.siteMonitorType = type
@@ -50,13 +52,13 @@ class SiteMonitorTabViewModel @Inject constructor(
private fun checkForInternetConnectivityAndPostErrorIfNeeded() : Boolean {
if (networkUtilsWrapper.isNetworkAvailable()) return true
- postUiState(mapper.toNoNetworkError(this@SiteMonitorTabViewModel::loadView))
+ postUiState(mapper.toNoNetworkError(::loadView))
return false
}
private fun validateAndPostErrorIfNeeded(): Boolean {
if (accountStore.account.userName.isNullOrEmpty() || accountStore.accessToken.isNullOrEmpty()) {
- postUiState(mapper.toGenericError(this@SiteMonitorTabViewModel::loadView))
+ postUiState(mapper.toGenericError(this::loadView))
return false
}
return true
@@ -98,17 +100,23 @@ class SiteMonitorTabViewModel @Inject constructor(
}
private fun postUiState(state: SiteMonitorUiState) {
- launch {
+ scope.launch {
_uiState.value = state
}
}
fun onUrlLoaded() {
- postUiState(SiteMonitorUiState.Loaded)
+ if (uiState.value is SiteMonitorUiState.Prepared){
+ postUiState(SiteMonitorUiState.Loaded)
+ }
}
fun onWebViewError() {
- postUiState(mapper.toGenericError(this@SiteMonitorTabViewModel::loadView))
+ postUiState(mapper.toGenericError(::loadView))
+ }
+
+ fun onCleared() {
+ scope.cancel()
}
companion object {
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorUtils.kt b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorUtils.kt
index a4b25ed2a1c7..a64e92029030 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorUtils.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorUtils.kt
@@ -33,6 +33,14 @@ class SiteMonitorUtils @Inject constructor(
))
}
+ fun trackTabLoadingError(siteMonitorType: SiteMonitorType) {
+ analyticsTrackerWrapper.track(
+ AnalyticsTracker.Stat.SITE_MONITORING_TAB_LOADING_ERROR,
+ mapOf(
+ TAB_TRACK_KEY to siteMonitorType.analyticsDescription
+ ))
+ }
+
companion object {
const val HTTP_PATTERN = "(https?://)"
const val PHP_LOGS_PATTERN = "/php"
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorWebViewClient.kt b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorWebViewClient.kt
index 29305e0d2ea0..af0717e8481b 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorWebViewClient.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorWebViewClient.kt
@@ -7,13 +7,15 @@ import android.webkit.WebView
import android.webkit.WebViewClient
class SiteMonitorWebViewClient(
- private val listener: SiteMonitorWebViewClientListener
+ private val listener: SiteMonitorWebViewClientListener,
+ private val tabType: SiteMonitorType
) : WebViewClient() {
private var errorReceived = false
private var requestedUrl: String? = null
+
interface SiteMonitorWebViewClientListener {
- fun onWebViewPageLoaded(url: String)
- fun onWebViewReceivedError(url: String)
+ fun onWebViewPageLoaded(url: String, tabType: SiteMonitorType)
+ fun onWebViewReceivedError(url: String, tabType: SiteMonitorType)
}
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
return false
@@ -28,7 +30,7 @@ class SiteMonitorWebViewClient(
override fun onPageFinished(view: WebView, url: String?) {
super.onPageFinished(view, url)
if (!errorReceived) {
- url?.let { listener.onWebViewPageLoaded(it) }
+ url?.let { listener.onWebViewPageLoaded(it, tabType) }
}
}
@@ -40,7 +42,7 @@ class SiteMonitorWebViewClient(
// > Thus, it is recommended to perform minimum required work in this callback.
if (request?.isForMainFrame == true && requestedUrl == request.url.toString()) {
errorReceived = true
- listener.onWebViewReceivedError(request.url.toString())
+ listener.onWebViewReceivedError(request.url.toString(), tabType)
}
}
}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsModule.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsModule.kt
index a3cbd2848638..74984c278010 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsModule.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsModule.kt
@@ -67,6 +67,7 @@ import javax.inject.Named
import javax.inject.Singleton
const val INSIGHTS_USE_CASE = "InsightsUseCase"
+const val TRAFFIC_USE_CASE = "TrafficStatsUseCase"
const val DAY_STATS_USE_CASE = "DayStatsUseCase"
const val WEEK_STATS_USE_CASE = "WeekStatsUseCase"
const val MONTH_STATS_USE_CASE = "MonthStatsUseCase"
@@ -265,6 +266,32 @@ class StatsModule {
)
}
+ /**
+ * Provides a singleton usecase that represents the TRAFFIC stats screen.
+ * @param useCasesFactories build the use cases for the DAYS granularity
+ */
+ @Provides
+ @Singleton
+ @Named(TRAFFIC_USE_CASE)
+ @Suppress("LongParameterList")
+ fun provideTrafficUseCase(
+ statsStore: StatsStore,
+ @Named(BG_THREAD) bgDispatcher: CoroutineDispatcher,
+ @Named(UI_THREAD) mainDispatcher: CoroutineDispatcher,
+ statsSiteProvider: StatsSiteProvider,
+ @Named(GRANULAR_USE_CASE_FACTORIES) useCasesFactories: List<@JvmSuppressWildcards GranularUseCaseFactory>,
+ uiModelMapper: UiModelMapper
+ ): BaseListUseCase {
+ return BaseListUseCase(
+ bgDispatcher,
+ mainDispatcher,
+ statsSiteProvider,
+ useCasesFactories.map { it.build(DAYS, BLOCK) },
+ { statsStore.getTimeStatsTypes(it) },
+ uiModelMapper::mapTimeStats
+ )
+ }
+
/**
* Provides a singleton usecase that represents the Day stats screen.
* @param useCasesFactories build the use cases for the DAYS granularity
@@ -374,8 +401,10 @@ class StatsModule {
@Provides
@Singleton
@Named(LIST_STATS_USE_CASES)
+ @Suppress("LongParameterList")
fun provideListStatsUseCases(
@Named(INSIGHTS_USE_CASE) insightsUseCase: BaseListUseCase,
+ @Named(TRAFFIC_USE_CASE) trafficUseCase: BaseListUseCase,
@Named(DAY_STATS_USE_CASE) dayStatsUseCase: BaseListUseCase,
@Named(WEEK_STATS_USE_CASE) weekStatsUseCase: BaseListUseCase,
@Named(MONTH_STATS_USE_CASE) monthStatsUseCase: BaseListUseCase,
@@ -383,6 +412,7 @@ class StatsModule {
): Map {
return mapOf(
StatsSection.INSIGHTS to insightsUseCase,
+ StatsSection.TRAFFIC to trafficUseCase,
StatsSection.DAYS to dayStatsUseCase,
StatsSection.WEEKS to weekStatsUseCase,
StatsSection.MONTHS to monthStatsUseCase,
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewAllFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewAllFragment.kt
index 952113eb5ee0..80d8688c73a7 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewAllFragment.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewAllFragment.kt
@@ -191,17 +191,17 @@ class StatsViewAllFragment : Fragment(R.layout.stats_view_all_fragment) {
}
private fun StatsViewAllFragmentBinding.setupObservers(activity: FragmentActivity) {
- viewModel.isRefreshing.observe(viewLifecycleOwner, {
+ viewModel.isRefreshing.observe(viewLifecycleOwner) {
it?.let { isRefreshing ->
swipeToRefreshHelper.isRefreshing = isRefreshing
}
- })
+ }
- viewModel.showSnackbarMessage.observeEvent(viewLifecycleOwner, { holder ->
+ viewModel.showSnackbarMessage.observeEvent(viewLifecycleOwner) { holder ->
showSnackbar(activity, holder)
- })
+ }
- viewModel.data.observe(viewLifecycleOwner, {
+ viewModel.data.observe(viewLifecycleOwner) {
if (it != null) {
with(statsListFragment) {
recyclerView.visibility = if (it is Success) View.VISIBLE else View.GONE
@@ -214,41 +214,44 @@ class StatsViewAllFragment : Fragment(R.layout.stats_view_all_fragment) {
is Success -> {
loadData(recyclerView, prepareLayout(it.data, it.type))
}
+
is Loading -> {
loadData(loadingRecyclerView, prepareLayout(it.data, it.type))
}
+
is Error -> {
errorView.statsErrorView.button.setOnClickListener {
viewModel.onRetryClick()
}
}
+
is EmptyBlock -> {
}
}
}
}
- })
- viewModel.navigationTarget.observeEvent(viewLifecycleOwner, { target ->
+ }
+ viewModel.navigationTarget.observeEvent(viewLifecycleOwner) { target ->
navigator.navigate(activity, target)
- })
+ }
- viewModel.dateSelectorData.observe(viewLifecycleOwner, { dateSelectorUiModel ->
+ viewModel.dateSelectorData.observe(viewLifecycleOwner) { dateSelectorUiModel ->
statsListFragment.drawDateSelector(dateSelectorUiModel)
- })
+ }
- viewModel.navigationTarget.observeEvent(viewLifecycleOwner, { target ->
+ viewModel.navigationTarget.observeEvent(viewLifecycleOwner) { target ->
navigator.navigate(activity, target)
- })
+ }
- viewModel.selectedDate.observe(viewLifecycleOwner, { event ->
+ viewModel.selectedDate?.observe(viewLifecycleOwner) { event ->
if (event != null) {
viewModel.onDateChanged()
}
- })
+ }
- viewModel.toolbarHasShadow.observe(viewLifecycleOwner, { hasShadow ->
+ viewModel.toolbarHasShadow.observe(viewLifecycleOwner) { hasShadow ->
appBarLayout.showShadow(hasShadow == true)
- })
+ }
}
private fun showSnackbar(
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewAllViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewAllViewModel.kt
index 57785b26a6be..73de971c26d3 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewAllViewModel.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewAllViewModel.kt
@@ -20,8 +20,8 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.granular.SelectedDa
import org.wordpress.android.ui.stats.refresh.utils.StatsDateSelector
import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider
import org.wordpress.android.ui.utils.UiString.UiStringRes
-import org.wordpress.android.util.mapSafe
import org.wordpress.android.util.mapNullable
+import org.wordpress.android.util.mapSafe
import org.wordpress.android.util.throttle
import org.wordpress.android.viewmodel.Event
import org.wordpress.android.viewmodel.ScopedViewModel
@@ -31,14 +31,14 @@ class StatsViewAllViewModel(
val bgDispatcher: CoroutineDispatcher,
val useCase: BaseStatsUseCase<*, *>,
private val statsSiteProvider: StatsSiteProvider,
- private val dateSelector: StatsDateSelector,
+ private val dateSelector: StatsDateSelector?,
@StringRes val title: Int
) : ScopedViewModel(mainDispatcher) {
- val selectedDate = dateSelector.selectedDate
+ val selectedDate = dateSelector?.selectedDate
- val dateSelectorData: LiveData = dateSelector.dateSelectorData.mapNullable {
+ val dateSelectorData: LiveData = dateSelector?.dateSelectorData?.mapNullable {
it ?: DateSelectorUiModel(false)
- }
+ } ?: MutableLiveData(DateSelectorUiModel(false))
val navigationTarget: LiveData> = useCase.navigationTarget
@@ -62,12 +62,12 @@ class StatsViewAllViewModel(
fun start(startDate: SelectedDate?) {
launch {
startDate?.let {
- dateSelector.start(startDate)
+ dateSelector?.start(startDate)
}
loadData(refresh = false, forced = false)
- dateSelector.updateDateSelector()
+ dateSelector?.updateDateSelector()
}
- dateSelector.updateDateSelector()
+ dateSelector?.updateDateSelector()
}
@SuppressLint("NullSafeMutableLiveData")
@@ -109,13 +109,13 @@ class StatsViewAllViewModel(
fun onNextDateSelected() {
launch(mainDispatcher) {
- dateSelector.onNextDateSelected()
+ dateSelector?.onNextDateSelected()
}
}
fun onPreviousDateSelected() {
launch(mainDispatcher) {
- dateSelector.onPreviousDateSelected()
+ dateSelector?.onPreviousDateSelected()
}
}
@@ -131,7 +131,7 @@ class StatsViewAllViewModel(
}
}
- fun getSelectedDate(): SelectedDate {
- return dateSelector.getSelectedDate()
+ fun getSelectedDate(): SelectedDate? {
+ return dateSelector?.getSelectedDate()
}
}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewAllViewModelFactory.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewAllViewModelFactory.kt
index 167735228b8c..cec09374d5c0 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewAllViewModelFactory.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewAllViewModelFactory.kt
@@ -29,8 +29,6 @@ import org.wordpress.android.ui.stats.StatsViewType.SEARCH_TERMS
import org.wordpress.android.ui.stats.StatsViewType.TAGS_AND_CATEGORIES
import org.wordpress.android.ui.stats.StatsViewType.TOP_POSTS_AND_PAGES
import org.wordpress.android.ui.stats.StatsViewType.VIDEO_PLAYS
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.INSIGHTS
import org.wordpress.android.ui.stats.refresh.lists.detail.PostAverageViewsPerDayUseCase
import org.wordpress.android.ui.stats.refresh.lists.detail.PostMonthsAndYearsUseCase
import org.wordpress.android.ui.stats.refresh.lists.detail.PostRecentWeeksUseCase
@@ -56,7 +54,6 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.insights.usecases.T
import org.wordpress.android.ui.stats.refresh.lists.sections.insights.usecases.ViewsAndVisitorsUseCase
import org.wordpress.android.ui.stats.refresh.utils.StatsDateSelector
import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider
-import org.wordpress.android.ui.stats.refresh.utils.toStatsSection
import java.security.InvalidParameterException
import javax.inject.Inject
import javax.inject.Named
@@ -66,7 +63,7 @@ class StatsViewAllViewModelFactory(
private val bgDispatcher: CoroutineDispatcher,
private val useCase: BaseStatsUseCase<*, *>,
private val statsSiteProvider: StatsSiteProvider,
- private val dateSelector: StatsDateSelector,
+ private val dateSelector: StatsDateSelector?,
@StringRes private val titleResource: Int
) : ViewModelProvider.Factory {
override fun create(modelClass: Class): T {
@@ -92,34 +89,29 @@ class StatsViewAllViewModelFactory(
private val statsSiteProvider: StatsSiteProvider,
private val dateSelectorFactory: StatsDateSelector.Factory
) {
- fun build(type: StatsViewType, granularity: StatsGranularity?): StatsViewAllViewModelFactory {
- return when {
- type == ANNUAL_STATS -> buildAnnualStatsFactory()
- granularity == null -> buildFactory(type)
- else -> buildFactory(type, granularity)
- }
- }
-
- private fun buildFactory(type: StatsViewType, granularity: StatsGranularity): StatsViewAllViewModelFactory {
- val (useCase, title) = getGranularUseCase(type, granularity, granularFactories)
- return StatsViewAllViewModelFactory(
- mainDispatcher,
- bgDispatcher,
- useCase,
- statsSiteProvider,
- dateSelectorFactory.build(granularity.toStatsSection()),
- title
- )
+ fun build(type: StatsViewType, granularity: StatsGranularity?) = if (type == ANNUAL_STATS) {
+ buildAnnualStatsFactory()
+ } else {
+ buildFactory(type, granularity)
}
- private fun buildFactory(type: StatsViewType): StatsViewAllViewModelFactory {
- val (useCase, title) = getInsightsUseCase(type, insightsUseCases)
+ private fun buildFactory(type: StatsViewType, granularity: StatsGranularity?): StatsViewAllViewModelFactory {
+ val (useCase, title) = if (granularity == null) {
+ getInsightsUseCase(type, insightsUseCases)
+ } else {
+ getGranularUseCase(type, granularity, granularFactories)
+ }
+ val dateSelector = if (granularity == null) {
+ null
+ } else {
+ dateSelectorFactory.build(granularity)
+ }
return StatsViewAllViewModelFactory(
mainDispatcher,
bgDispatcher,
useCase,
statsSiteProvider,
- dateSelectorFactory.build(INSIGHTS),
+ dateSelector,
title
)
}
@@ -133,7 +125,7 @@ class StatsViewAllViewModelFactory(
bgDispatcher,
useCase,
statsSiteProvider,
- dateSelectorFactory.build(StatsSection.ANNUAL_STATS),
+ dateSelectorFactory.build(StatsGranularity.YEARS),
R.string.stats_insights_annual_site_stats
)
}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/BaseListUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/BaseListUseCase.kt
index 6c8632bbb5f3..b01c21f2abc7 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/BaseListUseCase.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/BaseListUseCase.kt
@@ -9,10 +9,10 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.wordpress.android.R
import org.wordpress.android.fluxc.model.SiteModel
+import org.wordpress.android.fluxc.network.utils.StatsGranularity
import org.wordpress.android.fluxc.store.StatsStore.StatsType
import org.wordpress.android.ui.pages.SnackbarMessageHolder
import org.wordpress.android.ui.stats.refresh.NavigationTarget
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection
import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.UiModel
import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase
import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.UseCaseModel
@@ -140,8 +140,8 @@ class BaseListUseCase(
data.value = null
}
- suspend fun onDateChanged(selectedSection: StatsSection) {
- onParamChanged(UseCaseParam.SelectedDateParam(selectedSection))
+ suspend fun onDateChanged(selectedGranularity: StatsGranularity) {
+ onParamChanged(UseCaseParam.SelectedDateParam(selectedGranularity))
}
fun onListSelected() {
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListFragment.kt
index 8169b738f924..1469325b5c26 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListFragment.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListFragment.kt
@@ -27,6 +27,7 @@ import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.UiModel.E
import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.UiModel.Error
import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.UiModel.Success
import org.wordpress.android.ui.stats.refresh.lists.detail.DetailListViewModel
+import org.wordpress.android.ui.stats.refresh.utils.SelectedTrafficGranularityManager
import org.wordpress.android.ui.stats.refresh.utils.StatsDateFormatter
import org.wordpress.android.ui.stats.refresh.utils.StatsNavigator
import org.wordpress.android.ui.stats.refresh.utils.drawDateSelector
@@ -57,6 +58,9 @@ class StatsListFragment : ViewPagerFragment(R.layout.stats_list_fragment) {
@Inject
lateinit var statsTrafficTabFeatureConfig: StatsTrafficTabFeatureConfig
+ @Inject
+ lateinit var selectedTrafficGranularityManager: SelectedTrafficGranularityManager
+
private lateinit var viewModel: StatsListViewModel
private lateinit var statsSection: StatsSection
@@ -158,9 +162,14 @@ class StatsListFragment : ViewPagerFragment(R.layout.stats_list_fragment) {
StatsGranularity.entries.map { getString(it.toNameResource()) }
).apply { setDropDownViewResource(R.layout.toolbar_spinner_dropdown_item) }
+ val selectedGranularityItemPos = StatsGranularity.entries.indexOf(
+ selectedTrafficGranularityManager.getSelectedTrafficGranularity()
+ )
+ dateSelector.granularitySpinner.setSelection(selectedGranularityItemPos)
+
dateSelector.granularitySpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
- // TODO update TRAFFIC tab
+ selectedTrafficGranularityManager.setSelectedTrafficGranularity(StatsGranularity.entries[position])
}
@Suppress("EmptyFunctionBlock")
@@ -243,9 +252,9 @@ class StatsListFragment : ViewPagerFragment(R.layout.stats_list_fragment) {
navigator.navigate(activity, target)
}
- viewModel.selectedDate.observe(viewLifecycleOwner) { event ->
+ viewModel.selectedDate?.observe(viewLifecycleOwner) { event ->
if (event != null) {
- viewModel.onDateChanged(event.selectedSection)
+ viewModel.onDateChanged(event.selectedGranularity)
}
}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListViewModel.kt
index 386a6575a3a7..1643bedabaae 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListViewModel.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListViewModel.kt
@@ -10,6 +10,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import org.wordpress.android.R
import org.wordpress.android.analytics.AnalyticsTracker.Stat
+import org.wordpress.android.fluxc.network.utils.StatsGranularity
import org.wordpress.android.modules.UI_THREAD
import org.wordpress.android.ui.stats.refresh.DAY_STATS_USE_CASE
import org.wordpress.android.ui.stats.refresh.INSIGHTS_USE_CASE
@@ -20,15 +21,10 @@ import org.wordpress.android.ui.stats.refresh.StatsViewModel.DateSelectorUiModel
import org.wordpress.android.ui.stats.refresh.TOTAL_COMMENTS_DETAIL_USE_CASE
import org.wordpress.android.ui.stats.refresh.TOTAL_FOLLOWERS_DETAIL_USE_CASE
import org.wordpress.android.ui.stats.refresh.TOTAL_LIKES_DETAIL_USE_CASE
+import org.wordpress.android.ui.stats.refresh.TRAFFIC_USE_CASE
import org.wordpress.android.ui.stats.refresh.VIEWS_AND_VISITORS_USE_CASE
import org.wordpress.android.ui.stats.refresh.WEEK_STATS_USE_CASE
import org.wordpress.android.ui.stats.refresh.YEAR_STATS_USE_CASE
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.DAYS
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.INSIGHTS
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.MONTHS
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.TRAFFIC
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.WEEKS
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.YEARS
import org.wordpress.android.ui.stats.refresh.utils.ActionCardHandler
import org.wordpress.android.ui.stats.refresh.utils.ItemPopupMenuHandler
import org.wordpress.android.ui.stats.refresh.utils.NewsCardHandler
@@ -49,7 +45,7 @@ abstract class StatsListViewModel(
defaultDispatcher: CoroutineDispatcher,
private val statsUseCase: BaseListUseCase,
private val analyticsTracker: AnalyticsTrackerWrapper,
- protected val dateSelector: StatsDateSelector,
+ protected val dateSelector: StatsDateSelector?,
popupMenuHandler: ItemPopupMenuHandler? = null,
private val newsCardHandler: NewsCardHandler? = null,
actionCardHandler: ActionCardHandler? = null
@@ -72,7 +68,7 @@ abstract class StatsListViewModel(
ANNUAL_STATS(R.string.stats_insights_annual_site_stats);
}
- val selectedDate = dateSelector.selectedDate
+ val selectedDate = dateSelector?.selectedDate
private val mutableNavigationTarget = MutableLiveData>()
val navigationTarget: LiveData> = mergeNotNull(
@@ -85,9 +81,9 @@ abstract class StatsListViewModel(
statsUseCase.data.throttle(viewModelScope, distinct = true)
}
- val dateSelectorData: LiveData = dateSelector.dateSelectorData.mapNullable {
+ val dateSelectorData: LiveData = dateSelector?.dateSelectorData?.mapNullable {
it ?: DateSelectorUiModel(false)
- }
+ } ?: MutableLiveData(DateSelectorUiModel(false))
val typesChanged = merge(
popupMenuHandler?.typeMoved,
@@ -115,13 +111,13 @@ abstract class StatsListViewModel(
fun onNextDateSelected() {
launch(Dispatchers.Default) {
- dateSelector.onNextDateSelected()
+ dateSelector?.onNextDateSelected()
}
}
fun onPreviousDateSelected() {
launch(Dispatchers.Default) {
- dateSelector.onPreviousDateSelected()
+ dateSelector?.onPreviousDateSelected()
}
}
@@ -131,14 +127,14 @@ abstract class StatsListViewModel(
}
}
- fun onDateChanged(selectedSection: StatsSection) {
+ fun onDateChanged(selectedGranularity: StatsGranularity) {
launch {
- statsUseCase.onDateChanged(selectedSection)
+ statsUseCase.onDateChanged(selectedGranularity)
}
}
fun onListSelected() {
- dateSelector.updateDateSelector()
+ dateSelector?.updateDateSelector()
}
fun onEmptyInsightsButtonClicked() {
@@ -156,10 +152,10 @@ abstract class StatsListViewModel(
isInitialized = true
launch {
statsUseCase.loadData()
- dateSelector.updateDateSelector()
+ dateSelector?.updateDateSelector()
}
}
- dateSelector.updateDateSelector()
+ dateSelector?.updateDateSelector()
}
sealed class UiModel {
@@ -185,7 +181,6 @@ class InsightsListViewModel
@Named(UI_THREAD) mainDispatcher: CoroutineDispatcher,
@Named(INSIGHTS_USE_CASE) private val insightsUseCase: BaseListUseCase,
analyticsTracker: AnalyticsTrackerWrapper,
- dateSelectorFactory: StatsDateSelector.Factory,
popupMenuHandler: ItemPopupMenuHandler,
newsCardHandler: NewsCardHandler,
actionCardHandler: ActionCardHandler
@@ -193,72 +188,117 @@ class InsightsListViewModel
mainDispatcher,
insightsUseCase,
analyticsTracker,
- dateSelectorFactory.build(INSIGHTS),
+ null,
popupMenuHandler,
newsCardHandler,
actionCardHandler
)
+class TrafficListViewModel @Inject constructor(
+ @Named(UI_THREAD) mainDispatcher: CoroutineDispatcher,
+ @Named(TRAFFIC_USE_CASE) statsUseCase: BaseListUseCase,
+ analyticsTracker: AnalyticsTrackerWrapper,
+ dateSelectorFactory: StatsDateSelector.Factory
+) : StatsListViewModel(
+ mainDispatcher,
+ statsUseCase,
+ analyticsTracker,
+ dateSelectorFactory.build(StatsGranularity.DAYS, isGranularitySpinnerVisible = true)
+)
+
class YearsListViewModel @Inject constructor(
@Named(UI_THREAD) mainDispatcher: CoroutineDispatcher,
@Named(YEAR_STATS_USE_CASE) statsUseCase: BaseListUseCase,
analyticsTracker: AnalyticsTrackerWrapper,
dateSelectorFactory: StatsDateSelector.Factory
-) : StatsListViewModel(mainDispatcher, statsUseCase, analyticsTracker, dateSelectorFactory.build(YEARS))
+) : StatsListViewModel(
+ mainDispatcher,
+ statsUseCase,
+ analyticsTracker,
+ dateSelectorFactory.build(StatsGranularity.YEARS)
+)
class MonthsListViewModel @Inject constructor(
@Named(UI_THREAD) mainDispatcher: CoroutineDispatcher,
@Named(MONTH_STATS_USE_CASE) statsUseCase: BaseListUseCase,
analyticsTracker: AnalyticsTrackerWrapper,
dateSelectorFactory: StatsDateSelector.Factory
-) : StatsListViewModel(mainDispatcher, statsUseCase, analyticsTracker, dateSelectorFactory.build(MONTHS))
+) : StatsListViewModel(
+ mainDispatcher,
+ statsUseCase,
+ analyticsTracker,
+ dateSelectorFactory.build(StatsGranularity.MONTHS)
+)
class WeeksListViewModel @Inject constructor(
@Named(UI_THREAD) mainDispatcher: CoroutineDispatcher,
@Named(WEEK_STATS_USE_CASE) statsUseCase: BaseListUseCase,
analyticsTracker: AnalyticsTrackerWrapper,
dateSelectorFactory: StatsDateSelector.Factory
-) : StatsListViewModel(mainDispatcher, statsUseCase, analyticsTracker, dateSelectorFactory.build(WEEKS))
+) : StatsListViewModel(
+ mainDispatcher,
+ statsUseCase,
+ analyticsTracker,
+ dateSelectorFactory.build(StatsGranularity.WEEKS)
+)
class DaysListViewModel @Inject constructor(
@Named(UI_THREAD) mainDispatcher: CoroutineDispatcher,
@Named(DAY_STATS_USE_CASE) statsUseCase: BaseListUseCase,
analyticsTracker: AnalyticsTrackerWrapper,
dateSelectorFactory: StatsDateSelector.Factory
-) : StatsListViewModel(mainDispatcher, statsUseCase, analyticsTracker, dateSelectorFactory.build(DAYS))
-
-class TrafficListViewModel @Inject constructor(
- @Named(UI_THREAD) mainDispatcher: CoroutineDispatcher,
- @Named(DAY_STATS_USE_CASE) statsUseCase: BaseListUseCase,
- analyticsTracker: AnalyticsTrackerWrapper,
- dateSelectorFactory: StatsDateSelector.Factory
-) : StatsListViewModel(mainDispatcher, statsUseCase, analyticsTracker, dateSelectorFactory.build(TRAFFIC))
+) : StatsListViewModel(
+ mainDispatcher,
+ statsUseCase,
+ analyticsTracker,
+ dateSelectorFactory.build(StatsGranularity.DAYS)
+)
-// Using Weeks granularity on new insight details screens
+// Using Weeks granularity on insight details screens
class InsightsDetailListViewModel @Inject constructor(
@Named(UI_THREAD) mainDispatcher: CoroutineDispatcher,
@Named(VIEWS_AND_VISITORS_USE_CASE) statsUseCase: BaseListUseCase,
analyticsTracker: AnalyticsTrackerWrapper,
dateSelectorFactory: StatsDateSelector.Factory
-) : StatsListViewModel(mainDispatcher, statsUseCase, analyticsTracker, dateSelectorFactory.build(WEEKS))
+) : StatsListViewModel(
+ mainDispatcher,
+ statsUseCase,
+ analyticsTracker,
+ dateSelectorFactory.build(StatsGranularity.WEEKS)
+)
class TotalLikesDetailListViewModel @Inject constructor(
@Named(UI_THREAD) mainDispatcher: CoroutineDispatcher,
@Named(TOTAL_LIKES_DETAIL_USE_CASE) statsUseCase: BaseListUseCase,
analyticsTracker: AnalyticsTrackerWrapper,
dateSelectorFactory: StatsDateSelector.Factory
-) : StatsListViewModel(mainDispatcher, statsUseCase, analyticsTracker, dateSelectorFactory.build(WEEKS))
+) : StatsListViewModel(
+ mainDispatcher,
+ statsUseCase,
+ analyticsTracker,
+ dateSelectorFactory.build(StatsGranularity.WEEKS)
+)
class TotalCommentsDetailListViewModel @Inject constructor(
@Named(UI_THREAD) mainDispatcher: CoroutineDispatcher,
@Named(TOTAL_COMMENTS_DETAIL_USE_CASE) statsUseCase: BaseListUseCase,
analyticsTracker: AnalyticsTrackerWrapper,
dateSelectorFactory: StatsDateSelector.Factory
-) : StatsListViewModel(mainDispatcher, statsUseCase, analyticsTracker, dateSelectorFactory.build(WEEKS))
+) : StatsListViewModel(
+ mainDispatcher,
+ statsUseCase,
+ analyticsTracker,
+ dateSelectorFactory.build(StatsGranularity.WEEKS)
+)
class TotalFollowersDetailListViewModel @Inject constructor(
@Named(UI_THREAD) mainDispatcher: CoroutineDispatcher,
@Named(TOTAL_FOLLOWERS_DETAIL_USE_CASE) statsUseCase: BaseListUseCase,
analyticsTracker: AnalyticsTrackerWrapper,
dateSelectorFactory: StatsDateSelector.Factory
-) : StatsListViewModel(mainDispatcher, statsUseCase, analyticsTracker, dateSelectorFactory.build(WEEKS))
+) : StatsListViewModel(
+ mainDispatcher,
+ statsUseCase,
+ analyticsTracker,
+ dateSelectorFactory.build(StatsGranularity.WEEKS)
+)
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/DetailListViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/DetailListViewModel.kt
index 553ff06d2997..9ceb3563f701 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/DetailListViewModel.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/DetailListViewModel.kt
@@ -1,11 +1,11 @@
package org.wordpress.android.ui.stats.refresh.lists.detail
import kotlinx.coroutines.CoroutineDispatcher
+import org.wordpress.android.fluxc.network.utils.StatsGranularity
import org.wordpress.android.modules.UI_THREAD
import org.wordpress.android.ui.stats.refresh.BLOCK_DETAIL_USE_CASE
import org.wordpress.android.ui.stats.refresh.lists.BaseListUseCase
import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.DETAIL
import org.wordpress.android.ui.stats.refresh.utils.StatsDateSelector
import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper
import javax.inject.Inject
@@ -17,9 +17,14 @@ class DetailListViewModel
@Named(BLOCK_DETAIL_USE_CASE) private val detailUseCase: BaseListUseCase,
analyticsTracker: AnalyticsTrackerWrapper,
dateSelectorFactory: StatsDateSelector.Factory
-) : StatsListViewModel(mainDispatcher, detailUseCase, analyticsTracker, dateSelectorFactory.build(DETAIL)) {
+) : StatsListViewModel(
+ mainDispatcher,
+ detailUseCase,
+ analyticsTracker,
+ dateSelectorFactory.build(StatsGranularity.DAYS)
+) {
override fun onCleared() {
super.onCleared()
- dateSelector.clear()
+ dateSelector?.clear()
}
}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostDayViewsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostDayViewsUseCase.kt
index c5e7c1809c52..d6dd4e02c4d6 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostDayViewsUseCase.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostDayViewsUseCase.kt
@@ -8,7 +8,6 @@ import org.wordpress.android.fluxc.store.StatsStore.PostDetailType
import org.wordpress.android.fluxc.store.stats.PostDetailStore
import org.wordpress.android.modules.BG_THREAD
import org.wordpress.android.modules.UI_THREAD
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.DETAIL
import org.wordpress.android.ui.stats.refresh.lists.detail.PostDayViewsUseCase.UiState
import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase
import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem
@@ -39,7 +38,7 @@ class PostDayViewsUseCase
mainDispatcher,
backgroundDispatcher,
UiState(),
- uiUpdateParams = listOf(UseCaseParam.SelectedDateParam(DETAIL))
+ uiUpdateParams = listOf(UseCaseParam.SelectedDateParam(DAYS))
) {
override suspend fun loadCachedData(): PostDetailStatsModel? {
return statsPostProvider.postId?.let { postId ->
@@ -59,15 +58,15 @@ class PostDayViewsUseCase
return when {
error != null -> {
- selectedDateProvider.onDateLoadingFailed(DETAIL)
+ selectedDateProvider.onDateLoadingFailed(DAYS)
State.Error(error.message ?: error.type.name)
}
model != null && model.hasData() -> {
- selectedDateProvider.onDateLoadingSucceeded(DETAIL)
+ selectedDateProvider.onDateLoadingSucceeded(DAYS)
State.Data(model)
}
else -> {
- selectedDateProvider.onDateLoadingSucceeded(DETAIL)
+ selectedDateProvider.onDateLoadingSucceeded(DAYS)
State.Empty()
}
}
@@ -78,14 +77,14 @@ class PostDayViewsUseCase
val visibleBarCount = uiState.visibleBarCount ?: domainModel.dayViews.size
if (domainModel.hasData() && visibleBarCount > 0) {
- val periodFromProvider = selectedDateProvider.getSelectedDate(DETAIL)
+ val periodFromProvider = selectedDateProvider.getSelectedDate(DAYS)
val availablePeriods = domainModel.dayViews.takeLast(visibleBarCount)
val availableDates = availablePeriods.map { statsDateFormatter.parseStatsDate(DAYS, it.period) }
val selectedPeriod = periodFromProvider ?: availableDates.last()
val index = availableDates.indexOf(selectedPeriod)
- selectedDateProvider.selectDate(selectedPeriod, availableDates, DETAIL)
+ selectedDateProvider.selectDate(selectedPeriod, availableDates, DAYS)
val shiftedIndex = index + domainModel.dayViews.size - visibleBarCount
val selectedItem = domainModel.dayViews.getOrNull(shiftedIndex) ?: domainModel.dayViews.last()
@@ -107,7 +106,7 @@ class PostDayViewsUseCase
)
)
} else {
- selectedDateProvider.onDateLoadingFailed(DETAIL)
+ selectedDateProvider.onDateLoadingFailed(DAYS)
AppLog.e(T.STATS, "There is no data to be shown in the post day view block")
}
return items
@@ -129,7 +128,7 @@ class PostDayViewsUseCase
val selectedDate = statsDateFormatter.parseStatsDate(DAYS, period)
selectedDateProvider.selectDate(
selectedDate,
- DETAIL
+ DAYS
)
}
}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/BaseStatsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/BaseStatsUseCase.kt
index ec7c148fdc3c..7b86388e0e3a 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/BaseStatsUseCase.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/BaseStatsUseCase.kt
@@ -9,9 +9,9 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.wordpress.android.R
+import org.wordpress.android.fluxc.network.utils.StatsGranularity
import org.wordpress.android.fluxc.store.StatsStore.StatsType
import org.wordpress.android.ui.stats.refresh.NavigationTarget
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection
import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.State.Data
import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.State.Empty
import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.State.Error
@@ -282,6 +282,6 @@ abstract class BaseStatsUseCase(
}
sealed class UseCaseParam {
- data class SelectedDateParam(val statsSection: StatsSection) : UseCaseParam()
+ data class SelectedDateParam(val statsGranularity: StatsGranularity) : UseCaseParam()
}
}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/GranularStatefulUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/GranularStatefulUseCase.kt
index 30d1e2ae7bc3..dce50cf9b582 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/GranularStatefulUseCase.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/GranularStatefulUseCase.kt
@@ -8,7 +8,6 @@ import org.wordpress.android.fluxc.store.StatsStore.StatsType
import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase
import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem
import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider
-import org.wordpress.android.ui.stats.refresh.utils.toStatsSection
import java.util.Date
@Suppress("LongParameterList")
@@ -25,7 +24,7 @@ abstract class GranularStatefulUseCase(
mainDispatcher,
backgroundDispatcher,
defaultUiState,
- listOf(UseCaseParam.SelectedDateParam(statsGranularity.toStatsSection()))
+ listOf(UseCaseParam.SelectedDateParam(statsGranularity))
) {
abstract suspend fun loadCachedData(selectedDate: Date, site: SiteModel): DOMAIN_MODEL?
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/GranularStatelessUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/GranularStatelessUseCase.kt
index 8b6ffb02ff39..f4fc676d1b99 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/GranularStatelessUseCase.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/GranularStatelessUseCase.kt
@@ -8,7 +8,6 @@ import org.wordpress.android.fluxc.store.StatsStore.StatsType
import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.StatelessUseCase
import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem
import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider
-import org.wordpress.android.ui.stats.refresh.utils.toStatsSection
import java.util.Date
abstract class GranularStatelessUseCase(
@@ -22,7 +21,7 @@ abstract class GranularStatelessUseCase(
type,
mainDispatcher,
backgroundDispatcher,
- listOf(UseCaseParam.SelectedDateParam(statsGranularity.toStatsSection()))
+ listOf(UseCaseParam.SelectedDateParam(statsGranularity))
) {
abstract suspend fun loadCachedData(selectedDate: Date, site: SiteModel): DOMAIN_MODEL?
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/SelectedDateProvider.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/SelectedDateProvider.kt
index 6cc52b2e5572..1ce18f0db9b7 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/SelectedDateProvider.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/SelectedDateProvider.kt
@@ -11,17 +11,11 @@ import kotlinx.parcelize.Parcelize
import org.wordpress.android.analytics.AnalyticsTracker.Stat.STATS_NEXT_DATE_TAPPED
import org.wordpress.android.analytics.AnalyticsTracker.Stat.STATS_PREVIOUS_DATE_TAPPED
import org.wordpress.android.fluxc.network.utils.StatsGranularity
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.DAYS
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.MONTHS
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.WEEKS
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.YEARS
import org.wordpress.android.ui.stats.refresh.utils.StatsDateFormatter
-import org.wordpress.android.ui.stats.refresh.utils.toStatsSection
-import org.wordpress.android.ui.stats.refresh.utils.trackWithSection
+import org.wordpress.android.ui.stats.refresh.utils.trackWithGranularity
import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper
-import org.wordpress.android.util.extensions.readListCompat
import org.wordpress.android.util.extensions.getParcelableCompat
+import org.wordpress.android.util.extensions.readListCompat
import org.wordpress.android.util.filter
import java.util.Date
import javax.inject.Inject
@@ -36,121 +30,100 @@ class SelectedDateProvider
private val analyticsTrackerWrapper: AnalyticsTrackerWrapper
) {
private val mutableDates = mutableMapOf(
- DAYS to SelectedDate(loading = true),
- WEEKS to SelectedDate(loading = true),
- MONTHS to SelectedDate(loading = true),
- YEARS to SelectedDate(loading = true)
+ StatsGranularity.DAYS to SelectedDate(loading = true),
+ StatsGranularity.WEEKS to SelectedDate(loading = true),
+ StatsGranularity.MONTHS to SelectedDate(loading = true),
+ StatsGranularity.YEARS to SelectedDate(loading = true)
)
- private val selectedDateChanged = MutableLiveData()
-
- fun granularSelectedDateChanged(statsSection: StatsSection): LiveData {
- return selectedDateChanged.filter { it?.selectedSection == statsSection }
- }
+ private val selectedDateChanged = MutableLiveData()
- fun selectDate(date: Date, statsSection: StatsSection) {
- val selectedDate = getSelectedDateState(statsSection)
- updateSelectedDate(selectedDate.copy(dateValue = date), statsSection)
+ fun granularSelectedDateChanged(statsGranularity: StatsGranularity): LiveData {
+ return selectedDateChanged.filter { it?.selectedGranularity == statsGranularity }
}
fun selectDate(date: Date, statsGranularity: StatsGranularity) {
- selectDate(date, statsGranularity.toStatsSection())
+ val selectedDate = getSelectedDateState(statsGranularity)
+ updateSelectedDate(selectedDate.copy(dateValue = date), statsGranularity)
}
- fun selectDate(updatedDate: Date, availableDates: List, statsSection: StatsSection) {
- val selectedDate = getSelectedDateState(statsSection)
+ fun selectDate(updatedDate: Date, availableDates: List, statsGranularity: StatsGranularity) {
+ val selectedDate = getSelectedDateState(statsGranularity)
if (selectedDate.dateValue != updatedDate || selectedDate.availableDates != availableDates) {
updateSelectedDate(
selectedDate.copy(dateValue = updatedDate, availableDates = availableDates),
- statsSection
+ statsGranularity
)
}
}
- fun selectDate(updatedDate: Date, availableDates: List, statsGranularity: StatsGranularity) {
- selectDate(updatedDate, availableDates, statsGranularity.toStatsSection())
- }
-
- fun updateSelectedDate(selectedDate: SelectedDate, statsSection: StatsSection) {
- val currentDate = mutableDates[statsSection]
- mutableDates[statsSection] = selectedDate
+ fun updateSelectedDate(selectedDate: SelectedDate, statsGranularity: StatsGranularity) {
+ val currentDate = mutableDates[statsGranularity]
+ mutableDates[statsGranularity] = selectedDate
if (selectedDate != currentDate) {
- selectedDateChanged.postValue(SectionChange(statsSection))
+ selectedDateChanged.postValue(GranularityChange(statsGranularity))
}
}
fun setInitialSelectedPeriod(statsGranularity: StatsGranularity, period: String) {
val updatedDate = statsDateFormatter.parseStatsDate(statsGranularity, period)
val selectedDate = getSelectedDateState(statsGranularity)
- updateSelectedDate(selectedDate.copy(dateValue = updatedDate), statsGranularity.toStatsSection())
+ updateSelectedDate(selectedDate.copy(dateValue = updatedDate), statsGranularity)
}
fun getSelectedDate(statsGranularity: StatsGranularity): Date? {
- return getSelectedDate(statsGranularity.toStatsSection())
- }
-
- fun getSelectedDate(statsSection: StatsSection): Date? {
- return getSelectedDateState(statsSection).dateValue
+ return getSelectedDateState(statsGranularity).dateValue
}
fun getSelectedDateState(statsGranularity: StatsGranularity): SelectedDate {
- return getSelectedDateState(statsGranularity.toStatsSection())
+ return mutableDates[statsGranularity] ?: SelectedDate(loading = true)
}
- fun getSelectedDateState(statsSection: StatsSection): SelectedDate {
- return mutableDates[statsSection] ?: SelectedDate(loading = true)
- }
-
- fun hasPreviousDate(statsSection: StatsSection): Boolean {
- val selectedDate = getSelectedDateState(statsSection)
+ fun hasPreviousDate(statsGranularity: StatsGranularity): Boolean {
+ val selectedDate = getSelectedDateState(statsGranularity)
return selectedDate.hasData() && selectedDate.getDateIndex() > 0
}
- fun hasNextDate(statsSection: StatsSection): Boolean {
- val selectedDate = getSelectedDateState(statsSection)
+ fun hasNextDate(statsGranularity: StatsGranularity): Boolean {
+ val selectedDate = getSelectedDateState(statsGranularity)
return selectedDate.hasData() &&
selectedDate.getDateIndex() < selectedDate.availableDates.size - 1
}
- fun selectPreviousDate(statsSection: StatsSection) {
- val selectedDateState = getSelectedDateState(statsSection)
+ fun selectPreviousDate(statsGranularity: StatsGranularity) {
+ val selectedDateState = getSelectedDateState(statsGranularity)
if (selectedDateState.hasData()) {
- analyticsTrackerWrapper.trackWithSection(STATS_PREVIOUS_DATE_TAPPED, statsSection)
- updateSelectedDate(selectedDateState.copy(dateValue = selectedDateState.getPreviousDate()), statsSection)
+ analyticsTrackerWrapper.trackWithGranularity(STATS_PREVIOUS_DATE_TAPPED, statsGranularity)
+ updateSelectedDate(
+ selectedDateState.copy(dateValue = selectedDateState.getPreviousDate()),
+ statsGranularity
+ )
}
}
- fun selectNextDate(statsSection: StatsSection) {
- val selectedDateState = getSelectedDateState(statsSection)
+ fun selectNextDate(statsGranularity: StatsGranularity) {
+ val selectedDateState = getSelectedDateState(statsGranularity)
if (selectedDateState.hasData()) {
- analyticsTrackerWrapper.trackWithSection(STATS_NEXT_DATE_TAPPED, statsSection)
- updateSelectedDate(selectedDateState.copy(dateValue = selectedDateState.getNextDate()), statsSection)
+ analyticsTrackerWrapper.trackWithGranularity(STATS_NEXT_DATE_TAPPED, statsGranularity)
+ updateSelectedDate(selectedDateState.copy(dateValue = selectedDateState.getNextDate()), statsGranularity)
}
}
fun onDateLoadingFailed(statsGranularity: StatsGranularity) {
- onDateLoadingFailed(statsGranularity.toStatsSection())
- }
-
- fun onDateLoadingFailed(statsSection: StatsSection) {
- val selectedDate = getSelectedDateState(statsSection)
+ val selectedDate = getSelectedDateState(statsGranularity)
if (selectedDate.dateValue != null && !selectedDate.error) {
- updateSelectedDate(selectedDate.copy(error = true, loading = false), statsSection)
+ updateSelectedDate(selectedDate.copy(error = true, loading = false), statsGranularity)
} else if (selectedDate.dateValue == null) {
- updateSelectedDate(SelectedDate(error = true, loading = false), statsSection)
+ updateSelectedDate(SelectedDate(error = true, loading = false), statsGranularity)
}
}
fun onDateLoadingSucceeded(statsGranularity: StatsGranularity) {
- onDateLoadingSucceeded(statsGranularity.toStatsSection())
- }
-
- fun onDateLoadingSucceeded(statsSection: StatsSection) {
- val selectedDate = getSelectedDateState(statsSection)
+ val selectedDate = getSelectedDateState(statsGranularity)
if (selectedDate.dateValue != null && selectedDate.error) {
- updateSelectedDate(selectedDate.copy(error = false, loading = false), statsSection)
+ updateSelectedDate(selectedDate.copy(error = false, loading = false), statsGranularity)
} else if (selectedDate.dateValue == null) {
- updateSelectedDate(SelectedDate(error = false, loading = false), statsSection)
+ updateSelectedDate(SelectedDate(error = false, loading = false), statsGranularity)
}
}
@@ -161,8 +134,8 @@ class SelectedDateProvider
selectedDateChanged.value = null
}
- fun clear(statsSection: StatsSection) {
- mutableDates[statsSection] = SelectedDate(loading = true)
+ fun clear(statsGranularity: StatsGranularity) {
+ mutableDates[statsGranularity] = SelectedDate(loading = true)
selectedDateChanged.value = null
}
@@ -173,7 +146,12 @@ class SelectedDateProvider
}
fun onRestoreInstanceState(savedState: Bundle) {
- for (period in listOf(DAYS, WEEKS, MONTHS, YEARS)) {
+ for (period in listOf(
+ StatsGranularity.DAYS,
+ StatsGranularity.WEEKS,
+ StatsGranularity.MONTHS,
+ StatsGranularity.YEARS
+ )) {
val selectedDate = savedState.getParcelableCompat(buildStateKey(period))
if (selectedDate != null) {
mutableDates[period] = selectedDate
@@ -181,7 +159,7 @@ class SelectedDateProvider
}
}
- private fun buildStateKey(key: StatsSection) = SELECTED_DATE_STATE_KEY + key
+ private fun buildStateKey(key: StatsGranularity) = SELECTED_DATE_STATE_KEY + key
@Parcelize
@SuppressLint("ParcelCreator")
@@ -241,5 +219,5 @@ class SelectedDateProvider
}
}
- data class SectionChange(val selectedSection: StatsSection)
+ data class GranularityChange(val selectedGranularity: StatsGranularity)
}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCase.kt
index ef7b192d6ba7..104ebebb120c 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCase.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCase.kt
@@ -24,7 +24,6 @@ import org.wordpress.android.ui.stats.refresh.lists.widget.WidgetUpdater.StatsWi
import org.wordpress.android.ui.stats.refresh.utils.StatsDateFormatter
import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider
import org.wordpress.android.ui.stats.refresh.utils.StatsUtils
-import org.wordpress.android.ui.stats.refresh.utils.toStatsSection
import org.wordpress.android.ui.stats.refresh.utils.trackGranular
import org.wordpress.android.util.AppLog
import org.wordpress.android.util.AppLog.T
@@ -60,7 +59,7 @@ class OverviewUseCase constructor(
mainDispatcher,
backgroundDispatcher,
UiState(),
- uiUpdateParams = listOf(UseCaseParam.SelectedDateParam(statsGranularity.toStatsSection()))
+ uiUpdateParams = listOf(UseCaseParam.SelectedDateParam(statsGranularity))
) {
override fun buildLoadingItem(): List =
listOf(
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/AnnualSiteStatsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/AnnualSiteStatsUseCase.kt
index 3b4faa13812e..f9e5d4e8a284 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/AnnualSiteStatsUseCase.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/AnnualSiteStatsUseCase.kt
@@ -4,12 +4,12 @@ import android.view.View
import kotlinx.coroutines.CoroutineDispatcher
import org.wordpress.android.R
import org.wordpress.android.fluxc.model.stats.YearsInsightsModel
+import org.wordpress.android.fluxc.network.utils.StatsGranularity
import org.wordpress.android.fluxc.store.StatsStore.InsightType.ANNUAL_SITE_STATS
import org.wordpress.android.fluxc.store.stats.insights.MostPopularInsightsStore
import org.wordpress.android.modules.BG_THREAD
import org.wordpress.android.modules.UI_THREAD
import org.wordpress.android.ui.stats.refresh.NavigationTarget
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.ANNUAL_STATS
import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.StatelessUseCase
import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem
import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Link
@@ -59,13 +59,13 @@ class AnnualSiteStatsUseCase(
override fun buildLoadingItem(): List = listOf(Title(R.string.stats_insights_this_year_site_stats))
override fun buildUiModel(domainModel: YearsInsightsModel): List {
- val periodFromProvider = selectedDateProvider.getSelectedDate(ANNUAL_STATS)
+ val periodFromProvider = selectedDateProvider.getSelectedDate(StatsGranularity.YEARS)
val availablePeriods = domainModel.years
val availableDates = availablePeriods.map { yearToDate(it.year) }
val selectedPeriod = periodFromProvider ?: availableDates.last()
val index = availableDates.indexOf(selectedPeriod)
- selectedDateProvider.selectDate(selectedPeriod, availableDates, ANNUAL_STATS)
+ selectedDateProvider.selectDate(selectedPeriod, availableDates, StatsGranularity.YEARS)
val items = mutableListOf()
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/ViewsAndVisitorsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/ViewsAndVisitorsUseCase.kt
index 930d1bd04e49..01b1e4d5d106 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/ViewsAndVisitorsUseCase.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/ViewsAndVisitorsUseCase.kt
@@ -29,7 +29,6 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.insights.usecases.V
import org.wordpress.android.ui.stats.refresh.lists.widget.WidgetUpdater.StatsWidgetUpdaters
import org.wordpress.android.ui.stats.refresh.utils.StatsDateFormatter
import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider
-import org.wordpress.android.ui.stats.refresh.utils.toStatsSection
import org.wordpress.android.ui.stats.refresh.utils.trackGranular
import org.wordpress.android.ui.stats.refresh.utils.trackViewsVisitorsChips
import org.wordpress.android.ui.stats.refresh.utils.trackWithType
@@ -68,7 +67,7 @@ class ViewsAndVisitorsUseCase
mainDispatcher,
backgroundDispatcher,
UiState(),
- uiUpdateParams = listOf(UseCaseParam.SelectedDateParam(statsGranularity.toStatsSection()))
+ uiUpdateParams = listOf(UseCaseParam.SelectedDateParam(statsGranularity))
) {
override fun buildLoadingItem(): List =
listOf(
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/SelectedSectionManager.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/SelectedSectionManager.kt
index a71f504a41af..fc69e6cd1837 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/SelectedSectionManager.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/SelectedSectionManager.kt
@@ -10,12 +10,7 @@ import org.wordpress.android.fluxc.network.utils.StatsGranularity.MONTHS
import org.wordpress.android.fluxc.network.utils.StatsGranularity.WEEKS
import org.wordpress.android.fluxc.network.utils.StatsGranularity.YEARS
import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.ANNUAL_STATS
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.DETAIL
import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.INSIGHTS
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.TOTAL_COMMENTS_DETAIL
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.TOTAL_FOLLOWERS_DETAIL
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.TOTAL_LIKES_DETAIL
import javax.inject.Inject
const val SELECTED_SECTION_KEY = "SELECTED_STATS_SECTION_KEY"
@@ -47,9 +42,14 @@ class SelectedSectionManager
fun StatsSection.toStatsGranularity(): StatsGranularity? {
return when (this) {
- ANNUAL_STATS, DETAIL, TOTAL_LIKES_DETAIL, TOTAL_COMMENTS_DETAIL, TOTAL_FOLLOWERS_DETAIL, INSIGHTS -> null
+ StatsSection.TRAFFIC,
+ StatsSection.ANNUAL_STATS,
+ StatsSection.DETAIL,
+ StatsSection.TOTAL_LIKES_DETAIL,
+ StatsSection.TOTAL_COMMENTS_DETAIL,
+ StatsSection.TOTAL_FOLLOWERS_DETAIL,
+ StatsSection.INSIGHTS -> null
StatsSection.INSIGHT_DETAIL,
- StatsSection.TRAFFIC -> DAYS // Replace with TRAFFIC when it's implemented
StatsSection.DAYS -> DAYS
StatsSection.WEEKS -> WEEKS
StatsSection.MONTHS -> MONTHS
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/SelectedTrafficGranularityManager.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/SelectedTrafficGranularityManager.kt
new file mode 100644
index 000000000000..c17ec67b92aa
--- /dev/null
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/SelectedTrafficGranularityManager.kt
@@ -0,0 +1,20 @@
+package org.wordpress.android.ui.stats.refresh.utils
+
+import android.content.SharedPreferences
+import org.wordpress.android.fluxc.network.utils.StatsGranularity
+import org.wordpress.android.fluxc.network.utils.StatsGranularity.DAYS
+import javax.inject.Inject
+
+const val SELECTED_TRAFFIC_GRANULARITY_KEY = "SELECTED_TRAFFIC_GRANULARITY_KEY"
+
+class SelectedTrafficGranularityManager
+@Inject constructor(private val sharedPrefs: SharedPreferences) {
+ fun getSelectedTrafficGranularity(): StatsGranularity {
+ val value = sharedPrefs.getString(SELECTED_TRAFFIC_GRANULARITY_KEY, DAYS.name)
+ return value?.let { StatsGranularity.valueOf(value) } ?: DAYS
+ }
+
+ fun setSelectedTrafficGranularity(selectedTrafficGranularity: StatsGranularity) {
+ sharedPrefs.edit().putString(SELECTED_TRAFFIC_GRANULARITY_KEY, selectedTrafficGranularity.name).apply()
+ }
+}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/StatsAnalyticsUtils.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/StatsAnalyticsUtils.kt
index 95bb9b5e0f79..8113a9b097e7 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/StatsAnalyticsUtils.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/StatsAnalyticsUtils.kt
@@ -4,9 +4,6 @@ import org.wordpress.android.analytics.AnalyticsTracker.Stat
import org.wordpress.android.analytics.AnalyticsTracker.Stat.STATS_INSIGHTS_VIEWS_VISITORS_TOGGLED
import org.wordpress.android.fluxc.network.utils.StatsGranularity
import org.wordpress.android.fluxc.store.StatsStore.InsightType
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.INSIGHT_DETAIL
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.TRAFFIC
import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsWidgetConfigureFragment.WidgetType
import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsWidgetConfigureFragment.WidgetType.ALL_TIME_VIEWS
import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsWidgetConfigureFragment.WidgetType.TODAY_VIEWS
@@ -19,9 +16,6 @@ private const val DAYS_PROPERTY = "days"
private const val WEEKS_PROPERTY = "weeks"
private const val MONTHS_PROPERTY = "months"
private const val YEARS_PROPERTY = "years"
-private const val INSIGHTS_PROPERTY = "insights"
-private const val DETAIL_PROPERTY = "detail"
-private const val ANNUAL_STATS_PROPERTY = "annual_stats"
private const val TYPE = "type"
private const val TYPES = "types"
private const val WIDGET_TYPE = "widget_type"
@@ -30,9 +24,6 @@ private const val WEEKLY_VIEWS_WIDGET_PROPERTY = "weekly_views"
private const val WEEK_TOTALS_WIDGET_PROPERTY = "week_totals"
private const val ALL_TIME_WIDGET_PROPERTY = "all_time"
private const val MINIFIED_WIDGET_PROPERTY = "minified"
-private const val TOTAL_LIKES_PROPERTY = "total_likes_detail"
-private const val TOTAL_COMMENTS_PROPERTY = "total_comments_detail"
-private const val TOTAL_FOLLOWERS_PROPERTY = "total_followers_detail"
private const val CHIP_VIEWS_PROPERTY = "views"
private const val CHIP_VISITORS__PROPERTY = "visitors"
@@ -54,20 +45,8 @@ fun AnalyticsTrackerWrapper.trackViewsVisitorsChips(position: Int) {
this.track(STATS_INSIGHTS_VIEWS_VISITORS_TOGGLED, mapOf(TYPE to property))
}
-fun AnalyticsTrackerWrapper.trackWithSection(stat: Stat, section: StatsSection) {
- val property = when (section) {
- StatsSection.DAYS, TRAFFIC -> DAYS_PROPERTY // Replace with TRAFFIC when it's implemented
- StatsSection.WEEKS -> WEEKS_PROPERTY
- StatsSection.MONTHS -> MONTHS_PROPERTY
- StatsSection.YEARS -> YEARS_PROPERTY
- StatsSection.INSIGHTS, INSIGHT_DETAIL -> INSIGHTS_PROPERTY
- StatsSection.DETAIL -> DETAIL_PROPERTY
- StatsSection.ANNUAL_STATS -> ANNUAL_STATS_PROPERTY
- StatsSection.TOTAL_LIKES_DETAIL -> TOTAL_LIKES_PROPERTY
- StatsSection.TOTAL_COMMENTS_DETAIL -> TOTAL_COMMENTS_PROPERTY
- StatsSection.TOTAL_FOLLOWERS_DETAIL -> TOTAL_FOLLOWERS_PROPERTY
- }
- this.track(stat, mapOf(GRANULARITY_PROPERTY to property))
+fun AnalyticsTrackerWrapper.trackWithGranularity(stat: Stat, granularity: StatsGranularity) {
+ this.track(stat, mapOf(GRANULARITY_PROPERTY to granularity))
}
fun AnalyticsTrackerWrapper.trackWithType(stat: Stat, insightType: InsightType) {
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/StatsDateSelector.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/StatsDateSelector.kt
index 89a34a9786bf..bf7d89e37a20 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/StatsDateSelector.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/StatsDateSelector.kt
@@ -3,14 +3,7 @@ package org.wordpress.android.ui.stats.refresh.utils
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import org.wordpress.android.fluxc.network.utils.StatsGranularity
-import org.wordpress.android.fluxc.network.utils.StatsGranularity.DAYS
-import org.wordpress.android.fluxc.network.utils.StatsGranularity.MONTHS
-import org.wordpress.android.fluxc.network.utils.StatsGranularity.WEEKS
-import org.wordpress.android.fluxc.network.utils.StatsGranularity.YEARS
import org.wordpress.android.ui.stats.refresh.StatsViewModel.DateSelectorUiModel
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.INSIGHTS
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.TRAFFIC
import org.wordpress.android.ui.stats.refresh.lists.sections.granular.SelectedDateProvider
import org.wordpress.android.ui.stats.refresh.lists.sections.granular.SelectedDateProvider.SelectedDate
import org.wordpress.android.util.config.StatsTrafficTabFeatureConfig
@@ -22,45 +15,39 @@ constructor(
private val selectedDateProvider: SelectedDateProvider,
private val statsDateFormatter: StatsDateFormatter,
private val siteProvider: StatsSiteProvider,
- private val statsTrafficTabFeatureConfig: StatsTrafficTabFeatureConfig,
- private val statsSection: StatsSection
+ private val statsGranularity: StatsGranularity,
+ private val isGranularitySpinnerVisible: Boolean,
+ private val statsTrafficTabFeatureConfig: StatsTrafficTabFeatureConfig
) {
private val _dateSelectorUiModel = MutableLiveData()
val dateSelectorData: LiveData = _dateSelectorUiModel
- val selectedDate = selectedDateProvider.granularSelectedDateChanged(this.statsSection)
+ val selectedDate = selectedDateProvider.granularSelectedDateChanged(statsGranularity)
.perform {
updateDateSelector()
}
fun start(startDate: SelectedDate) {
- selectedDateProvider.updateSelectedDate(startDate, statsSection)
+ selectedDateProvider.updateSelectedDate(startDate, statsGranularity)
}
fun updateDateSelector() {
- val shouldShowDateSelection = this.statsSection != INSIGHTS
- val shouldShowGranularitySpinner = statsTrafficTabFeatureConfig.isEnabled() && this.statsSection == TRAFFIC
-
val updatedDate = getDateLabelForSection()
val currentState = dateSelectorData.value
- if (!shouldShowDateSelection && currentState?.isVisible != false) {
- emitValue(currentState, DateSelectorUiModel(false))
+ val timeZone = if (statsTrafficTabFeatureConfig.isEnabled()) {
+ null
} else {
- val timeZone = if (statsTrafficTabFeatureConfig.isEnabled()) {
- null
- } else {
- statsDateFormatter.printTimeZone(siteProvider.siteModel)
- }
- val updatedState = DateSelectorUiModel(
- shouldShowDateSelection,
- shouldShowGranularitySpinner,
- updatedDate,
- enableSelectPrevious = selectedDateProvider.hasPreviousDate(statsSection),
- enableSelectNext = selectedDateProvider.hasNextDate(statsSection),
- timeZone = timeZone
- )
- emitValue(currentState, updatedState)
+ statsDateFormatter.printTimeZone(siteProvider.siteModel)
}
+ val updatedState = DateSelectorUiModel(
+ true,
+ isGranularitySpinnerVisible,
+ updatedDate,
+ enableSelectPrevious = selectedDateProvider.hasPreviousDate(statsGranularity),
+ enableSelectNext = selectedDateProvider.hasNextDate(statsGranularity),
+ timeZone = timeZone
+ )
+ emitValue(currentState, updatedState)
}
private fun emitValue(
@@ -74,41 +61,25 @@ constructor(
private fun getDateLabelForSection(): String? {
return statsDateFormatter.printGranularDate(
- selectedDateProvider.getSelectedDate(statsSection) ?: selectedDateProvider.getCurrentDate(),
- toStatsGranularity()
+ selectedDateProvider.getSelectedDate(statsGranularity) ?: selectedDateProvider.getCurrentDate(),
+ statsGranularity
)
}
- private fun toStatsGranularity(): StatsGranularity {
- return when (statsSection) {
- StatsSection.DETAIL,
- StatsSection.TOTAL_LIKES_DETAIL,
- StatsSection.TOTAL_COMMENTS_DETAIL,
- StatsSection.TOTAL_FOLLOWERS_DETAIL,
- StatsSection.INSIGHTS,
- StatsSection.INSIGHT_DETAIL,
- StatsSection.DAYS, TRAFFIC -> DAYS // Replace with TRAFFIC when it's implemented
- StatsSection.WEEKS -> WEEKS
- StatsSection.MONTHS -> MONTHS
- StatsSection.ANNUAL_STATS,
- StatsSection.YEARS -> YEARS
- }
- }
-
fun onNextDateSelected() {
- selectedDateProvider.selectNextDate(statsSection)
+ selectedDateProvider.selectNextDate(statsGranularity)
}
fun onPreviousDateSelected() {
- selectedDateProvider.selectPreviousDate(statsSection)
+ selectedDateProvider.selectPreviousDate(statsGranularity)
}
fun clear() {
- selectedDateProvider.clear(statsSection)
+ selectedDateProvider.clear(statsGranularity)
}
fun getSelectedDate(): SelectedDate {
- return selectedDateProvider.getSelectedDateState(statsSection)
+ return selectedDateProvider.getSelectedDateState(statsGranularity)
}
class Factory
@@ -118,13 +89,14 @@ constructor(
private val statsDateFormatter: StatsDateFormatter,
private val statsTrafficTabFeatureConfig: StatsTrafficTabFeatureConfig
) {
- fun build(statsSection: StatsSection): StatsDateSelector {
+ fun build(statsGranularity: StatsGranularity, isGranularitySpinnerVisible: Boolean = false): StatsDateSelector {
return StatsDateSelector(
selectedDateProvider,
statsDateFormatter,
siteProvider,
- statsTrafficTabFeatureConfig,
- statsSection
+ statsGranularity,
+ isGranularitySpinnerVisible,
+ statsTrafficTabFeatureConfig
)
}
}
diff --git a/WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorMapperTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorMapperTest.kt
new file mode 100644
index 000000000000..d9b349bc2873
--- /dev/null
+++ b/WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorMapperTest.kt
@@ -0,0 +1,58 @@
+package org.wordpress.android.ui.sitemonitor
+
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.junit.MockitoJUnitRunner
+import org.mockito.kotlin.whenever
+import org.wordpress.android.BaseUnitTest
+
+@ExperimentalCoroutinesApi
+@RunWith(MockitoJUnitRunner::class)
+class SiteMonitorMapperTest : BaseUnitTest() {
+ @Mock
+ lateinit var siteMonitorUtils: SiteMonitorUtils
+
+ private lateinit var siteMonitorMapper: SiteMonitorMapper
+
+ @Before
+ fun setup() {
+ siteMonitorMapper = SiteMonitorMapper(siteMonitorUtils)
+ }
+
+ @Test
+ fun `given prepared request, when mapper is called, then site monitor model is created`() {
+ whenever(siteMonitorUtils.getUserAgent()).thenReturn(USER_AGENT)
+
+ val state = siteMonitorMapper.toPrepared(URL, ADDRESS_TO_LOAD, SiteMonitorType.METRICS)
+
+ assertThat(state.model.siteMonitorType).isEqualTo(SiteMonitorType.METRICS)
+ assertThat(state.model.url).isEqualTo(URL)
+ assertThat(state.model.addressToLoad).isEqualTo(ADDRESS_TO_LOAD)
+ assertThat(state.model.userAgent).isEqualTo(USER_AGENT)
+ }
+
+ @Test
+ fun `given network error, when mapper is called, then NoNetwork error is created`() {
+ val state = siteMonitorMapper.toNoNetworkError(mock())
+
+ assertThat(state).isInstanceOf(SiteMonitorUiState.NoNetworkError::class.java)
+ }
+
+ @Test
+ fun `given generic error error, when mapper is called, then Generic error is created`() {
+ val state = siteMonitorMapper.toGenericError(mock())
+
+ assertThat(state).isInstanceOf(SiteMonitorUiState.GenericError::class.java)
+ }
+
+ companion object {
+ const val USER_AGENT = "user_agent"
+ const val URL = "url"
+ const val ADDRESS_TO_LOAD = "address_to_load"
+ }
+}
diff --git a/WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorParentViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorParentViewModelTest.kt
new file mode 100644
index 000000000000..411f72a9a442
--- /dev/null
+++ b/WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorParentViewModelTest.kt
@@ -0,0 +1,170 @@
+package org.wordpress.android.ui.sitemonitor
+
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnitRunner
+import org.mockito.kotlin.any
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+import org.wordpress.android.BaseUnitTest
+import org.wordpress.android.fluxc.model.SiteModel
+
+@ExperimentalCoroutinesApi
+@RunWith(MockitoJUnitRunner::class)
+class SiteMonitorParentViewModelTest: BaseUnitTest(){
+ @Mock
+ private lateinit var siteMonitorUtils: SiteMonitorUtils
+ @Mock
+ private lateinit var metricsViewModel: SiteMonitorTabViewModelSlice
+ @Mock
+ private lateinit var phpLogViewModel: SiteMonitorTabViewModelSlice
+ @Mock
+ private lateinit var webServerViewModel: SiteMonitorTabViewModelSlice
+
+ private lateinit var viewModel: SiteMonitorParentViewModel
+
+ @Before
+ fun setUp() {
+ viewModel = SiteMonitorParentViewModel(
+ testDispatcher(),
+ siteMonitorUtils,
+ metricsViewModel,
+ phpLogViewModel,
+ webServerViewModel
+ )
+ }
+
+ @Test
+ fun `when viewmodel is started, then track screen shown`() {
+ val site = mock()
+ viewModel.start(site)
+
+ verify(siteMonitorUtils).trackActivityLaunched()
+ }
+
+ @Test
+ fun `when viewmodel is created, then view model slices are initialized`() {
+ verify(metricsViewModel).initialize(any())
+ verify(phpLogViewModel).initialize(any())
+ verify(webServerViewModel).initialize(any())
+ }
+
+ @Test
+ fun `when start is invoked, then view models are started with the correct tab item`() {
+ val site = mock()
+ viewModel.start(site)
+
+ verify(metricsViewModel).start(SiteMonitorType.METRICS, SiteMonitorTabItem.Metrics.urlTemplate, site)
+ verify(phpLogViewModel).start(SiteMonitorType.PHP_LOGS, SiteMonitorTabItem.PHPLogs.urlTemplate, site)
+ verify(webServerViewModel).start(
+ SiteMonitorType.WEB_SERVER_LOGS,
+ SiteMonitorTabItem.WebServerLogs.urlTemplate,
+ site
+ )
+ }
+
+ @Test
+ fun `when loadData is invoked, then view models are started with the correct tab item`() {
+ val site = mock()
+ viewModel.start(site)
+
+ clearInvocations(metricsViewModel, phpLogViewModel, webServerViewModel)
+
+ viewModel.loadData()
+
+ verify(metricsViewModel).start(SiteMonitorType.METRICS, SiteMonitorTabItem.Metrics.urlTemplate, site)
+ verify(phpLogViewModel).start(SiteMonitorType.PHP_LOGS, SiteMonitorTabItem.PHPLogs.urlTemplate, site)
+ verify(webServerViewModel).start(
+ SiteMonitorType.WEB_SERVER_LOGS,
+ SiteMonitorTabItem.WebServerLogs.urlTemplate,
+ site
+ )
+ }
+
+ @Test
+ fun `given metrics, when getUiState is invoked, then ui state is returned`() {
+ whenever(metricsViewModel.uiState).thenReturn(mock())
+ val site = mock()
+ viewModel.start(site)
+
+ advanceUntilIdle()
+
+ val state = viewModel.getUiState(SiteMonitorType.METRICS)
+
+ assertThat(state).isNotNull
+ }
+
+ @Test
+ fun `given phplogs, when getUiState is invoked, then ui state is returned`() {
+ whenever(phpLogViewModel.uiState).thenReturn(mock())
+ val site = mock()
+ viewModel.start(site)
+
+ advanceUntilIdle()
+
+ val state = viewModel.getUiState(SiteMonitorType.PHP_LOGS)
+
+ assertThat(state).isNotNull
+ }
+
+ @Test
+ fun `given webserver logs, when getUiState is invoked, then ui state is returned`() {
+ whenever(webServerViewModel.uiState).thenReturn(mock())
+ val site = mock()
+ viewModel.start(site)
+
+ advanceUntilIdle()
+
+ val state = viewModel.getUiState(SiteMonitorType.WEB_SERVER_LOGS)
+
+ assertThat(state).isNotNull
+ }
+
+ @Test
+ fun `given metrics, when onUrlLoaded is invoked, then metric vm slice onUrlLoaded is invoked`() {
+ viewModel.onUrlLoaded(SiteMonitorType.METRICS)
+
+ verify(metricsViewModel).onUrlLoaded()
+ }
+
+ @Test
+ fun `given php logs, when onUrlLoaded is invoked, then php logs vm slice onUrlLoaded is invoked`() {
+ viewModel.onUrlLoaded(SiteMonitorType.PHP_LOGS)
+
+ verify(phpLogViewModel).onUrlLoaded()
+ }
+
+ @Test
+ fun `given webserver logs, when onUrlLoaded is invoked, then webserver logs vm slice onUrlLoaded is invoked`() {
+ viewModel.onUrlLoaded(SiteMonitorType.WEB_SERVER_LOGS)
+
+ verify(webServerViewModel).onUrlLoaded()
+ }
+
+ @Test
+ fun `given metrics, when onWebViewError is invoked, then metric vm slice onWebViewError is invoked`() {
+ viewModel.onWebViewError(SiteMonitorType.METRICS)
+
+ verify(metricsViewModel).onWebViewError()
+ }
+
+ @Test
+ fun `given php logs, when onWebViewError is invoked, then php logs vm slice onWebViewError is invoked`() {
+ viewModel.onWebViewError(SiteMonitorType.PHP_LOGS)
+
+ verify(phpLogViewModel).onWebViewError()
+ }
+
+ @Test
+ fun `given webserver logs, when onWebViewError is invoked, then webserver vm slice onWebViewError is invoked`() {
+ viewModel.onWebViewError(SiteMonitorType.WEB_SERVER_LOGS)
+
+ verify(webServerViewModel).onWebViewError()
+ }
+}
diff --git a/WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModelSliceTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModelSliceTest.kt
new file mode 100644
index 000000000000..3fe113c67fb7
--- /dev/null
+++ b/WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModelSliceTest.kt
@@ -0,0 +1,127 @@
+package org.wordpress.android.ui.sitemonitor
+
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+import org.wordpress.android.BaseUnitTest
+import org.wordpress.android.fluxc.model.SiteModel
+import org.wordpress.android.fluxc.store.AccountStore
+import org.wordpress.android.fluxc.store.SiteStore
+import org.wordpress.android.util.NetworkUtilsWrapper
+
+@ExperimentalCoroutinesApi
+class SiteMonitorTabViewModelSliceTest : BaseUnitTest() {
+ @Mock
+ private lateinit var networkUtilsWrapper: NetworkUtilsWrapper
+ @Mock
+ private lateinit var accountStore: AccountStore
+ @Mock
+ private lateinit var mapper: SiteMonitorMapper
+ @Mock
+ private lateinit var siteMonitorUtils: SiteMonitorUtils
+ @Mock
+ private lateinit var siteStore: SiteStore
+
+ private lateinit var viewModel: SiteMonitorTabViewModelSlice
+
+ val site = mock()
+
+ @Before
+ fun setUp() = test {
+ viewModel = SiteMonitorTabViewModelSlice(
+ networkUtilsWrapper,
+ accountStore,
+ mapper,
+ siteMonitorUtils,
+ siteStore
+ )
+
+ whenever(accountStore.account).thenReturn(mock())
+ whenever(accountStore.account.userName).thenReturn(USER_NAME)
+ whenever(accountStore.accessToken).thenReturn(ACCESS_TOKEN)
+
+ whenever(networkUtilsWrapper.isNetworkAvailable()).thenReturn(true)
+ whenever(mapper.toGenericError(any())).thenReturn(mock())
+ whenever(mapper.toNoNetworkError(any())).thenReturn(mock())
+ whenever(mapper.toPrepared(any(), any(), any())).thenReturn(mock())
+
+ whenever(site.url).thenReturn(URL)
+ whenever(siteMonitorUtils.sanitizeSiteUrl(any())).thenReturn(URL)
+ whenever(siteMonitorUtils.getAuthenticationPostData(any(), any(), any(), any(), any())).thenReturn(URL)
+
+ viewModel.initialize(testScope())
+ }
+
+ @Test
+ fun `when slice is instantiated, then uiState is in preparing`() {
+ assertThat(viewModel.uiState.value).isEqualTo(SiteMonitorUiState.Preparing)
+ }
+
+ @Test
+ fun `given loadView(), when slice is started, then uiState is in prepared`() = test {
+ viewModel.start(SiteMonitorType.METRICS, SiteMonitorTabItem.Metrics.urlTemplate, site)
+
+ assertThat(viewModel.uiState.value).isInstanceOf(SiteMonitorUiState.Prepared::class.java)
+ }
+
+ @Test
+ fun `given null username, when slice is started, then uiState is in toGenericError`() {
+ whenever(accountStore.account.userName).thenReturn(null)
+
+ viewModel.start(SiteMonitorType.METRICS, SiteMonitorTabItem.Metrics.urlTemplate, site)
+
+ assertThat(viewModel.uiState.value).isInstanceOf(SiteMonitorUiState.GenericError::class.java)
+ }
+
+ @Test
+ fun `given null accessToken, when slice is started, then uiState is in toGenericError`() {
+ whenever(accountStore.accessToken).thenReturn(null)
+
+ viewModel.start(SiteMonitorType.METRICS, SiteMonitorTabItem.Metrics.urlTemplate, site)
+
+ assertThat(viewModel.uiState.value).isInstanceOf(SiteMonitorUiState.GenericError::class.java)
+ }
+
+ @Test
+ fun `given no network, when slice is started, then uiState is in error`() {
+ whenever(networkUtilsWrapper.isNetworkAvailable()).thenReturn(false)
+
+ viewModel.start(SiteMonitorType.METRICS, SiteMonitorTabItem.Metrics.urlTemplate, site)
+
+ assertThat(viewModel.uiState.value).isInstanceOf(SiteMonitorUiState.NoNetworkError::class.java)
+ }
+
+ @Test
+ fun `given prepared state, when url is loaded, then uiState loaded is posted`() = test {
+ viewModel.start(SiteMonitorType.METRICS, SiteMonitorTabItem.Metrics.urlTemplate, site)
+ advanceUntilIdle()
+ viewModel.onUrlLoaded()
+
+ assertThat(viewModel.uiState.value).isInstanceOf(SiteMonitorUiState.Loaded::class.java)
+ }
+
+ @Test
+ fun `given preparing state, when url is loaded, then uiState loaded is not posted`() = test {
+ viewModel.onUrlLoaded()
+
+ assertThat(viewModel.uiState.value).isInstanceOf(SiteMonitorUiState.Preparing::class.java)
+ }
+
+ @Test
+ fun `when web view error, then error state is posted`() = test {
+ viewModel.onWebViewError()
+
+ assertThat(viewModel.uiState.value).isInstanceOf(SiteMonitorUiState.GenericError::class.java)
+ }
+
+ companion object {
+ const val USER_NAME = "user_name"
+ const val ACCESS_TOKEN = "access_token"
+ const val URL = "test.wordpress.com"
+ }
+}
diff --git a/WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorUtilsTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorUtilsTest.kt
new file mode 100644
index 000000000000..0bf5076d32d5
--- /dev/null
+++ b/WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorUtilsTest.kt
@@ -0,0 +1,92 @@
+package org.wordpress.android.ui.sitemonitor
+
+import junit.framework.TestCase.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnitRunner
+import org.mockito.kotlin.verify
+import org.wordpress.android.analytics.AnalyticsTracker
+import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper
+
+@RunWith(MockitoJUnitRunner::class)
+class SiteMonitorUtilsTest {
+ @Mock
+ lateinit var analyticsTrackerWrapper: AnalyticsTrackerWrapper
+
+ private lateinit var siteMonitorUtils: SiteMonitorUtils
+
+ @Before
+ fun setup() {
+ siteMonitorUtils = SiteMonitorUtils(analyticsTrackerWrapper)
+ }
+
+ @Test
+ fun `when activity is launched, then event is tracked`() {
+ siteMonitorUtils.trackActivityLaunched()
+
+ verify(analyticsTrackerWrapper).track(AnalyticsTracker.Stat.SITE_MONITORING_SCREEN_SHOWN)
+ }
+
+ @Test
+ fun `given url matches pattern, when sanitize is requested, then url is sanitized`() {
+ val result = siteMonitorUtils.sanitizeSiteUrl("http://example.com")
+
+ assertEquals("example.com", result)
+ }
+
+ @Test
+ fun `given url is null, when sanitize is requested, then url is empty`() {
+ val result = siteMonitorUtils.sanitizeSiteUrl(null)
+
+ assertEquals("", result)
+ }
+
+ @Test
+ fun `given url does not match pattern, when sanitize is requested, then url is not sanitized`() {
+ val url = "gibberish"
+ val result = siteMonitorUtils.sanitizeSiteUrl(url)
+
+ assertEquals(url, result)
+ }
+
+ @Test
+ fun `when metrics tab is launched, then event is tracked`() {
+ siteMonitorUtils.trackTabLoaded(SiteMonitorType.METRICS)
+
+ // Verify that the correct method was called on the analyticsTrackerWrapper
+ verify(analyticsTrackerWrapper).track(
+ AnalyticsTracker.Stat.SITE_MONITORING_TAB_SHOWN,
+ mapOf(
+ SiteMonitorUtils.TAB_TRACK_KEY to SiteMonitorType.METRICS.analyticsDescription
+ )
+ )
+ }
+
+ @Test
+ fun `when php logs tab is launched, then event is tracked`() {
+ siteMonitorUtils.trackTabLoaded(SiteMonitorType.PHP_LOGS)
+
+ // Verify that the correct method was called on the analyticsTrackerWrapper
+ verify(analyticsTrackerWrapper).track(
+ AnalyticsTracker.Stat.SITE_MONITORING_TAB_SHOWN,
+ mapOf(
+ SiteMonitorUtils.TAB_TRACK_KEY to SiteMonitorType.PHP_LOGS.analyticsDescription
+ )
+ )
+ }
+
+ @Test
+ fun `when web server logs tab is launched, then event is tracked`() {
+ siteMonitorUtils.trackTabLoaded(SiteMonitorType.WEB_SERVER_LOGS)
+
+ // Verify that the correct method was called on the analyticsTrackerWrapper
+ verify(analyticsTrackerWrapper).track(
+ AnalyticsTracker.Stat.SITE_MONITORING_TAB_SHOWN,
+ mapOf(
+ SiteMonitorUtils.TAB_TRACK_KEY to SiteMonitorType.WEB_SERVER_LOGS.analyticsDescription
+ )
+ )
+ }
+}
diff --git a/WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorWebViewClientTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorWebViewClientTest.kt
new file mode 100644
index 000000000000..38b11dbd998d
--- /dev/null
+++ b/WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorWebViewClientTest.kt
@@ -0,0 +1,70 @@
+package org.wordpress.android.ui.sitemonitor
+
+import android.net.Uri
+import android.webkit.WebResourceRequest
+import android.webkit.WebView
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+import org.wordpress.android.BaseUnitTest
+import android.webkit.WebResourceError
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mockito.mock
+import org.mockito.kotlin.never
+
+@ExperimentalCoroutinesApi
+class SiteMonitorWebViewClientTest : BaseUnitTest() {
+ @Mock
+ private lateinit var mockListener: SiteMonitorWebViewClient.SiteMonitorWebViewClientListener
+
+ @Mock
+ private lateinit var uri: Uri
+
+ private lateinit var webViewClient: SiteMonitorWebViewClient
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.openMocks(this)
+ webViewClient = SiteMonitorWebViewClient(mockListener, SiteMonitorType.METRICS)
+ }
+
+ @Test
+ fun `when onPageFinished, then should invoke on web view page loaded`() {
+ webViewClient.onPageFinished(mock(WebView::class.java), "https://example.com")
+
+ verify(mockListener).onWebViewPageLoaded("https://example.com", SiteMonitorType.METRICS)
+ }
+
+ @Test
+ fun `when onReceivedError, then should invoke on web view error received`() {
+ val mockRequest = mock(WebResourceRequest::class.java)
+ whenever(mockRequest.isForMainFrame).thenReturn(true)
+ val url = "https://some.domain"
+ whenever(uri.toString()).thenReturn(url)
+ whenever(mockRequest.url).thenReturn(uri)
+
+ webViewClient.onPageStarted(mock(WebView::class.java), url, null)
+ webViewClient.onReceivedError(
+ mock(WebView::class.java),
+ mockRequest,
+ mock(WebResourceError::class.java)
+ )
+
+ verify(mockListener).onWebViewReceivedError(url, SiteMonitorType.METRICS)
+ }
+
+ @Test
+ fun `when onPageFinished, then should not invoke OnReceivedError`() {
+ val url = "https://some.domain"
+
+ webViewClient.onPageFinished(mock(WebView::class.java), url)
+
+ verify(mockListener, never()).onWebViewReceivedError(anyString(), any())
+ }
+}
+
diff --git a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/StatsDateSelectorTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/StatsDateSelectorTest.kt
index dd6096d09abd..af7fd0adf5f2 100644
--- a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/StatsDateSelectorTest.kt
+++ b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/StatsDateSelectorTest.kt
@@ -10,9 +10,8 @@ import org.mockito.kotlin.whenever
import org.wordpress.android.BaseUnitTest
import org.wordpress.android.fluxc.network.utils.StatsGranularity
import org.wordpress.android.ui.stats.refresh.StatsViewModel.DateSelectorUiModel
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection
import org.wordpress.android.ui.stats.refresh.lists.sections.granular.SelectedDateProvider
-import org.wordpress.android.ui.stats.refresh.lists.sections.granular.SelectedDateProvider.SectionChange
+import org.wordpress.android.ui.stats.refresh.lists.sections.granular.SelectedDateProvider.GranularityChange
import org.wordpress.android.ui.stats.refresh.utils.StatsDateFormatter
import org.wordpress.android.ui.stats.refresh.utils.StatsDateSelector
import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider
@@ -34,28 +33,29 @@ class StatsDateSelectorTest : BaseUnitTest() {
lateinit var statsTrafficTabFeatureConfig: StatsTrafficTabFeatureConfig
private val selectedDate = Date(0)
private val selectedDateLabel = "Jan 1"
- private val statsSection = StatsSection.DAYS
private val statsGranularity = StatsGranularity.DAYS
private val updatedDate = Date(10)
private val updatedLabel = "Jan 2"
- private val dateProviderSelectedDate = MutableLiveData()
+ private val dateProviderSelectedDate = MutableLiveData()
private lateinit var dateSelector: StatsDateSelector
@Before
fun setUp() {
- dateProviderSelectedDate.value = SectionChange(statsSection)
- whenever(selectedDateProvider.granularSelectedDateChanged(statsSection)).thenReturn(dateProviderSelectedDate)
+ dateProviderSelectedDate.value = GranularityChange(statsGranularity)
+ whenever(selectedDateProvider.granularSelectedDateChanged(statsGranularity))
+ .thenReturn(dateProviderSelectedDate)
dateSelector = StatsDateSelector(
selectedDateProvider,
statsDateFormatter,
siteProvider,
- statsTrafficTabFeatureConfig,
- statsSection
+ statsGranularity,
+ false,
+ statsTrafficTabFeatureConfig
)
- whenever(selectedDateProvider.getSelectedDate(statsSection)).thenReturn(selectedDate)
+ whenever(selectedDateProvider.getSelectedDate(statsGranularity)).thenReturn(selectedDate)
whenever(statsDateFormatter.printGranularDate(selectedDate, statsGranularity)).thenReturn(selectedDateLabel)
whenever(statsDateFormatter.printGranularDate(updatedDate, statsGranularity)).thenReturn(updatedLabel)
whenever(statsTrafficTabFeatureConfig.isEnabled()).thenReturn(true)
@@ -77,9 +77,9 @@ class StatsDateSelectorTest : BaseUnitTest() {
@Test
fun `shows date selector on days screen`() {
- whenever(selectedDateProvider.getSelectedDate(statsSection)).thenReturn(selectedDate)
- whenever(selectedDateProvider.hasPreviousDate(statsSection)).thenReturn(true)
- whenever(selectedDateProvider.hasNextDate(statsSection)).thenReturn(true)
+ whenever(selectedDateProvider.getSelectedDate(statsGranularity)).thenReturn(selectedDate)
+ whenever(selectedDateProvider.hasPreviousDate(statsGranularity)).thenReturn(true)
+ whenever(selectedDateProvider.hasNextDate(statsGranularity)).thenReturn(true)
var model: DateSelectorUiModel? = null
dateSelector.dateSelectorData.observeForever { model = it }
@@ -95,8 +95,8 @@ class StatsDateSelectorTest : BaseUnitTest() {
@Test
fun `updates date selector on date change`() {
- whenever(selectedDateProvider.hasPreviousDate(statsSection)).thenReturn(true)
- whenever(selectedDateProvider.hasNextDate(statsSection)).thenReturn(true)
+ whenever(selectedDateProvider.hasPreviousDate(statsGranularity)).thenReturn(true)
+ whenever(selectedDateProvider.hasNextDate(statsGranularity)).thenReturn(true)
var model: DateSelectorUiModel? = null
dateSelector.dateSelectorData.observeForever { model = it }
@@ -104,31 +104,10 @@ class StatsDateSelectorTest : BaseUnitTest() {
Assertions.assertThat(model?.date).isEqualTo(selectedDateLabel)
- whenever(selectedDateProvider.getSelectedDate(statsSection)).thenReturn(updatedDate)
+ whenever(selectedDateProvider.getSelectedDate(statsGranularity)).thenReturn(updatedDate)
dateSelector.updateDateSelector()
Assertions.assertThat(model?.date).isEqualTo(updatedLabel)
}
-
- @Test
- fun `verify date selector hidden for insights`() {
- whenever(selectedDateProvider.granularSelectedDateChanged(StatsSection.INSIGHTS)).thenReturn(
- dateProviderSelectedDate
- )
- dateSelector = StatsDateSelector(
- selectedDateProvider,
- statsDateFormatter,
- siteProvider,
- statsTrafficTabFeatureConfig,
- StatsSection.INSIGHTS
- )
- var model: DateSelectorUiModel? = null
- dateSelector.dateSelectorData.observeForever { model = it }
-
- dateSelector.updateDateSelector()
-
- Assertions.assertThat(model).isNotNull
- Assertions.assertThat(model?.isVisible).isFalse()
- }
}
diff --git a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/AnnualSiteStatsUseCaseTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/AnnualSiteStatsUseCaseTest.kt
index 260266fd77dd..22bddae81009 100644
--- a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/AnnualSiteStatsUseCaseTest.kt
+++ b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/AnnualSiteStatsUseCaseTest.kt
@@ -13,12 +13,12 @@ import org.wordpress.android.R
import org.wordpress.android.fluxc.model.SiteModel
import org.wordpress.android.fluxc.model.stats.YearsInsightsModel
import org.wordpress.android.fluxc.model.stats.YearsInsightsModel.YearInsights
+import org.wordpress.android.fluxc.network.utils.StatsGranularity
import org.wordpress.android.fluxc.store.StatsStore.OnStatsFetched
import org.wordpress.android.fluxc.store.StatsStore.StatsError
import org.wordpress.android.fluxc.store.StatsStore.StatsErrorType.GENERIC_ERROR
import org.wordpress.android.fluxc.store.stats.insights.MostPopularInsightsStore
import org.wordpress.android.ui.stats.refresh.NavigationTarget
-import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.ANNUAL_STATS
import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.UseCaseMode.BLOCK
import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.UseCaseMode.VIEW_ALL
import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.UseCaseModel
@@ -107,7 +107,11 @@ class AnnualSiteStatsUseCaseTest : BaseUnitTest() {
selectedDate.set(Calendar.YEAR, 2019)
selectedDate.set(Calendar.MONTH, Calendar.DECEMBER)
selectedDate.set(Calendar.DAY_OF_MONTH, 31)
- verify(selectedDateProvider, times(1)).selectDate(selectedDate.time, listOf(selectedDate.time), ANNUAL_STATS)
+ verify(selectedDateProvider, times(1)).selectDate(
+ selectedDate.time,
+ listOf(selectedDate.time),
+ StatsGranularity.YEARS
+ )
}
@Test
diff --git a/build.gradle b/build.gradle
index 284bcf07204d..b909a47e97fc 100644
--- a/build.gradle
+++ b/build.gradle
@@ -23,7 +23,7 @@ ext {
automatticRestVersion = '1.0.8'
automatticStoriesVersion = '2.4.0'
automatticTracksVersion = '3.3.0'
- gutenbergMobileVersion = 'v1.112.0-alpha3'
+ gutenbergMobileVersion = 'v1.112.0-alpha5'
wordPressAztecVersion = 'v2.0'
wordPressFluxCVersion = '2.64.0'
wordPressLoginVersion = '1.11.0'
@@ -32,7 +32,8 @@ ext {
indexosMediaForMobileVersion = '43a9026f0973a2f0a74fa813132f6a16f7499c3a'
// debug
- stethoVersion = '1.6.0'
+ flipperVersion = '0.245.0'
+ soLoaderVersion = '0.10.5'
// main
androidInstallReferrerVersion = '2.2'
@@ -88,7 +89,7 @@ ext {
zendeskVersion = '5.1.2'
// react native
- facebookReactVersion = '0.71.11'
+ facebookReactVersion = '0.71.15'
// test
assertjVersion = '3.23.1'
diff --git a/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java b/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java
index 4ecc68844c66..9c93b7293a73 100644
--- a/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java
+++ b/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java
@@ -1104,6 +1104,7 @@ public enum Stat {
SITE_MONITORING_SCREEN_SHOWN,
OPENED_SITE_MONITORING,
SITE_MONITORING_TAB_SHOWN,
+ SITE_MONITORING_TAB_LOADING_ERROR
}
private static final List TRACKERS = new ArrayList<>();
diff --git a/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTrackerNosara.java b/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTrackerNosara.java
index 6bc51ec5bdbf..5e5fa351b389 100644
--- a/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTrackerNosara.java
+++ b/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTrackerNosara.java
@@ -2701,6 +2701,8 @@ public static String getEventNameForStat(AnalyticsTracker.Stat stat) {
return "opened_site_monitoring";
case SITE_MONITORING_TAB_SHOWN:
return "site_monitoring_tab_shown";
+ case SITE_MONITORING_TAB_LOADING_ERROR:
+ return "site_monitoring_tab_loading_error";
}
return null;
}