From 964fd10b6de5c0c3cd6d12484235fe190a0ab323 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Tue, 30 Jan 2024 15:02:11 +0530 Subject: [PATCH 01/32] + Adds: model to loaded state --- .../wordpress/android/ui/sitemonitor/SiteMonitorUiState.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorUiState.kt b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorUiState.kt index 50bca3c0dec8..7bdbae624d3f 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorUiState.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorUiState.kt @@ -10,7 +10,9 @@ sealed class SiteMonitorUiState { val model: SiteMonitorModel ) : SiteMonitorUiState() - object Loaded : SiteMonitorUiState() + data class Loaded( + val model: SiteMonitorModel + ) : SiteMonitorUiState() open class Error( val title: UiString, From ae9ca81a7cf54cf9676a060a2c29d70eb4252993 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Tue, 30 Jan 2024 15:02:37 +0530 Subject: [PATCH 02/32] =?UTF-8?q?=E2=86=91=20Updates:=20the=20webview=20cl?= =?UTF-8?q?ient=20to=20use=20tab=20type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/sitemonitor/SiteMonitorWebViewClient.kt | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) 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..9938522b782d 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 @@ -1,19 +1,22 @@ package org.wordpress.android.ui.sitemonitor import android.graphics.Bitmap +import android.util.Log import android.webkit.WebResourceError import android.webkit.WebResourceRequest 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 @@ -26,9 +29,10 @@ class SiteMonitorWebViewClient( } override fun onPageFinished(view: WebView, url: String?) { + Log.e("Site Monitor Webview client", "onPageFinished: $url") super.onPageFinished(view, url) if (!errorReceived) { - url?.let { listener.onWebViewPageLoaded(it) } + url?.let { listener.onWebViewPageLoaded(it, tabType) } } } @@ -40,7 +44,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) } } } From 236b45044cdb36f583b6959501f6e36e8fc4e711 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Tue, 30 Jan 2024 15:04:11 +0530 Subject: [PATCH 03/32] + Adds: SiteMonitorParentViewModel --- .../sitemonitor/SiteMonitorParentViewModel.kt | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorParentViewModel.kt 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..b430397116d6 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorParentViewModel.kt @@ -0,0 +1,91 @@ +package org.wordpress.android.ui.sitemonitor + +import androidx.compose.runtime.MutableState +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.CoroutineDispatcher +import org.wordpress.android.analytics.AnalyticsTracker +import org.wordpress.android.fluxc.model.SiteModel +import org.wordpress.android.modules.BG_THREAD +import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper +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 analyticsTrackerWrapper: AnalyticsTrackerWrapper, + 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 + trackActivityLaunched() + 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): MutableState { + return when (siteMonitorType) { + SiteMonitorType.METRICS -> { + metricsViewModel.uiState + } + SiteMonitorType.PHP_LOGS -> { + phpLogViewModel.uiState + } + SiteMonitorType.WEB_SERVER_LOGS -> { + webServerViewModel.uiState + } + } + } + + fun onUrlLoaded(siteMonitorType: SiteMonitorType, url: String) { + when (siteMonitorType) { + SiteMonitorType.METRICS -> { + metricsViewModel.onUrlLoaded(url) + } + SiteMonitorType.PHP_LOGS -> { + phpLogViewModel.onUrlLoaded(url) + } + SiteMonitorType.WEB_SERVER_LOGS -> { + webServerViewModel.onUrlLoaded(url) + } + } + } + + fun onWebViewError(siteMonitorType: SiteMonitorType) { + when (siteMonitorType) { + SiteMonitorType.METRICS -> { + metricsViewModel.onWebViewError() + } + SiteMonitorType.PHP_LOGS -> { + phpLogViewModel.onWebViewError() + } + SiteMonitorType.WEB_SERVER_LOGS -> { + webServerViewModel.onWebViewError() + } + } + } + + private fun trackActivityLaunched() { + analyticsTrackerWrapper.track(AnalyticsTracker.Stat.SITE_MONITORING_SCREEN_SHOWN) + } + + override fun onCleared() { + super.onCleared() + metricsViewModel.onCleared() + phpLogViewModel.onCleared() + webServerViewModel.onCleared() + } +} From 586e3ed7207b8fc391445a452cc32db405a0f2a7 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Tue, 30 Jan 2024 15:04:33 +0530 Subject: [PATCH 04/32] =?UTF-8?q?=E2=86=91=20Updates:=20the=20logic=20in?= =?UTF-8?q?=20activity=20to=20use=20tabs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sitemonitor/SiteMonitorParentActivity.kt | 205 +++++++++++++----- 1 file changed, 148 insertions(+), 57 deletions(-) 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 c2118117baf2..020bd6f5d857 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 @@ -2,38 +2,56 @@ package org.wordpress.android.ui.sitemonitor import android.annotation.SuppressLint import android.os.Bundle +import android.util.Log 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.material.Scaffold +import androidx.compose.material.Button +import androidx.compose.material.MaterialTheme +import androidx.compose.material.TabRow +import androidx.compose.material.Text import androidx.compose.material3.Surface +import androidx.compose.material3.Tab 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.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.compose.components.MainTopAppBar -import org.wordpress.android.ui.compose.components.NavigationIcons +import org.wordpress.android.ui.WPWebViewActivity import org.wordpress.android.ui.compose.theme.AppTheme +import org.wordpress.android.ui.compose.utils.uiStringText import org.wordpress.android.util.extensions.getSerializableExtraCompat @AndroidEntryPoint -class SiteMonitorParentActivity: AppCompatActivity() { +class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient.SiteMonitorWebViewClientListener { private var savedStateSparseArray = SparseArray() private var currentSelectItemId = 0 + private val siteMonitorParentViewModel: SiteMonitorParentViewModel by viewModels() + @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -43,6 +61,8 @@ class SiteMonitorParentActivity: AppCompatActivity() { ) ?: savedStateSparseArray currentSelectItemId = savedInstanceState.getInt(SAVED_STATE_CURRENT_TAB_KEY) + } else { + siteMonitorParentViewModel.start(getSite()) } setContent { AppTheme { @@ -78,66 +98,137 @@ class SiteMonitorParentActivity: AppCompatActivity() { @Composable @SuppressLint("UnusedMaterialScaffoldPaddingParameter") - fun SiteMonitorScreen() { - var selectedTab by rememberSaveable { mutableStateOf(SiteMonitorTabItem.Metrics.route) } - Scaffold( - topBar = { - MainTopAppBar( - title = stringResource(id = R.string.site_monitoring), - navigationIcon = NavigationIcons.BackIcon, - onNavigationIconClick = onBackPressedDispatcher::onBackPressed, - ) - } - ) { padding -> - Column(modifier = Modifier.padding(padding)) { - SiteMonitorTabHeader { clickTab -> - selectedTab = clickTab - } - SiteMonitorTabNavigation(selectedTab) { selectedTab -> - val item = enumValues().find { - it.route == selectedTab - } ?: initialItem(getInitialTab()) - - SiteMonitorFragmentContainer( - modifier = Modifier.fillMaxSize(), - commit = getCommitFunction( - SiteMonitorTabFragment.newInstance(item.urlTemplate, item.siteMonitorType, getSite()), - item.route - ) + fun SiteMonitorScreen(modifier: Modifier = Modifier) { + var tabIndex by remember { mutableStateOf(0) } + + val tabs = listOf( + R.string.site_monitoring_tab_title_metrics, + R.string.site_monitoring_tab_title_php_logs, + R.string.site_monitoring_tab_title_web_server_logs + ) + + Column(modifier = modifier.fillMaxWidth()) { + TabRow( + selectedTabIndex = tabIndex, + backgroundColor = MaterialTheme.colors.surface, + contentColor = MaterialTheme.colors.onSurface, + ) { + tabs.forEachIndexed { index, title -> + Tab(text = { Text(stringResource(id = title)) }, + selected = tabIndex == index, + onClick = { tabIndex = index } ) } } + when (tabIndex) { + 0 -> SiteMonitorTabContent(SiteMonitorType.METRICS, modifier) + 1 -> SiteMonitorTabContent(SiteMonitorType.PHP_LOGS, modifier) + 2 -> SiteMonitorTabContent(SiteMonitorType.WEB_SERVER_LOGS, modifier) + } } } - private fun initialItem(type: SiteMonitorType): SiteMonitorTabItem { - return enumValues().find { - it.siteMonitorType == type - } ?: SiteMonitorTabItem.Metrics + @Composable + private fun SiteMonitorTabContent(tabType: SiteMonitorType, modifier: Modifier) { + val uiState by remember(key1 = tabType) { + siteMonitorParentViewModel.getUiState(tabType) + } + 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) + } } - 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 - ) { - val currentFragment = supportFragmentManager.findFragmentById(currentSelectItemId) - if (currentFragment != null) { - savedStateSparseArray.put( - currentSelectItemId, - supportFragmentManager.saveFragmentInstanceState(currentFragment) - ) + @SuppressLint("SetJavaScriptEnabled") + @Composable + private fun SiteMonitorWebView(uiState: SiteMonitorUiState, tabType: SiteMonitorType, modifier: Modifier) { + Log.e("Track", "SiteMonitorWebView $uiState") + var webView: WebView? by remember { mutableStateOf(null) } + + Log.e("Track", "SiteMonitorWebView $webView") + + val model = when (uiState) { + is SiteMonitorUiState.Prepared -> uiState.model + is SiteMonitorUiState.Loaded -> uiState.model + else -> null + } + + model?.let { + LaunchedEffect(true) { + webView = WebView(this@SiteMonitorParentActivity).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@SiteMonitorParentActivity, tabType) + } + } + if(uiState is SiteMonitorUiState.Prepared) { + webView?.postUrl(WPWebViewActivity.WPCOM_LOGIN_URL, model.addressToLoad.toByteArray()) + } else { + webView?.loadUrl(model.url) + } + } + + 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.fillMaxSize() + ) + } + } } - currentSelectItemId = tabId - fragment.setInitialSavedState(savedStateSparseArray[currentSelectItemId]) } + + override fun onWebViewPageLoaded(url: String, tabType: SiteMonitorType) = + siteMonitorParentViewModel.onUrlLoaded(tabType, url) + + override fun onWebViewReceivedError(url: String, tabType: SiteMonitorType) = + siteMonitorParentViewModel.onWebViewError(tabType) } From 84d25bfd665432c13cdd87bcb060c6feaefcb229 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Tue, 30 Jan 2024 15:05:20 +0530 Subject: [PATCH 05/32] =?UTF-8?q?=E2=86=91=20Updates:=20the=20site=20monit?= =?UTF-8?q?or=20tab=20view=20model=20to=20use=20state=20flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/sitemonitor/SiteMonitorTabViewModel.kt | 50 +++++++++++-------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModel.kt index aaae3507f424..aebbb08c9423 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModel.kt @@ -2,34 +2,34 @@ package org.wordpress.android.ui.sitemonitor import android.text.TextUtils import android.util.Log -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow +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 = _uiState + + fun initialize(scope: CoroutineScope) { + this.scope = scope + } fun start(type: SiteMonitorType, urlTemplate: String, site: SiteModel) { Log.i("Track", "TheViewModel start with $urlTemplate and $type") @@ -52,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 @@ -100,18 +100,28 @@ class SiteMonitorTabViewModel @Inject constructor( } private fun postUiState(state: SiteMonitorUiState) { - launch { + scope.launch { _uiState.value = state } } - fun onUrlLoaded() { + fun onUrlLoaded(url: String) { + Log.i("Track", "TheViewModel onUrlLoaded $url") siteMonitorUtils.trackTabLoaded(siteMonitorType) - postUiState(SiteMonitorUiState.Loaded) + if (uiState.value is SiteMonitorUiState.Prepared){ + postUiState(SiteMonitorUiState + .Loaded((_uiState.value as SiteMonitorUiState.Prepared).model.copy(url = url))) + } } + fun onWebViewError() { - postUiState(mapper.toGenericError(this@SiteMonitorTabViewModel::loadView)) + postUiState(mapper.toGenericError(::loadView)) + } + + fun onCleared() { + Log.i("Track", "TheViewModel onCleared") + scope.cancel() } companion object { From d73c94216d5da235b5e7e4bb548ccdfbfae3db26 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Tue, 30 Jan 2024 15:05:59 +0530 Subject: [PATCH 06/32] =?UTF-8?q?=E2=86=91=20Updates:=20the=20fragment=20t?= =?UTF-8?q?o=20use=20loaded=20state?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/sitemonitor/SiteMonitorTabFragment.kt | 57 ++++++++++++------- 1 file changed, 36 insertions(+), 21 deletions(-) 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 index 8fb61cc8ed03..841612550ae8 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabFragment.kt @@ -2,6 +2,7 @@ package org.wordpress.android.ui.sitemonitor import android.annotation.SuppressLint import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -17,7 +18,6 @@ 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 @@ -29,7 +29,7 @@ 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 androidx.fragment.app.activityViewModels import dagger.hilt.android.AndroidEntryPoint import org.wordpress.android.WordPress import org.wordpress.android.fluxc.model.SiteModel @@ -38,6 +38,11 @@ import org.wordpress.android.ui.compose.utils.uiStringText @AndroidEntryPoint class SiteMonitorTabFragment : Fragment(), SiteMonitorWebViewClient.SiteMonitorWebViewClientListener { + val tabType by lazy { getSiteMonitorType() } + + private val siteMonitorWebViewClient = SiteMonitorWebViewClient(this@SiteMonitorTabFragment, SiteMonitorType.PHP_LOGS) + + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -47,20 +52,14 @@ class SiteMonitorTabFragment : Fragment(), SiteMonitorWebViewClient.SiteMonitorW } } - private val viewModel: SiteMonitorTabViewModel by viewModels() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - initViewModel(getSiteMonitorType(), getUrlTemplate(), getSite()) + override fun onResume() { + super.onResume() } - @Suppress("DEPRECATION") - private fun getSite(): SiteModel { - return requireNotNull(arguments?.getSerializable(WordPress.SITE)) as SiteModel - } + private val viewModel: SiteMonitorParentViewModel by activityViewModels() - private fun getUrlTemplate(): String { - return requireNotNull(arguments?.getString(KEY_URL_TEMPLATE)) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) } @Suppress("DEPRECATION") @@ -68,13 +67,11 @@ class SiteMonitorTabFragment : Fragment(), SiteMonitorWebViewClient.SiteMonitorW 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 onWebViewPageLoaded(url: String, tabType: SiteMonitorType) = + viewModel.onUrlLoaded(tabType, url) - override fun onWebViewReceivedError(url: String) = viewModel.onWebViewError() + override fun onWebViewReceivedError(url: String, tabType: SiteMonitorType) = + viewModel.onWebViewError(tabType) companion object { const val KEY_URL_TEMPLATE = "KEY_URL" @@ -92,7 +89,7 @@ class SiteMonitorTabFragment : Fragment(), SiteMonitorWebViewClient.SiteMonitorW @Composable private fun SiteMonitorTabContent() { - val uiState by viewModel.uiState.collectAsState() + val uiState by viewModel.getUiState(tabType) when (uiState) { is SiteMonitorUiState.Preparing -> LoadingState() is SiteMonitorUiState.Prepared, is SiteMonitorUiState.Loaded -> SiteMonitorWebView(uiState) @@ -135,8 +132,11 @@ class SiteMonitorTabFragment : Fragment(), SiteMonitorWebViewClient.SiteMonitorW @SuppressLint("SetJavaScriptEnabled") @Composable private fun SiteMonitorWebView(uiState: SiteMonitorUiState) { + Log.e("Track", "SiteMonitorWebView $uiState") var webView: WebView? by remember { mutableStateOf(null) } + Log.e("Track", "SiteMonitorWebView $webView") + if (uiState is SiteMonitorUiState.Prepared) { val model = uiState.model LaunchedEffect(true) { @@ -149,10 +149,25 @@ class SiteMonitorTabFragment : Fragment(), SiteMonitorWebViewClient.SiteMonitorW settings.userAgentString = model.userAgent settings.javaScriptEnabled = true settings.domStorageEnabled = true - webViewClient = SiteMonitorWebViewClient(this@SiteMonitorTabFragment) + webViewClient = SiteMonitorWebViewClient(this@SiteMonitorTabFragment, SiteMonitorType.PHP_LOGS) postUrl(WPWebViewActivity.WPCOM_LOGIN_URL, model.addressToLoad.toByteArray()) } } + } else if (uiState is SiteMonitorUiState.Loaded) { + 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 + loadUrl(model.url) + } + } } Box( From 47471c790678669f41ca120da27f372417edb844 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Wed, 31 Jan 2024 11:46:29 +0530 Subject: [PATCH 07/32] =?UTF-8?q?=E2=86=91=20Updates:=20the=20logic=20to?= =?UTF-8?q?=20use=20three=20web=20views=20for=20each=20tab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sitemonitor/SiteMonitorParentActivity.kt | 86 ++++++++++++------- 1 file changed, 53 insertions(+), 33 deletions(-) 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 020bd6f5d857..52915c9ec16d 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,8 +1,8 @@ package org.wordpress.android.ui.sitemonitor import android.annotation.SuppressLint +import android.os.Build import android.os.Bundle -import android.util.Log import android.util.SparseArray import android.view.View import android.view.ViewGroup @@ -24,7 +24,6 @@ import androidx.compose.material.Text import androidx.compose.material3.Surface import androidx.compose.material3.Tab import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -44,7 +43,9 @@ import org.wordpress.android.ui.WPWebViewActivity 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(), SiteMonitorWebViewClient.SiteMonitorWebViewClientListener { private var savedStateSparseArray = SparseArray() @@ -52,6 +53,39 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. private val siteMonitorParentViewModel: SiteMonitorParentViewModel by viewModels() + @Inject + lateinit var siteMonitorUtils: SiteMonitorUtils + + 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) @@ -176,36 +210,15 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. @SuppressLint("SetJavaScriptEnabled") @Composable private fun SiteMonitorWebView(uiState: SiteMonitorUiState, tabType: SiteMonitorType, modifier: Modifier) { - Log.e("Track", "SiteMonitorWebView $uiState") - var webView: WebView? by remember { mutableStateOf(null) } - - Log.e("Track", "SiteMonitorWebView $webView") - - val model = when (uiState) { - is SiteMonitorUiState.Prepared -> uiState.model - is SiteMonitorUiState.Loaded -> uiState.model - else -> null + // retrieve the webview from the actvity + var webView = when (tabType) { + SiteMonitorType.METRICS -> metricsWebView + SiteMonitorType.PHP_LOGS -> phpLogsWebView + SiteMonitorType.WEB_SERVER_LOGS -> webServerLogsWebView } - model?.let { - LaunchedEffect(true) { - webView = WebView(this@SiteMonitorParentActivity).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@SiteMonitorParentActivity, tabType) - } - } - if(uiState is SiteMonitorUiState.Prepared) { - webView?.postUrl(WPWebViewActivity.WPCOM_LOGIN_URL, model.addressToLoad.toByteArray()) - } else { - webView?.loadUrl(model.url) - } + if (uiState is SiteMonitorUiState.Prepared) { + webView.postUrl(WPWebViewActivity.WPCOM_LOGIN_URL, uiState.model.addressToLoad.toByteArray()) } Box( @@ -213,19 +226,26 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. contentAlignment = Alignment.Center ) { if (uiState is SiteMonitorUiState.Prepared) { - LoadingState() + LoadingState(modifier) } else { - webView?.let { theWebView -> + webView.let { theWebView -> AndroidView( factory = { theWebView }, update = { webView = it }, - modifier = Modifier.fillMaxSize() + modifier = modifier.fillMaxSize() ) } } } } + override fun onDestroy() { + super.onDestroy() + metricsWebView.destroy() + phpLogsWebView.destroy() + webServerLogsWebView.destroy() + } + override fun onWebViewPageLoaded(url: String, tabType: SiteMonitorType) = siteMonitorParentViewModel.onUrlLoaded(tabType, url) From 5ada1fadf841432b1636865a89425faa900f4063 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Wed, 31 Jan 2024 12:14:00 +0530 Subject: [PATCH 08/32] + Adds: top bar view to the activity --- .../sitemonitor/SiteMonitorParentActivity.kt | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) 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 52915c9ec16d..417393c5ddfc 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 @@ -19,6 +19,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.Button import androidx.compose.material.MaterialTheme +import androidx.compose.material.Scaffold import androidx.compose.material.TabRow import androidx.compose.material.Text import androidx.compose.material3.Surface @@ -40,6 +41,8 @@ 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 @@ -103,7 +106,7 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. Surface( modifier = Modifier.fillMaxSize(), ) { - SiteMonitorScreen() + SiteMonitorTabScreen() } } } @@ -130,6 +133,23 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. const val SAVED_STATE_CURRENT_TAB_KEY = "CurrentTabKey" } + @Composable + @SuppressLint("UnusedMaterialScaffoldPaddingParameter") + fun SiteMonitorTabScreen(modifier: Modifier = Modifier) { + Scaffold( + topBar = { + MainTopAppBar( + title = stringResource(id = R.string.site_monitoring), + navigationIcon = NavigationIcons.BackIcon, + onNavigationIconClick = onBackPressedDispatcher::onBackPressed, + ) + }, + content = { + SiteMonitorScreen(modifier = modifier) + } + ) + } + @Composable @SuppressLint("UnusedMaterialScaffoldPaddingParameter") fun SiteMonitorScreen(modifier: Modifier = Modifier) { From 554b42dcb8164993b09bba7caa80a7e389d65990 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Wed, 31 Jan 2024 13:02:50 +0530 Subject: [PATCH 09/32] + Adds: the logic to handle initial tab logic --- .../sitemonitor/SiteMonitorParentActivity.kt | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) 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 399905aad493..15a1cea8727c 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 @@ -103,13 +103,14 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. currentSelectItemId = savedInstanceState.getInt(SAVED_STATE_CURRENT_TAB_KEY) } else { siteMonitorParentViewModel.start(getSite()) + currentSelectItemId = getInitialTab() } setContent { AppTheme { Surface( modifier = Modifier.fillMaxSize(), ) { - SiteMonitorScreen() + SiteMonitorScreen(initialTab = currentSelectItemId) } } } @@ -125,9 +126,14 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. 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 { @@ -138,7 +144,7 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. @Composable @SuppressLint("UnusedMaterialScaffoldPaddingParameter") - fun SiteMonitorTabScreen(modifier: Modifier = Modifier) { + fun SiteMonitorScreen(initialTab: Int, modifier: Modifier = Modifier) { Scaffold( topBar = { MainTopAppBar( @@ -148,15 +154,15 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. ) }, content = { - SiteMonitorScreen(modifier = modifier) + SiteMonitorTab(initialTab, modifier = modifier) } ) } @Composable @SuppressLint("UnusedMaterialScaffoldPaddingParameter") - fun SiteMonitorScreen(modifier: Modifier = Modifier) { - var tabIndex by remember { mutableStateOf(0) } + fun SiteMonitorTab(initialTab: Int, modifier: Modifier = Modifier) { + var tabIndex by remember { mutableStateOf(initialTab) } val tabs = listOf( R.string.site_monitoring_tab_title_metrics, @@ -173,7 +179,9 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. tabs.forEachIndexed { index, title -> Tab(text = { Text(stringResource(id = title)) }, selected = tabIndex == index, - onClick = { tabIndex = index } + onClick = { + tabIndex = index + } ) } } From 54442404fc1d96da673ccef60c8e9f854fec2c3f Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Wed, 31 Jan 2024 13:03:41 +0530 Subject: [PATCH 10/32] * Fixes: the incorrect passing of modifier and formatting --- .../sitemonitor/SiteMonitorParentActivity.kt | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) 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 15a1cea8727c..ad02e584aa8b 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 @@ -60,15 +60,15 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. private val siteMonitorParentViewModel: SiteMonitorParentViewModel by viewModels() private val metricsWebView by lazy { - commonWebView(SiteMonitorType.METRICS) + commonWebView(SiteMonitorType.METRICS) } private val phpLogsWebView by lazy { - commonWebView(SiteMonitorType.PHP_LOGS) + commonWebView(SiteMonitorType.PHP_LOGS) } private val webServerLogsWebView by lazy { - commonWebView(SiteMonitorType.WEB_SERVER_LOGS) + commonWebView(SiteMonitorType.WEB_SERVER_LOGS) } private fun commonWebView( @@ -85,7 +85,7 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. 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 + settings.isAlgorithmicDarkeningAllowed = true } } @@ -240,7 +240,11 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. @SuppressLint("SetJavaScriptEnabled") @Composable - private fun SiteMonitorWebView(uiState: SiteMonitorUiState, tabType: SiteMonitorType, modifier: Modifier) { + private fun SiteMonitorWebView( + uiState: SiteMonitorUiState, + tabType: SiteMonitorType, + modifier: Modifier = Modifier + ) { // retrieve the webview from the actvity var webView = when (tabType) { SiteMonitorType.METRICS -> metricsWebView @@ -257,13 +261,13 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. contentAlignment = Alignment.Center ) { if (uiState is SiteMonitorUiState.Prepared) { - LoadingState(modifier) + LoadingState() } else { webView.let { theWebView -> AndroidView( factory = { theWebView }, update = { webView = it }, - modifier = modifier.fillMaxSize() + modifier = Modifier.fillMaxWidth() ) } } From 32877e77c1f8a088965fe0646f11df70d0c291ae Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Wed, 31 Jan 2024 13:04:58 +0530 Subject: [PATCH 11/32] * Fixes: Incorrect passing of modifier instance --- .../android/ui/sitemonitor/SiteMonitorParentActivity.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 ad02e584aa8b..4659db0bd29a 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 @@ -186,15 +186,15 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. } } when (tabIndex) { - 0 -> SiteMonitorTabContent(SiteMonitorType.METRICS, modifier) - 1 -> SiteMonitorTabContent(SiteMonitorType.PHP_LOGS, modifier) - 2 -> SiteMonitorTabContent(SiteMonitorType.WEB_SERVER_LOGS, modifier) + 0 -> SiteMonitorTabContent(SiteMonitorType.METRICS) + 1 -> SiteMonitorTabContent(SiteMonitorType.PHP_LOGS) + 2 -> SiteMonitorTabContent(SiteMonitorType.WEB_SERVER_LOGS) } } } @Composable - private fun SiteMonitorTabContent(tabType: SiteMonitorType, modifier: Modifier) { + private fun SiteMonitorTabContent(tabType: SiteMonitorType, modifier: Modifier = Modifier) { val uiState by remember(key1 = tabType) { siteMonitorParentViewModel.getUiState(tabType) } From d532763873f3d4ebef2d3af8d82689570ef538aa Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Wed, 31 Jan 2024 13:19:07 +0530 Subject: [PATCH 12/32] * Fixes: Incorrect tab tracking and activity tracking --- .../android/ui/sitemonitor/SiteMonitorParentActivity.kt | 3 +-- .../android/ui/sitemonitor/SiteMonitorTabViewModel.kt | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) 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 4659db0bd29a..d52f5d0e1608 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 @@ -93,8 +93,6 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - siteMonitorUtils.trackActivityLaunched() - if (savedInstanceState != null) { savedStateSparseArray = savedInstanceState.getSparseParcelableArray( SAVED_STATE_CONTAINER_KEY @@ -180,6 +178,7 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. Tab(text = { Text(stringResource(id = title)) }, selected = tabIndex == index, onClick = { + siteMonitorUtils.trackTabLoaded(SiteMonitorType.entries[index]) tabIndex = index } ) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModel.kt index 90a47486ed93..8e4e681efcf1 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModel.kt @@ -105,7 +105,6 @@ class SiteMonitorTabViewModelSlice @Inject constructor( } fun onUrlLoaded(url: String) { - siteMonitorUtils.trackTabLoaded(siteMonitorType) if (uiState.value is SiteMonitorUiState.Prepared){ postUiState(SiteMonitorUiState .Loaded((_uiState.value as SiteMonitorUiState.Prepared).model.copy(url = url))) From 89f98957bdabf9de635a1a361034b53acebf374d Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Wed, 31 Jan 2024 14:23:06 +0530 Subject: [PATCH 13/32] - Removes: Redundant logs from SiteMonitorWebViewClient --- .../android/ui/sitemonitor/SiteMonitorWebViewClient.kt | 2 -- 1 file changed, 2 deletions(-) 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 9938522b782d..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 @@ -1,7 +1,6 @@ package org.wordpress.android.ui.sitemonitor import android.graphics.Bitmap -import android.util.Log import android.webkit.WebResourceError import android.webkit.WebResourceRequest import android.webkit.WebView @@ -29,7 +28,6 @@ class SiteMonitorWebViewClient( } override fun onPageFinished(view: WebView, url: String?) { - Log.e("Site Monitor Webview client", "onPageFinished: $url") super.onPageFinished(view, url) if (!errorReceived) { url?.let { listener.onWebViewPageLoaded(it, tabType) } From 7bf963ae8ec51e0974e63c0e8c4db0a941d8d8b2 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Wed, 31 Jan 2024 14:35:54 +0530 Subject: [PATCH 14/32] =?UTF-8?q?=E2=86=92=20Moves:=20Site=20Monitor=20Tab?= =?UTF-8?q?=20header=20logic=20to=20activity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sitemonitor/SiteMonitorParentActivity.kt | 44 ++++++++----- .../ui/sitemonitor/SiteMonitorTabHeader.kt | 61 ------------------- 2 files changed, 30 insertions(+), 75 deletions(-) delete mode 100644 WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabHeader.kt 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 d52f5d0e1608..ebab29ae5870 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 @@ -20,10 +20,11 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material.Button import androidx.compose.material.MaterialTheme import androidx.compose.material.Scaffold -import androidx.compose.material.TabRow import androidx.compose.material.Text import androidx.compose.material3.Surface import androidx.compose.material3.Tab +import androidx.compose.material3.TabRowDefaults +import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -33,6 +34,7 @@ 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 @@ -152,35 +154,48 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. ) }, content = { - SiteMonitorTab(initialTab, modifier = modifier) + SiteMonitorHeader(initialTab, modifier = modifier) } ) } @Composable @SuppressLint("UnusedMaterialScaffoldPaddingParameter") - fun SiteMonitorTab(initialTab: Int, modifier: Modifier = Modifier) { + fun SiteMonitorHeader(initialTab: Int, modifier: Modifier = Modifier) { var tabIndex by remember { mutableStateOf(initialTab) } - val tabs = listOf( - R.string.site_monitoring_tab_title_metrics, - R.string.site_monitoring_tab_title_php_logs, - R.string.site_monitoring_tab_title_web_server_logs - ) + val tabs = SiteMonitorTabItem.entries Column(modifier = modifier.fillMaxWidth()) { - TabRow( + androidx.compose.material3.TabRow( selectedTabIndex = tabIndex, - backgroundColor = MaterialTheme.colors.surface, + 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 + ) + } ) { - tabs.forEachIndexed { index, title -> - Tab(text = { Text(stringResource(id = title)) }, + tabs.forEachIndexed { index, item -> + Tab( + text = { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + androidx.compose.material3.Text( + text = stringResource(item.title), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + }, selected = tabIndex == index, onClick = { - siteMonitorUtils.trackTabLoaded(SiteMonitorType.entries[index]) + siteMonitorUtils.trackTabLoaded(tabs[index].siteMonitorType) tabIndex = index - } + }, ) } } @@ -201,6 +216,7 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. is SiteMonitorUiState.Preparing -> LoadingState(modifier) is SiteMonitorUiState.Prepared, is SiteMonitorUiState.Loaded -> SiteMonitorWebView(uiState, tabType, modifier) + is SiteMonitorUiState.Error -> SiteMonitorError(uiState as SiteMonitorUiState.Error, modifier) } } 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) - }, - ) - } - } -} From dc1a6ba99f50c6634ed62fe80aad09275a311de5 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Wed, 31 Jan 2024 14:39:03 +0530 Subject: [PATCH 15/32] - Removes: Site Monitor Fragment and Fragment container --- .../SiteMonitorFragmentContainer.kt | 76 ------- .../sitemonitor/SiteMonitorParentActivity.kt | 10 + .../ui/sitemonitor/SiteMonitorTabFragment.kt | 199 ------------------ 3 files changed, 10 insertions(+), 275 deletions(-) delete mode 100644 WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorFragmentContainer.kt delete mode 100644 WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabFragment.kt 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 ebab29ae5870..4307590192b4 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 @@ -21,6 +21,7 @@ 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.TabRowDefaults @@ -220,6 +221,15 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. is SiteMonitorUiState.Error -> SiteMonitorError(uiState as SiteMonitorUiState.Error, modifier) } } + @Composable + fun LoadingState(modifier: Modifier = Modifier) { + Box( + contentAlignment = Alignment.Center, + modifier = modifier.fillMaxSize() + ) { + CircularProgressIndicator() + } + } @Composable fun SiteMonitorError(error: SiteMonitorUiState.Error, modifier: Modifier = Modifier) { 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 841612550ae8..000000000000 --- a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabFragment.kt +++ /dev/null @@ -1,199 +0,0 @@ -package org.wordpress.android.ui.sitemonitor - -import android.annotation.SuppressLint -import android.os.Bundle -import android.util.Log -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.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.activityViewModels -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 { - val tabType by lazy { getSiteMonitorType() } - - private val siteMonitorWebViewClient = SiteMonitorWebViewClient(this@SiteMonitorTabFragment, SiteMonitorType.PHP_LOGS) - - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View = ComposeView(requireContext()).apply { - setContent { - SiteMonitorTabContent() - } - } - - override fun onResume() { - super.onResume() - } - - private val viewModel: SiteMonitorParentViewModel by activityViewModels() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - } - - @Suppress("DEPRECATION") - private fun getSiteMonitorType(): SiteMonitorType { - return requireNotNull(arguments?.getSerializable(KEY_SITE_MONITOR_TYPE)) as SiteMonitorType - } - - override fun onWebViewPageLoaded(url: String, tabType: SiteMonitorType) = - viewModel.onUrlLoaded(tabType, url) - - override fun onWebViewReceivedError(url: String, tabType: SiteMonitorType) = - viewModel.onWebViewError(tabType) - - 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.getUiState(tabType) - 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) { - Log.e("Track", "SiteMonitorWebView $uiState") - var webView: WebView? by remember { mutableStateOf(null) } - - Log.e("Track", "SiteMonitorWebView $webView") - - 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, SiteMonitorType.PHP_LOGS) - postUrl(WPWebViewActivity.WPCOM_LOGIN_URL, model.addressToLoad.toByteArray()) - } - } - } else if (uiState is SiteMonitorUiState.Loaded) { - 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 - loadUrl(model.url) - } - } - } - - 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() - } -} From 8a6984acafa847575735a1ef6d40781253ae562f Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Wed, 31 Jan 2024 14:39:27 +0530 Subject: [PATCH 16/32] - Removes: Redundant SiteMonitorTabNavigation.kt --- .../sitemonitor/SiteMonitorTabNavigation.kt | 20 ------------------- 1 file changed, 20 deletions(-) delete mode 100644 WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabNavigation.kt 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) - } - } -} From adf41776d7531d7944c5621c024b86813065d26b Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Wed, 31 Jan 2024 14:46:35 +0530 Subject: [PATCH 17/32] * Reverts: the change for loaded state object as its no longer needed --- .../android/ui/sitemonitor/SiteMonitorParentActivity.kt | 2 +- .../android/ui/sitemonitor/SiteMonitorParentViewModel.kt | 8 ++++---- .../android/ui/sitemonitor/SiteMonitorTabViewModel.kt | 5 ++--- .../android/ui/sitemonitor/SiteMonitorUiState.kt | 4 +--- 4 files changed, 8 insertions(+), 11 deletions(-) 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 4307590192b4..7a36e9a19334 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 @@ -307,7 +307,7 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. } override fun onWebViewPageLoaded(url: String, tabType: SiteMonitorType) = - siteMonitorParentViewModel.onUrlLoaded(tabType, url) + siteMonitorParentViewModel.onUrlLoaded(tabType) override fun onWebViewReceivedError(url: String, tabType: SiteMonitorType) = siteMonitorParentViewModel.onWebViewError(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 index b430397116d6..2e0e9ad03813 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorParentViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorParentViewModel.kt @@ -50,16 +50,16 @@ class SiteMonitorParentViewModel @Inject constructor( } } - fun onUrlLoaded(siteMonitorType: SiteMonitorType, url: String) { + fun onUrlLoaded(siteMonitorType: SiteMonitorType) { when (siteMonitorType) { SiteMonitorType.METRICS -> { - metricsViewModel.onUrlLoaded(url) + metricsViewModel.onUrlLoaded() } SiteMonitorType.PHP_LOGS -> { - phpLogViewModel.onUrlLoaded(url) + phpLogViewModel.onUrlLoaded() } SiteMonitorType.WEB_SERVER_LOGS -> { - webServerViewModel.onUrlLoaded(url) + webServerViewModel.onUrlLoaded() } } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModel.kt index 8e4e681efcf1..fab664186e62 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModel.kt @@ -104,10 +104,9 @@ class SiteMonitorTabViewModelSlice @Inject constructor( } } - fun onUrlLoaded(url: String) { + fun onUrlLoaded() { if (uiState.value is SiteMonitorUiState.Prepared){ - postUiState(SiteMonitorUiState - .Loaded((_uiState.value as SiteMonitorUiState.Prepared).model.copy(url = url))) + postUiState(SiteMonitorUiState.Loaded) } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorUiState.kt b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorUiState.kt index dfeb23933876..8b7dee5a31cc 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorUiState.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorUiState.kt @@ -10,9 +10,7 @@ sealed class SiteMonitorUiState { val model: SiteMonitorModel ) : SiteMonitorUiState() - data class Loaded( - val model: SiteMonitorModel - ) : SiteMonitorUiState() + object Loaded : SiteMonitorUiState() open class Error( val title: UiString, From da6711c088938ee7e4a405f49feee17bbc015096 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Wed, 31 Jan 2024 15:24:13 +0530 Subject: [PATCH 18/32] * Renames: SiteMonitorTabViewModel.kt to SiteMonitorTabViewModelSlice --- ...SiteMonitorTabViewModel.kt => SiteMonitorTabViewModelSlice.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/{SiteMonitorTabViewModel.kt => SiteMonitorTabViewModelSlice.kt} (100%) 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 100% 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 From f6633cf9d849a39afd0328dadcb88138504cae3b Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Wed, 31 Jan 2024 16:20:42 +0530 Subject: [PATCH 19/32] =?UTF-8?q?=E2=86=91=20Updates:=20the=20state=20retu?= =?UTF-8?q?rned=20from=20mutable=20to=20immutable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/ui/sitemonitor/SiteMonitorParentViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 2e0e9ad03813..98a6add7e83b 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorParentViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorParentViewModel.kt @@ -1,6 +1,6 @@ package org.wordpress.android.ui.sitemonitor -import androidx.compose.runtime.MutableState +import androidx.compose.runtime.State import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher From cfb913251bf44b8d93720d977e6105f08f544064 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Wed, 31 Jan 2024 16:21:06 +0530 Subject: [PATCH 20/32] * Fixes: data not loaded in orientation change --- .../ui/sitemonitor/SiteMonitorParentActivity.kt | 1 + .../ui/sitemonitor/SiteMonitorParentViewModel.kt | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) 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 7a36e9a19334..3538f3ac27f5 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 @@ -102,6 +102,7 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. ) ?: savedStateSparseArray currentSelectItemId = savedInstanceState.getInt(SAVED_STATE_CURRENT_TAB_KEY) + siteMonitorParentViewModel.loadData() } else { siteMonitorParentViewModel.start(getSite()) currentSelectItemId = getInitialTab() 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 index 98a6add7e83b..22fa8eb98614 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorParentViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorParentViewModel.kt @@ -31,19 +31,25 @@ class SiteMonitorParentViewModel @Inject constructor( fun start(site: SiteModel) { this.site = site 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): MutableState { + fun getUiState(siteMonitorType: SiteMonitorType): State { return when (siteMonitorType) { SiteMonitorType.METRICS -> { metricsViewModel.uiState } + SiteMonitorType.PHP_LOGS -> { phpLogViewModel.uiState } + SiteMonitorType.WEB_SERVER_LOGS -> { webServerViewModel.uiState } @@ -55,9 +61,11 @@ class SiteMonitorParentViewModel @Inject constructor( SiteMonitorType.METRICS -> { metricsViewModel.onUrlLoaded() } + SiteMonitorType.PHP_LOGS -> { phpLogViewModel.onUrlLoaded() } + SiteMonitorType.WEB_SERVER_LOGS -> { webServerViewModel.onUrlLoaded() } @@ -69,9 +77,11 @@ class SiteMonitorParentViewModel @Inject constructor( SiteMonitorType.METRICS -> { metricsViewModel.onWebViewError() } + SiteMonitorType.PHP_LOGS -> { phpLogViewModel.onWebViewError() } + SiteMonitorType.WEB_SERVER_LOGS -> { webServerViewModel.onWebViewError() } From 8aa801bd0633a3cb1633d72ebdad6135601aa611 Mon Sep 17 00:00:00 2001 From: Annmarie Ziegler Date: Wed, 31 Jan 2024 08:01:06 -0500 Subject: [PATCH 21/32] Refactor: wrap the content in it's own column to prevent tab jumping --- .../ui/sitemonitor/SiteMonitorParentActivity.kt | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) 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 3538f3ac27f5..fe446346c8eb 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 @@ -17,6 +17,7 @@ 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 @@ -214,12 +215,15 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. val uiState by remember(key1 = tabType) { siteMonitorParentViewModel.getUiState(tabType) } - 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) + 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 From c53f6ff5ef33963c13812c835124a456a2985863 Mon Sep 17 00:00:00 2001 From: Annmarie Ziegler Date: Wed, 31 Jan 2024 08:41:57 -0500 Subject: [PATCH 22/32] Add unit tests for Site Monitor mapper and utils --- .../ui/sitemonitor/SiteMonitorMapperTest.kt | 58 ++++++++++++ .../ui/sitemonitor/SiteMonitorUtilsTest.kt | 92 +++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorMapperTest.kt create mode 100644 WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorUtilsTest.kt 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/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 + ) + ) + } +} From 6a7a13d5c795f89862419e15ed903ddbdf822eef Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Wed, 31 Jan 2024 15:53:37 +0200 Subject: [PATCH 23/32] Add tracking for initial site monitor tab --- .../android/ui/sitemonitor/SiteMonitorParentActivity.kt | 5 +++++ 1 file changed, 5 insertions(+) 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 fe446346c8eb..9f1d0bd3caef 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 @@ -28,6 +28,7 @@ import androidx.compose.material3.Tab 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.remember @@ -169,6 +170,10 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. val tabs = SiteMonitorTabItem.entries + LaunchedEffect(true) { + siteMonitorUtils.trackTabLoaded(tabs[initialTab].siteMonitorType) + } + Column(modifier = modifier.fillMaxWidth()) { androidx.compose.material3.TabRow( selectedTabIndex = tabIndex, From b697e790b10ae789175c7ae20c4b459040772478 Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Wed, 31 Jan 2024 16:36:45 +0200 Subject: [PATCH 24/32] Add tracking for site monitor webview error --- .../android/ui/sitemonitor/SiteMonitorParentActivity.kt | 5 ++++- .../wordpress/android/ui/sitemonitor/SiteMonitorUtils.kt | 8 ++++++++ .../org/wordpress/android/analytics/AnalyticsTracker.java | 1 + .../android/analytics/AnalyticsTrackerNosara.java | 2 ++ 4 files changed, 15 insertions(+), 1 deletion(-) 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 9f1d0bd3caef..57da7e93e2e2 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 @@ -319,6 +319,9 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. override fun onWebViewPageLoaded(url: String, tabType: SiteMonitorType) = siteMonitorParentViewModel.onUrlLoaded(tabType) - override fun onWebViewReceivedError(url: String, tabType: SiteMonitorType) = + 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/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/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; } From 5479d36c14fdd62380ec9891bb98d17fae5dc75a Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Wed, 31 Jan 2024 16:45:21 +0200 Subject: [PATCH 25/32] Add capitalisation for tab names --- .../ui/mysite/personalization/PersonalizationActivity.kt | 2 +- .../android/ui/sitemonitor/SiteMonitorParentActivity.kt | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) 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/SiteMonitorParentActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorParentActivity.kt index 57da7e93e2e2..9fba7836ae95 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 @@ -25,6 +25,7 @@ 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 @@ -175,7 +176,7 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. } Column(modifier = modifier.fillMaxWidth()) { - androidx.compose.material3.TabRow( + TabRow( selectedTabIndex = tabIndex, containerColor = MaterialTheme.colors.surface, contentColor = MaterialTheme.colors.onSurface, @@ -192,8 +193,8 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. Tab( text = { Column(horizontalAlignment = Alignment.CenterHorizontally) { - androidx.compose.material3.Text( - text = stringResource(item.title), + Text( + text = stringResource(item.title).uppercase(), maxLines = 1, overflow = TextOverflow.Ellipsis ) From 29fa1f5bc9fcf61ad5ac979609d4137be8a46798 Mon Sep 17 00:00:00 2001 From: Annmarie Ziegler Date: Wed, 31 Jan 2024 09:39:08 -0500 Subject: [PATCH 26/32] Add unit tests for Site Monitor web view client --- .../SiteMonitorWebViewClientTest.kt | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorWebViewClientTest.kt 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..c4f98154d6c4 --- /dev/null +++ b/WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorWebViewClientTest.kt @@ -0,0 +1,68 @@ +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.Mockito.* + +@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 webview 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()) + } +} + From fb3c6b9815cda5d18f97a1a9227042a936dc91c4 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Wed, 31 Jan 2024 20:32:49 +0530 Subject: [PATCH 27/32] * Updates: the _uiState to use immutable state variable --- .../android/ui/sitemonitor/SiteMonitorTabViewModelSlice.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModelSlice.kt b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModelSlice.kt index fab664186e62..45ec9a56d76f 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModelSlice.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModelSlice.kt @@ -1,6 +1,7 @@ package org.wordpress.android.ui.sitemonitor import android.text.TextUtils +import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel @@ -25,7 +26,7 @@ class SiteMonitorTabViewModelSlice @Inject constructor( private lateinit var urlTemplate: String private val _uiState = mutableStateOf(SiteMonitorUiState.Preparing) - val uiState = _uiState + val uiState: State = _uiState fun initialize(scope: CoroutineScope) { this.scope = scope From 6f0c48efb8226e3caac83eb5a426b8263b194a80 Mon Sep 17 00:00:00 2001 From: Annmarie Ziegler Date: Wed, 31 Jan 2024 11:00:32 -0500 Subject: [PATCH 28/32] Refactor: replace direct access to analytics tracker with siteMonitorUtils --- .../ui/sitemonitor/SiteMonitorParentViewModel.kt | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) 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 index 22fa8eb98614..2a18f07e1fb2 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorParentViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/sitemonitor/SiteMonitorParentViewModel.kt @@ -4,10 +4,8 @@ import androidx.compose.runtime.State import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher -import org.wordpress.android.analytics.AnalyticsTracker import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.modules.BG_THREAD -import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper import org.wordpress.android.viewmodel.ScopedViewModel import javax.inject.Inject import javax.inject.Named @@ -15,7 +13,7 @@ import javax.inject.Named @HiltViewModel class SiteMonitorParentViewModel @Inject constructor( @param:Named(BG_THREAD) private val bgDispatcher: CoroutineDispatcher, - private val analyticsTrackerWrapper: AnalyticsTrackerWrapper, + private val siteMonitorUtils: SiteMonitorUtils, private val metricsViewModel: SiteMonitorTabViewModelSlice, private val phpLogViewModel: SiteMonitorTabViewModelSlice, private val webServerViewModel: SiteMonitorTabViewModelSlice @@ -30,7 +28,7 @@ class SiteMonitorParentViewModel @Inject constructor( fun start(site: SiteModel) { this.site = site - trackActivityLaunched() + siteMonitorUtils.trackActivityLaunched() loadData() } @@ -88,10 +86,6 @@ class SiteMonitorParentViewModel @Inject constructor( } } - private fun trackActivityLaunched() { - analyticsTrackerWrapper.track(AnalyticsTracker.Stat.SITE_MONITORING_SCREEN_SHOWN) - } - override fun onCleared() { super.onCleared() metricsViewModel.onCleared() From 1cf5193b0ec6d96b384237d4e6a786152d7fb5c5 Mon Sep 17 00:00:00 2001 From: Annmarie Ziegler Date: Wed, 31 Jan 2024 11:00:54 -0500 Subject: [PATCH 29/32] Add unit tests for SiteMonitorParentViewModel --- .../SiteMonitorParentViewModelTest.kt | 170 ++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorParentViewModelTest.kt 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() + } +} From c21915f7cb49ca34eaec75ca62139a478ce5d163 Mon Sep 17 00:00:00 2001 From: Annmarie Ziegler Date: Wed, 31 Jan 2024 11:01:10 -0500 Subject: [PATCH 30/32] Fix detekt issue --- .../android/ui/sitemonitor/SiteMonitorWebViewClientTest.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 index c4f98154d6c4..38b11dbd998d 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorWebViewClientTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorWebViewClientTest.kt @@ -13,7 +13,9 @@ import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import org.wordpress.android.BaseUnitTest import android.webkit.WebResourceError -import org.mockito.Mockito.* +import org.mockito.ArgumentMatchers.anyString +import org.mockito.Mockito.mock +import org.mockito.kotlin.never @ExperimentalCoroutinesApi class SiteMonitorWebViewClientTest : BaseUnitTest() { @@ -32,7 +34,7 @@ class SiteMonitorWebViewClientTest : BaseUnitTest() { } @Test - fun `when onPageFinished, then should invoke on webview page loaded`() { + 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) From e8da1d82ad1c4d47880b26a56e778962e3f9c7b0 Mon Sep 17 00:00:00 2001 From: Annmarie Ziegler Date: Wed, 31 Jan 2024 12:15:56 -0500 Subject: [PATCH 31/32] Add unit tests for SiteMonitorTabViewModelSlice --- .../SiteMonitorTabViewModelSliceTest.kt | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 WordPress/src/test/java/org/wordpress/android/ui/sitemonitor/SiteMonitorTabViewModelSliceTest.kt 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" + } +} From 82d7b927c466cb4babe24dac1c50370b2c9184f3 Mon Sep 17 00:00:00 2001 From: Annmarie Ziegler Date: Wed, 31 Jan 2024 13:21:45 -0500 Subject: [PATCH 32/32] Fix checkstyle empty line --- .../android/ui/sitemonitor/SiteMonitorParentActivity.kt | 1 - 1 file changed, 1 deletion(-) 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 9fba7836ae95..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 @@ -324,5 +324,4 @@ class SiteMonitorParentActivity : AppCompatActivity(), SiteMonitorWebViewClient. siteMonitorParentViewModel.onWebViewError(tabType) siteMonitorUtils.trackTabLoadingError(tabType) } - }