From 2148e2c3a02e418e7d75c69995bbf017e3bd8a93 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2024 18:53:48 +0000 Subject: [PATCH 01/27] Bump androidx-wear-compose from 1.3.1 to 1.4.0 Bumps `androidx-wear-compose` from 1.3.1 to 1.4.0. Updates `androidx.wear.compose:compose-material` from 1.3.1 to 1.4.0 Updates `androidx.wear.compose:compose-foundation` from 1.3.1 to 1.4.0 --- updated-dependencies: - dependency-name: androidx.wear.compose:compose-material dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: androidx.wear.compose:compose-foundation dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 892284023ad..92745be6710 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -28,7 +28,7 @@ androidx-test-ext = '1.1.5' androidx-test-main = '1.4.0' androidx-test-uiautomator = '2.2.0' androidx-transition = '1.5.1' -androidx-wear-compose = '1.3.1' +androidx-wear-compose = '1.4.0' androidx-wear-tiles = '1.3.0' androidx-wear-tooling = '1.0.0' androidx-wear-watchface = '1.2.1' From 8e03f9b5e64bde68f49e81bd0ae85e289e2c3602 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 6 Jan 2025 14:55:00 +0100 Subject: [PATCH 02/27] Previews with theme --- .../android/ui/prefs/plugins/PluginsScreen.kt | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt index bfd66d7571c..e3e442dfa59 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt @@ -44,6 +44,7 @@ import com.woocommerce.android.extensions.isNotNullOrEmpty import com.woocommerce.android.ui.compose.component.Toolbar import com.woocommerce.android.ui.compose.component.WCColoredButton import com.woocommerce.android.ui.compose.preview.LightDarkThemePreviews +import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin.PluginStatus.Inactive @@ -252,33 +253,39 @@ private fun Error(onRetryTapped: () -> Unit) { @LightDarkThemePreviews @Composable private fun PreviewPlugins() { - PluginsScreen( - ViewState.Loaded( - plugins = listOf( - Plugin("Plugin 1", "Automattic", "1.0", UpToDate("Up-to-date")), - Plugin("Plugin 2", null, "2.0", UpdateAvailable("Update available (4.9)")), - Plugin("Plugin 3", "Gutenberg", "3.0", Inactive("Inactive")), - Plugin("Plugin 5", "Blabla", "5.0", Unknown) - ) - ), - onRetryTapped = {} - ) + WooThemeWithBackground { + PluginsScreen( + ViewState.Loaded( + plugins = listOf( + Plugin("Plugin 1", "Automattic", "1.0", UpToDate("Up-to-date")), + Plugin("Plugin 2", null, "2.0", UpdateAvailable("Update available (4.9)")), + Plugin("Plugin 3", "Gutenberg", "3.0", Inactive("Inactive")), + Plugin("Plugin 5", "Blabla", "5.0", Unknown) + ) + ), + onRetryTapped = {} + ) + } } @LightDarkThemePreviews @Composable private fun PreviewError() { - PluginsScreen( - ViewState.Error, - onRetryTapped = {} - ) + WooThemeWithBackground { + PluginsScreen( + ViewState.Error, + onRetryTapped = {} + ) + } } @LightDarkThemePreviews @Composable private fun PreviewLoading() { - PluginsScreen( - ViewState.Loading, - onRetryTapped = {} - ) + WooThemeWithBackground { + PluginsScreen( + ViewState.Loading, + onRetryTapped = {} + ) + } } From 65d7f68d9124c07798f35f48ce8c0437026cf419 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 6 Jan 2025 16:03:34 +0100 Subject: [PATCH 03/27] Changes in the UI to hint that the row is clickable --- .../android/ui/prefs/plugins/PluginsScreen.kt | 44 ++++++++++++++++--- .../ui/prefs/plugins/PluginsViewModel.kt | 23 +++++----- 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt index e3e442dfa59..333fa92b8b6 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt @@ -16,16 +16,19 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.Divider +import androidx.compose.material.Icon import androidx.compose.material.MaterialTheme import androidx.compose.material.Scaffold import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.Refresh import androidx.compose.runtime.Composable import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment @@ -83,9 +86,11 @@ private fun PluginsScreen(state: ViewState, onRetryTapped: () -> Unit) { is ViewState.Loading -> { ShimmerPluginsList() } + is ViewState.Error -> { Error(onRetryTapped) } + is ViewState.Loaded -> { Plugins(it.plugins) } @@ -134,12 +139,41 @@ private fun PluginItem(plugin: Plugin) { ) } - if (plugin.status !is Unknown) { - Text( + when (plugin.status) { + is Inactive -> Text( text = plugin.status.title, color = colorResource(id = plugin.status.color), fontWeight = FontWeight.Bold ) + + is UpToDate -> Text( + text = plugin.status.title, + color = colorResource(id = plugin.status.color), + fontWeight = FontWeight.Bold + ) + + is UpdateAvailable -> { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + Icon( + imageVector = Icons.Default.Refresh, + contentDescription = plugin.status.title, + tint = colorResource(id = plugin.status.color), + modifier = Modifier + .size(24.dp) + ) + + Text( + text = plugin.status.title, + color = colorResource(id = plugin.status.color), + fontWeight = FontWeight.Bold + ) + } + } + + Unknown -> {} } } } @@ -257,9 +291,9 @@ private fun PreviewPlugins() { PluginsScreen( ViewState.Loaded( plugins = listOf( - Plugin("Plugin 1", "Automattic", "1.0", UpToDate("Up-to-date")), - Plugin("Plugin 2", null, "2.0", UpdateAvailable("Update available (4.9)")), - Plugin("Plugin 3", "Gutenberg", "3.0", Inactive("Inactive")), + Plugin("Plugin 1", "Automattic", "1.0", UpToDate("Up-to-date", R.color.color_info)), + Plugin("Plugin 2", "Something", "2.0", UpdateAvailable("Update available (4.9)", R.color.color_primary)), + Plugin("Plugin 3", "Gutenberg", "3.0", Inactive("Inactive", R.color.color_on_surface_disabled)), Plugin("Plugin 5", "Blabla", "5.0", Unknown) ) ), diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt index 591bee9769b..5be0f00f0c9 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt @@ -69,12 +69,16 @@ class PluginsViewModel @Inject constructor( private fun SystemPluginModel.getState(): Plugin.PluginStatus { return when { - !isActive -> Inactive(resourceProvider.getString(R.string.plugin_state_inactive)) + !isActive -> Inactive( + resourceProvider.getString(R.string.plugin_state_inactive), + R.color.color_on_surface_disabled, + ) versionLatest.isNullOrEmpty() -> Unknown isUpdateAvailable() -> UpdateAvailable( - resourceProvider.getString(R.string.plugin_state_update_available, versionLatest!!) + resourceProvider.getString(R.string.plugin_state_update_available, versionLatest!!), + R.color.color_primary, ) - else -> UpToDate(resourceProvider.getString(R.string.plugin_state_up_to_date)) + else -> UpToDate(resourceProvider.getString(R.string.plugin_state_up_to_date), R.color.color_info) } } @@ -102,14 +106,11 @@ class PluginsViewModel @Inject constructor( val version: String, val status: PluginStatus ) { - sealed class PluginStatus(open val title: String, @ColorRes val color: Int) { - data class UpToDate(override val title: String) : PluginStatus(title, R.color.color_info) - data class UpdateAvailable(override val title: String) : PluginStatus(title, R.color.color_alert) - data class Inactive(override val title: String) : PluginStatus( - title, - R.color.color_on_surface_disabled - ) - data object Unknown : PluginStatus("", R.color.color_on_surface_disabled) + sealed class PluginStatus { + data class UpToDate(val title: String, @ColorRes val color: Int) : PluginStatus() + data class UpdateAvailable(val title: String, @ColorRes val color: Int) : PluginStatus() + data class Inactive(val title: String, @ColorRes val color: Int) : PluginStatus() + data object Unknown : PluginStatus() } } } From df1f80f4078373148bac508a636acd2f22cab0bf Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 6 Jan 2025 16:11:46 +0100 Subject: [PATCH 04/27] Click on plugin. Removed unknown status --- .../android/ui/prefs/plugins/PluginsScreen.kt | 40 ++++++++++++++----- .../ui/prefs/plugins/PluginsViewModel.kt | 33 ++++++++++----- 2 files changed, 53 insertions(+), 20 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt index 333fa92b8b6..3303cccf9a6 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt @@ -8,6 +8,7 @@ import androidx.compose.animation.core.infiniteRepeatable import androidx.compose.animation.core.rememberInfiniteTransition import androidx.compose.animation.core.tween import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -73,14 +74,22 @@ fun PluginsScreen(viewModel: PluginsViewModel) { .padding(paddingValues) ) { viewModel.viewState.observeAsState().value?.let { state -> - PluginsScreen(state, viewModel::onRetryClicked) + PluginsScreen( + state = state, + onRetryTapped = viewModel::onRetryClicked, + onPluginClicked = viewModel::onPluginClicked, + ) } } } } @Composable -private fun PluginsScreen(state: ViewState, onRetryTapped: () -> Unit) { +private fun PluginsScreen( + state: ViewState, + onRetryTapped: () -> Unit, + onPluginClicked: (Plugin) -> Unit, +) { Crossfade(targetState = state, label = "") { when (it) { is ViewState.Loading -> { @@ -92,17 +101,23 @@ private fun PluginsScreen(state: ViewState, onRetryTapped: () -> Unit) { } is ViewState.Loaded -> { - Plugins(it.plugins) + Plugins( + it.plugins, + onPluginClicked, + ) } } } } @Composable -private fun Plugins(plugins: List) { +private fun Plugins( + plugins: List, + onPluginClicked: (Plugin) -> Unit +) { LazyColumn { items(plugins) { plugin -> - PluginItem(plugin) + PluginItem(plugin, onPluginClicked) if (plugins.last() != plugin) { Divider() @@ -112,11 +127,15 @@ private fun Plugins(plugins: List) { } @Composable -private fun PluginItem(plugin: Plugin) { +private fun PluginItem( + plugin: Plugin, + onPluginClicked: (Plugin) -> Unit +) { Row( modifier = Modifier .fillMaxWidth() .padding(dimensionResource(R.dimen.major_100)) + .clickable(onClick = { onPluginClicked(plugin) }) ) { Column( modifier = Modifier @@ -297,7 +316,8 @@ private fun PreviewPlugins() { Plugin("Plugin 5", "Blabla", "5.0", Unknown) ) ), - onRetryTapped = {} + onRetryTapped = {}, + onPluginClicked = {}, ) } } @@ -308,7 +328,8 @@ private fun PreviewError() { WooThemeWithBackground { PluginsScreen( ViewState.Error, - onRetryTapped = {} + onRetryTapped = {}, + onPluginClicked = {}, ) } } @@ -319,7 +340,8 @@ private fun PreviewLoading() { WooThemeWithBackground { PluginsScreen( ViewState.Loading, - onRetryTapped = {} + onRetryTapped = {}, + onPluginClicked = {}, ) } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt index 5be0f00f0c9..41a11bd262d 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt @@ -11,7 +11,6 @@ import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Error import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin.PluginStatus.Inactive -import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin.PluginStatus.Unknown import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin.PluginStatus.UpToDate import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin.PluginStatus.UpdateAvailable import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loading @@ -51,13 +50,18 @@ class PluginsViewModel @Inject constructor( Loaded( plugins = response.model!! .filter { it.version.isNotNullOrEmpty() && it.name.isNotNullOrEmpty() } - .map { - Plugin( - name = StringEscapeUtils.unescapeHtml4(it.name), - authorName = StringEscapeUtils.unescapeHtml4(it.authorName), - version = it.version!!, - status = it.getState() - ) + .mapNotNull { + val status = it.getStatus() + if (status == null) { + null + } else { + Plugin( + name = StringEscapeUtils.unescapeHtml4(it.name), + authorName = StringEscapeUtils.unescapeHtml4(it.authorName), + version = it.version!!, + status = status + ) + } } ) ) @@ -67,13 +71,13 @@ class PluginsViewModel @Inject constructor( } } - private fun SystemPluginModel.getState(): Plugin.PluginStatus { + private fun SystemPluginModel.getStatus(): Plugin.PluginStatus? { return when { !isActive -> Inactive( resourceProvider.getString(R.string.plugin_state_inactive), R.color.color_on_surface_disabled, ) - versionLatest.isNullOrEmpty() -> Unknown + versionLatest.isNullOrEmpty() -> null isUpdateAvailable() -> UpdateAvailable( resourceProvider.getString(R.string.plugin_state_update_available, versionLatest!!), R.color.color_primary, @@ -90,6 +94,14 @@ class PluginsViewModel @Inject constructor( loadPlugins() } + fun onPluginClicked(plugin: Plugin) { + when (plugin.status) { + is Inactive -> {} + is UpToDate -> TODO() + is UpdateAvailable -> TODO() + } + } + fun onBackPressed() { triggerEvent(Exit) } @@ -110,7 +122,6 @@ class PluginsViewModel @Inject constructor( data class UpToDate(val title: String, @ColorRes val color: Int) : PluginStatus() data class UpdateAvailable(val title: String, @ColorRes val color: Int) : PluginStatus() data class Inactive(val title: String, @ColorRes val color: Int) : PluginStatus() - data object Unknown : PluginStatus() } } } From ba0769615c9b4aae8decceed6e1ab3865e991de7 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 6 Jan 2025 16:12:29 +0100 Subject: [PATCH 05/27] Returned back unknown status --- .../ui/prefs/plugins/PluginsViewModel.kt | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt index 41a11bd262d..639a2979680 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt @@ -11,6 +11,7 @@ import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Error import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin.PluginStatus.Inactive +import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin.PluginStatus.Unknown import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin.PluginStatus.UpToDate import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin.PluginStatus.UpdateAvailable import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loading @@ -50,18 +51,13 @@ class PluginsViewModel @Inject constructor( Loaded( plugins = response.model!! .filter { it.version.isNotNullOrEmpty() && it.name.isNotNullOrEmpty() } - .mapNotNull { - val status = it.getStatus() - if (status == null) { - null - } else { - Plugin( - name = StringEscapeUtils.unescapeHtml4(it.name), - authorName = StringEscapeUtils.unescapeHtml4(it.authorName), - version = it.version!!, - status = status - ) - } + .map { + Plugin( + name = StringEscapeUtils.unescapeHtml4(it.name), + authorName = StringEscapeUtils.unescapeHtml4(it.authorName), + version = it.version!!, + status = it.getState() + ) } ) ) @@ -71,13 +67,13 @@ class PluginsViewModel @Inject constructor( } } - private fun SystemPluginModel.getStatus(): Plugin.PluginStatus? { + private fun SystemPluginModel.getState(): Plugin.PluginStatus? { return when { !isActive -> Inactive( resourceProvider.getString(R.string.plugin_state_inactive), R.color.color_on_surface_disabled, ) - versionLatest.isNullOrEmpty() -> null + versionLatest.isNullOrEmpty() -> Unknown isUpdateAvailable() -> UpdateAvailable( resourceProvider.getString(R.string.plugin_state_update_available, versionLatest!!), R.color.color_primary, @@ -97,6 +93,7 @@ class PluginsViewModel @Inject constructor( fun onPluginClicked(plugin: Plugin) { when (plugin.status) { is Inactive -> {} + Unknown -> {} is UpToDate -> TODO() is UpdateAvailable -> TODO() } @@ -122,6 +119,7 @@ class PluginsViewModel @Inject constructor( data class UpToDate(val title: String, @ColorRes val color: Int) : PluginStatus() data class UpdateAvailable(val title: String, @ColorRes val color: Int) : PluginStatus() data class Inactive(val title: String, @ColorRes val color: Int) : PluginStatus() + data object Unknown : PluginStatus() } } } From ea01f72e277a27c01813e7048088d5424e65b8ed Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 6 Jan 2025 16:13:05 +0100 Subject: [PATCH 06/27] Renamed state to status in the function name --- .../woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt index 639a2979680..b741bb1f48e 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt @@ -56,7 +56,7 @@ class PluginsViewModel @Inject constructor( name = StringEscapeUtils.unescapeHtml4(it.name), authorName = StringEscapeUtils.unescapeHtml4(it.authorName), version = it.version!!, - status = it.getState() + status = it.getStatus() ) } ) @@ -67,7 +67,7 @@ class PluginsViewModel @Inject constructor( } } - private fun SystemPluginModel.getState(): Plugin.PluginStatus? { + private fun SystemPluginModel.getStatus(): Plugin.PluginStatus { return when { !isActive -> Inactive( resourceProvider.getString(R.string.plugin_state_inactive), From a5d822a82d2499dd3ba3ed78dba3fa426be8757d Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 6 Jan 2025 16:23:10 +0100 Subject: [PATCH 07/27] Handle click --- .../woocommerce/android/ui/prefs/plugins/PluginsEvent.kt | 7 +++++++ .../android/ui/prefs/plugins/PluginsFragment.kt | 5 +++++ .../android/ui/prefs/plugins/PluginsViewModel.kt | 7 +++++-- 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsEvent.kt diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsEvent.kt new file mode 100644 index 00000000000..ed659390759 --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsEvent.kt @@ -0,0 +1,7 @@ +package com.woocommerce.android.ui.prefs.plugins + +import com.woocommerce.android.viewmodel.MultiLiveEvent + +sealed class PluginsEvent : MultiLiveEvent.Event() { + data class NavigateToPluginsWeb(val url: String) : PluginsEvent() +} diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsFragment.kt index b1c12d44c29..7f43878b59e 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsFragment.kt @@ -7,6 +7,7 @@ import android.view.ViewGroup import androidx.compose.ui.platform.ComposeView import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController +import com.woocommerce.android.NavGraphSettingsDirections import com.woocommerce.android.analytics.AnalyticsTracker import com.woocommerce.android.ui.base.BaseFragment import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground @@ -46,6 +47,10 @@ class PluginsFragment : BaseFragment() { viewModel.event.observe(viewLifecycleOwner) { event -> when (event) { is MultiLiveEvent.Event.Exit -> findNavController().navigateUp() + is PluginsEvent.NavigateToPluginsWeb -> { + findNavController() + .navigate(NavGraphSettingsDirections.actionGlobalWPComWebViewFragment(event.url)) + } } } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt index b741bb1f48e..abd4dcbe25a 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import com.woocommerce.android.R +import com.woocommerce.android.extensions.adminUrlOrDefault import com.woocommerce.android.extensions.isNotNullOrEmpty import com.woocommerce.android.tools.SelectedSite import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Error @@ -94,8 +95,10 @@ class PluginsViewModel @Inject constructor( when (plugin.status) { is Inactive -> {} Unknown -> {} - is UpToDate -> TODO() - is UpdateAvailable -> TODO() + is UpToDate -> {} + is UpdateAvailable -> { + triggerEvent(PluginsEvent.NavigateToPluginsWeb("${site.get().adminUrlOrDefault}/plugins.php")) + } } } From cf65ddf36b87cc42bf79cc2c740e52e2697ea7f2 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 6 Jan 2025 16:31:52 +0100 Subject: [PATCH 08/27] Change order of the padding and clickable to have proper reaction on click --- .../com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt index 3303cccf9a6..da412e744c6 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt @@ -134,8 +134,8 @@ private fun PluginItem( Row( modifier = Modifier .fillMaxWidth() - .padding(dimensionResource(R.dimen.major_100)) .clickable(onClick = { onPluginClicked(plugin) }) + .padding(dimensionResource(R.dimen.major_100)) ) { Column( modifier = Modifier From 1fadd3923883518761b4c7340175d0bf406e26b2 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 6 Jan 2025 16:35:57 +0100 Subject: [PATCH 09/27] Move view state to a separate class --- .../android/ui/prefs/plugins/PluginsScreen.kt | 25 ++++++----- .../ui/prefs/plugins/PluginsViewModel.kt | 41 ++++--------------- .../ui/prefs/plugins/PluginsViewState.kt | 25 +++++++++++ 3 files changed, 46 insertions(+), 45 deletions(-) create mode 100644 WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewState.kt diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt index da412e744c6..dcfe8265a20 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt @@ -49,12 +49,11 @@ import com.woocommerce.android.ui.compose.component.Toolbar import com.woocommerce.android.ui.compose.component.WCColoredButton import com.woocommerce.android.ui.compose.preview.LightDarkThemePreviews import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground -import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState -import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin -import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin.PluginStatus.Inactive -import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin.PluginStatus.Unknown -import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin.PluginStatus.UpToDate -import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin.PluginStatus.UpdateAvailable +import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin +import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin.PluginStatus.Inactive +import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin.PluginStatus.Unknown +import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin.PluginStatus.UpToDate +import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin.PluginStatus.UpdateAvailable @Composable fun PluginsScreen(viewModel: PluginsViewModel) { @@ -86,21 +85,21 @@ fun PluginsScreen(viewModel: PluginsViewModel) { @Composable private fun PluginsScreen( - state: ViewState, + state: PluginsViewState, onRetryTapped: () -> Unit, onPluginClicked: (Plugin) -> Unit, ) { Crossfade(targetState = state, label = "") { when (it) { - is ViewState.Loading -> { + is PluginsViewState.Loading -> { ShimmerPluginsList() } - is ViewState.Error -> { + is PluginsViewState.Error -> { Error(onRetryTapped) } - is ViewState.Loaded -> { + is PluginsViewState.Loaded -> { Plugins( it.plugins, onPluginClicked, @@ -308,7 +307,7 @@ private fun Error(onRetryTapped: () -> Unit) { private fun PreviewPlugins() { WooThemeWithBackground { PluginsScreen( - ViewState.Loaded( + PluginsViewState.Loaded( plugins = listOf( Plugin("Plugin 1", "Automattic", "1.0", UpToDate("Up-to-date", R.color.color_info)), Plugin("Plugin 2", "Something", "2.0", UpdateAvailable("Update available (4.9)", R.color.color_primary)), @@ -327,7 +326,7 @@ private fun PreviewPlugins() { private fun PreviewError() { WooThemeWithBackground { PluginsScreen( - ViewState.Error, + PluginsViewState.Error, onRetryTapped = {}, onPluginClicked = {}, ) @@ -339,7 +338,7 @@ private fun PreviewError() { private fun PreviewLoading() { WooThemeWithBackground { PluginsScreen( - ViewState.Loading, + PluginsViewState.Loading, onRetryTapped = {}, onPluginClicked = {}, ) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt index abd4dcbe25a..e9d25313da4 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt @@ -1,6 +1,5 @@ package com.woocommerce.android.ui.prefs.plugins -import androidx.annotation.ColorRes import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope @@ -8,14 +7,14 @@ import com.woocommerce.android.R import com.woocommerce.android.extensions.adminUrlOrDefault import com.woocommerce.android.extensions.isNotNullOrEmpty import com.woocommerce.android.tools.SelectedSite -import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Error -import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded -import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin -import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin.PluginStatus.Inactive -import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin.PluginStatus.Unknown -import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin.PluginStatus.UpToDate -import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin.PluginStatus.UpdateAvailable -import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loading +import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Error +import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded +import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin +import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin.PluginStatus.Inactive +import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin.PluginStatus.Unknown +import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin.PluginStatus.UpToDate +import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin.PluginStatus.UpdateAvailable +import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loading import com.woocommerce.android.util.isGreaterThanPluginVersion import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.Exit import com.woocommerce.android.viewmodel.ResourceProvider @@ -36,7 +35,7 @@ class PluginsViewModel @Inject constructor( private val wooCommerceStore: WooCommerceStore, private val resourceProvider: ResourceProvider ) : ScopedViewModel(savedStateHandle) { - private val _viewState = MutableSharedFlow(1) + private val _viewState = MutableSharedFlow(1) val viewState = _viewState.asLiveData() init { @@ -105,26 +104,4 @@ class PluginsViewModel @Inject constructor( fun onBackPressed() { triggerEvent(Exit) } - - sealed interface ViewState { - data object Loading : ViewState - data object Error : ViewState - data class Loaded( - val plugins: List = emptyList() - ) : ViewState { - data class Plugin( - val name: String, - val authorName: String?, - val version: String, - val status: PluginStatus - ) { - sealed class PluginStatus { - data class UpToDate(val title: String, @ColorRes val color: Int) : PluginStatus() - data class UpdateAvailable(val title: String, @ColorRes val color: Int) : PluginStatus() - data class Inactive(val title: String, @ColorRes val color: Int) : PluginStatus() - data object Unknown : PluginStatus() - } - } - } - } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewState.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewState.kt new file mode 100644 index 00000000000..256068ad217 --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewState.kt @@ -0,0 +1,25 @@ +package com.woocommerce.android.ui.prefs.plugins + +import androidx.annotation.ColorRes + +sealed interface PluginsViewState { + data object Loading : PluginsViewState + data object Error : PluginsViewState + data class Loaded( + val plugins: List = emptyList() + ) : PluginsViewState { + data class Plugin( + val name: String, + val authorName: String?, + val version: String, + val status: PluginStatus + ) { + sealed class PluginStatus { + data class UpToDate(val title: String, @ColorRes val color: Int) : PluginStatus() + data class UpdateAvailable(val title: String, @ColorRes val color: Int) : PluginStatus() + data class Inactive(val title: String, @ColorRes val color: Int) : PluginStatus() + data object Unknown : PluginStatus() + } + } + } +} From 03a312978f00f5308da27f9c0b5341d1bb0ade2e Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 6 Jan 2025 16:36:52 +0100 Subject: [PATCH 10/27] Fixed formatting --- .../woocommerce/android/ui/prefs/plugins/PluginsScreen.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt index dcfe8265a20..9a7e15ed6dc 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsScreen.kt @@ -310,7 +310,12 @@ private fun PreviewPlugins() { PluginsViewState.Loaded( plugins = listOf( Plugin("Plugin 1", "Automattic", "1.0", UpToDate("Up-to-date", R.color.color_info)), - Plugin("Plugin 2", "Something", "2.0", UpdateAvailable("Update available (4.9)", R.color.color_primary)), + Plugin( + "Plugin 2", + "Something", + "2.0", + UpdateAvailable("Update available (4.9)", R.color.color_primary) + ), Plugin("Plugin 3", "Gutenberg", "3.0", Inactive("Inactive", R.color.color_on_surface_disabled)), Plugin("Plugin 5", "Blabla", "5.0", Unknown) ) From 5bf5ea576a9952d549c86197a50baa16b96020d8 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 6 Jan 2025 16:43:47 +0100 Subject: [PATCH 11/27] Updated release notes --- RELEASE-NOTES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index f4674cd7fec..76cfa703f81 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -8,6 +8,7 @@ - [*] Fixed a crash on the order details [https://github.com/woocommerce/woocommerce-android/pull/13191] - [**] Fixed a crash when a shop manager was trying to install or activate plugin in the POS onboarding [https://github.com/woocommerce/woocommerce-android/pull/13203] - [**] Introduced fallback logic for the barcode scanner to use the front-facing camera when a back-facing camera is unavailable [https://github.com/woocommerce/woocommerce-android/pull/13230] +- [*] It possible to quickly open plugins page from the plugins list in the up to update an outdated plugin [https://github.com/woocommerce/woocommerce-android/pull/13246] 21.3 ----- From f146f7e6945d1f452a2c0c036bb8ea6b06d1914f Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 6 Jan 2025 17:14:54 +0100 Subject: [PATCH 12/27] Some unit tests --- .../ui/prefs/plugins/PluginsViewModelTest.kt | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 WooCommerce/src/test/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModelTest.kt diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModelTest.kt new file mode 100644 index 00000000000..b9720043624 --- /dev/null +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModelTest.kt @@ -0,0 +1,148 @@ +package com.woocommerce.android.ui.prefs.plugins + +import androidx.lifecycle.SavedStateHandle +import com.woocommerce.android.R +import com.woocommerce.android.tools.SelectedSite +import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded +import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin +import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin.PluginStatus.* +import com.woocommerce.android.util.captureValues +import com.woocommerce.android.viewmodel.BaseUnitTest +import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.Exit +import com.woocommerce.android.viewmodel.ResourceProvider +import kotlinx.coroutines.ExperimentalCoroutinesApi +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever +import org.wordpress.android.fluxc.model.SiteModel +import org.wordpress.android.fluxc.network.BaseRequest.GenericErrorType +import org.wordpress.android.fluxc.network.rest.wpcom.wc.WooError +import org.wordpress.android.fluxc.network.rest.wpcom.wc.WooErrorType +import org.wordpress.android.fluxc.network.rest.wpcom.wc.WooResult +import org.wordpress.android.fluxc.network.rest.wpcom.wc.system.WCSystemPluginResponse.SystemPluginModel +import org.wordpress.android.fluxc.store.WooCommerceStore + +@OptIn(ExperimentalCoroutinesApi::class) +class PluginsViewModelTest : BaseUnitTest() { + private val savedStateHandle: SavedStateHandle = mock() + private val selectedSite: SelectedSite = mock { + on { get() }.thenReturn(mock()) + } + private val wooCommerceStore: WooCommerceStore = mock() + private val resourceProvider: ResourceProvider = mock { + on { getString(R.string.plugin_state_update_available, "1.1.0") } + .thenReturn("Update available to 1.1.0") + on { getString(R.string.plugin_state_up_to_date) }.thenReturn("Up-to-date") + on { getString(R.string.plugin_state_inactive) }.thenReturn("Inactive") + } + + private fun createViewModel() = PluginsViewModel( + savedStateHandle, + selectedSite, + wooCommerceStore, + resourceProvider + ) + + @Test + fun `given plugins are fetched successfully, when vm init, then viewState should be Loaded`() = testBlocking { + // GIVEN + val pluginsResponse = listOf( + SystemPluginModel( + name = "Plugin A", + authorName = "Author A", + plugin = "plugin.php", + version = "1.0.0", + versionLatest = "1.1.0", + url = "https://example.com/plugins.php", + isActive = true + ), + SystemPluginModel( + name = "Plugin B", + authorName = "Author B", + plugin = "plugin.php", + version = "2.0.0", + versionLatest = "2.0.0", + url = "https://example.com/plugins.php", + isActive = false + ) + ) + whenever(wooCommerceStore.fetchSystemPlugins(selectedSite.get())).thenReturn(WooResult(pluginsResponse)) + + // WHEN + val viewModel = createViewModel() + + // THEN + val values = viewModel.viewState.captureValues() + assertThat(values.last()).isEqualTo( + Loaded( + plugins = listOf( + Plugin( + "Plugin A", + "Author A", + "1.0.0", + UpdateAvailable("Update available to 1.1.0", R.color.color_primary) + ), + Plugin("Plugin B", "Author B", "2.0.0", Inactive("Inactive", R.color.color_on_surface_disabled)) + ) + ) + ) + } + + @Test + fun `given plugins fetch fails, when vm init, then viewState should be Error`() = testBlocking { + // GIVEN + whenever(wooCommerceStore.fetchSystemPlugins(selectedSite.get())).thenReturn( + WooResult( + WooError( + type = WooErrorType.GENERIC_ERROR, + original = GenericErrorType.SERVER_ERROR + ) + ) + ) + + // WHEN + val viewModel = createViewModel() + + // THEN + val values = viewModel.viewState.captureValues() + assertThat(values.last()).isEqualTo(PluginsViewState.Error) + } + + @Test + fun `given plugin status is UpdateAvailable, when onPluginClicked is called, then navigate event is triggered`() = + testBlocking { + // GIVEN + val plugin = Plugin( + name = "Plugin A", + authorName = "Author A", + version = "1.0.0", + status = UpdateAvailable("Update available to 1.1.0", R.color.color_primary) + ) + val siteModel = mock { + on { adminUrl }.thenReturn("https://example.com/wp-admin") + } + whenever(selectedSite.get()).thenReturn(siteModel) + val viewModel = createViewModel() + + // WHEN + viewModel.onPluginClicked(plugin) + + // THEN + assertThat(viewModel.event.value).isEqualTo( + PluginsEvent.NavigateToPluginsWeb("https://example.com/wp-admin/plugins.php") + ) + } + + @Test + fun `when onBackPressed is called, then Exit event is triggered`() { + // GIVEN + val viewModel = createViewModel() + + // WHEN + viewModel.onBackPressed() + + // THEN + assertThat(viewModel.event.value).isEqualTo(Exit) + } +} From 069f300342d5cc4d811f8788caedb865d26f70c1 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 7 Jan 2025 10:55:39 +0100 Subject: [PATCH 13/27] More tests --- .../ui/prefs/plugins/PluginsViewModel.kt | 6 +- .../ui/prefs/plugins/PluginsViewModelTest.kt | 173 +++++++++++++++++- 2 files changed, 171 insertions(+), 8 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt index e9d25313da4..c3de9c8bec3 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModel.kt @@ -20,6 +20,7 @@ import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.Exit import com.woocommerce.android.viewmodel.ResourceProvider import com.woocommerce.android.viewmodel.ScopedViewModel import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.launch @@ -33,7 +34,8 @@ class PluginsViewModel @Inject constructor( savedStateHandle: SavedStateHandle, private val site: SelectedSite, private val wooCommerceStore: WooCommerceStore, - private val resourceProvider: ResourceProvider + private val resourceProvider: ResourceProvider, + private val dispatcher: CoroutineDispatcher = Dispatchers.IO, ) : ScopedViewModel(savedStateHandle) { private val _viewState = MutableSharedFlow(1) val viewState = _viewState.asLiveData() @@ -43,7 +45,7 @@ class PluginsViewModel @Inject constructor( } private fun loadPlugins() { - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(dispatcher) { _viewState.emit(Loading) val response = wooCommerceStore.fetchSystemPlugins(site.get()) if (!response.isError && response.model != null) { diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModelTest.kt index b9720043624..a6c59f41388 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/prefs/plugins/PluginsViewModelTest.kt @@ -5,12 +5,16 @@ import com.woocommerce.android.R import com.woocommerce.android.tools.SelectedSite import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin -import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin.PluginStatus.* +import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin.PluginStatus.Inactive +import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin.PluginStatus.Unknown +import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin.PluginStatus.UpToDate +import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin.PluginStatus.UpdateAvailable import com.woocommerce.android.util.captureValues import com.woocommerce.android.viewmodel.BaseUnitTest import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.Exit import com.woocommerce.android.viewmodel.ResourceProvider import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.UnconfinedTestDispatcher import org.assertj.core.api.Assertions.assertThat import org.junit.Test import org.mockito.kotlin.mock @@ -33,15 +37,18 @@ class PluginsViewModelTest : BaseUnitTest() { private val resourceProvider: ResourceProvider = mock { on { getString(R.string.plugin_state_update_available, "1.1.0") } .thenReturn("Update available to 1.1.0") - on { getString(R.string.plugin_state_up_to_date) }.thenReturn("Up-to-date") - on { getString(R.string.plugin_state_inactive) }.thenReturn("Inactive") + on { getString(R.string.plugin_state_update_available, "2.1.0") } + .thenReturn("Update available to 2.1.0") + on { getString(R.string.plugin_state_inactive) } + .thenReturn("Inactive") } private fun createViewModel() = PluginsViewModel( savedStateHandle, selectedSite, wooCommerceStore, - resourceProvider + resourceProvider, + UnconfinedTestDispatcher() ) @Test @@ -67,7 +74,8 @@ class PluginsViewModelTest : BaseUnitTest() { isActive = false ) ) - whenever(wooCommerceStore.fetchSystemPlugins(selectedSite.get())).thenReturn(WooResult(pluginsResponse)) + whenever(wooCommerceStore.fetchSystemPlugins(selectedSite.get())) + .thenReturn(WooResult(pluginsResponse)) // WHEN val viewModel = createViewModel() @@ -83,7 +91,12 @@ class PluginsViewModelTest : BaseUnitTest() { "1.0.0", UpdateAvailable("Update available to 1.1.0", R.color.color_primary) ), - Plugin("Plugin B", "Author B", "2.0.0", Inactive("Inactive", R.color.color_on_surface_disabled)) + Plugin( + "Plugin B", + "Author B", + "2.0.0", + Inactive("Inactive", R.color.color_on_surface_disabled) + ) ) ) ) @@ -145,4 +158,152 @@ class PluginsViewModelTest : BaseUnitTest() { // THEN assertThat(viewModel.event.value).isEqualTo(Exit) } + + @Test + fun `given plugin status is Inactive, when onPluginClicked is called, then no event is triggered`() = testBlocking { + // GIVEN + val plugin = Plugin( + name = "Plugin A", + authorName = "Author A", + version = "1.0.0", + status = Inactive("Inactive", R.color.color_on_surface_disabled) + ) + val viewModel = createViewModel() + + // WHEN + viewModel.onPluginClicked(plugin) + + // THEN + assertThat(viewModel.event.value).isNull() + } + + @Test + fun `given plugin status is Unknown, when onPluginClicked is called, then no event is triggered`() = testBlocking { + // GIVEN + val plugin = Plugin( + name = "Plugin A", + authorName = "Author A", + version = "1.2.3", + status = Unknown + ) + val viewModel = createViewModel() + + // WHEN + viewModel.onPluginClicked(plugin) + + // THEN + assertThat(viewModel.event.value).isNull() + } + + @Test + fun `given plugin status is UpToDate, when onPluginClicked is called, then no event is triggered`() = testBlocking { + // GIVEN + val plugin = Plugin( + name = "Plugin A", + authorName = "Author A", + version = "1.2.3", + status = UpToDate("Up-to-date", R.color.color_info) + ) + val viewModel = createViewModel() + + // WHEN + viewModel.onPluginClicked(plugin) + + // THEN + assertThat(viewModel.event.value).isNull() + } + + @Test + fun `given store returns success, when onRetryClicked is called, then viewState should be Loaded`() = testBlocking { + // GIVEN + val initialResponse = listOf( + SystemPluginModel( + name = "Plugin A", + authorName = "Author A", + plugin = "plugin.php", + version = "1.0.0", + versionLatest = "1.1.0", + url = "https://example.com/plugins.php", + isActive = true + ) + ) + whenever(wooCommerceStore.fetchSystemPlugins(selectedSite.get())) + .thenReturn(WooResult(initialResponse)) + + val viewModel = createViewModel() + + var values = viewModel.viewState.captureValues() + + val newResponse = listOf( + SystemPluginModel( + name = "Plugin B", + authorName = "Author B", + plugin = "plugin.php", + version = "2.0.0", + versionLatest = "2.1.0", + url = "https://example.com/plugins.php", + isActive = true + ) + ) + whenever(wooCommerceStore.fetchSystemPlugins(selectedSite.get())) + .thenReturn(WooResult(newResponse)) + + // WHEN + viewModel.onRetryClicked() + + // THEN + values = viewModel.viewState.captureValues() + assertThat(values.last()).isEqualTo( + Loaded( + plugins = listOf( + Plugin( + name = "Plugin B", + authorName = "Author B", + version = "2.0.0", + status = UpdateAvailable("Update available to 2.1.0", R.color.color_primary) + ) + ) + ) + ) + } + + @Test + fun `given store returns error, when onRetryClicked is called, then viewState should be Error`() = testBlocking { + // GIVEN + // Return success for the initial load + val initialResponse = listOf( + SystemPluginModel( + name = "Plugin A", + authorName = "Author A", + plugin = "plugin.php", + version = "1.0.0", + versionLatest = "1.1.0", + url = "https://example.com/plugins.php", + isActive = true + ) + ) + whenever(wooCommerceStore.fetchSystemPlugins(selectedSite.get())) + .thenReturn(WooResult(initialResponse)) + + val viewModel = createViewModel() + + var values = viewModel.viewState.captureValues() + + whenever(wooCommerceStore.fetchSystemPlugins(selectedSite.get())) + .thenReturn( + WooResult( + WooError( + type = WooErrorType.GENERIC_ERROR, + original = GenericErrorType.SERVER_ERROR + ) + ) + ) + + // WHEN + viewModel.onRetryClicked() + + // THEN + values = viewModel.viewState.captureValues() + assertThat(values.last()).isEqualTo(PluginsViewState.Error) + } } From d755480214c4421c34325d287d0fad72151aa398 Mon Sep 17 00:00:00 2001 From: AnirudhBhat Date: Wed, 8 Jan 2025 09:55:35 +0530 Subject: [PATCH 14/27] Navigate back to items screen on orders successfully completed --- .../android/ui/woopos/home/WooPosHomeViewModel.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModel.kt index 23e7eabbba2..947b3200ae1 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModel.kt @@ -8,6 +8,7 @@ import com.woocommerce.android.ui.woopos.home.ParentToChildrenEvent.OrderSuccess import com.woocommerce.android.ui.woopos.home.WooPosHomeState.ExitConfirmationDialog import com.woocommerce.android.ui.woopos.home.WooPosHomeState.ProductsInfoDialog import com.woocommerce.android.ui.woopos.home.WooPosHomeState.ScreenPositionState +import com.woocommerce.android.ui.woopos.home.items.navigation.WooPosItemsNavigator import com.woocommerce.android.viewmodel.getStateFlow import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow @@ -20,6 +21,7 @@ import javax.inject.Inject class WooPosHomeViewModel @Inject constructor( private val childrenToParentEventReceiver: WooPosChildrenToParentEventReceiver, private val parentToChildrenEventSender: WooPosParentToChildrenEventSender, + private val wooPosItemsNavigator: WooPosItemsNavigator, savedStateHandle: SavedStateHandle, ) : ViewModel() { private val _state = savedStateHandle.getStateFlow( @@ -200,6 +202,11 @@ class WooPosHomeViewModel @Inject constructor( } private fun onOrderSuccessfullyPaid(paymentMethod: PaymentMethod) { + viewModelScope.launch { + wooPosItemsNavigator.sendNavigationEvent( + WooPosItemsNavigator.WooPosItemsScreenNavigationEvent.NavigateBackToItemListScreen + ) + } _state.value = _state.value.copy( screenPositionState = ScreenPositionState.Checkout.FullScreenTotals ) From 04b09d0a956263f7fc4aab08af422728c2686d00 Mon Sep 17 00:00:00 2001 From: AnirudhBhat Date: Wed, 8 Jan 2025 09:55:54 +0530 Subject: [PATCH 15/27] Remove unnecessary code --- .../android/ui/woopos/home/totals/WooPosTotalsViewModel.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewModel.kt index 5b607f56b42..40da8f0109e 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewModel.kt @@ -28,7 +28,6 @@ import com.woocommerce.android.ui.woopos.home.ParentToChildrenEvent.OrderSuccess import com.woocommerce.android.ui.woopos.home.WooPosChildrenToParentEventSender import com.woocommerce.android.ui.woopos.home.WooPosParentToChildrenEventReceiver import com.woocommerce.android.ui.woopos.home.items.WooPosItemsViewModel -import com.woocommerce.android.ui.woopos.home.items.navigation.WooPosItemsNavigator import com.woocommerce.android.ui.woopos.home.totals.WooPosTotalsViewState.PaymentFailed import com.woocommerce.android.ui.woopos.home.totals.WooPosTotalsViewState.PaymentInProgress import com.woocommerce.android.ui.woopos.util.WooPosNetworkStatus @@ -62,7 +61,6 @@ class WooPosTotalsViewModel @Inject constructor( private val priceFormat: WooPosFormatPrice, private val analyticsTracker: WooPosAnalyticsTracker, private val networkStatus: WooPosNetworkStatus, - private val wooPosItemsNavigator: WooPosItemsNavigator, private val isReceiptSendingSupported: WooPosEmailReceiptIsSendingSupported, private val cardReaderPaymentControllerFactory: CardReaderPaymentControllerFactory, private val uiStringParser: UiStringParser, @@ -305,9 +303,6 @@ class WooPosTotalsViewModel @Inject constructor( } is CardReaderPaymentState.PaymentSuccessful -> { - wooPosItemsNavigator.sendNavigationEvent( - WooPosItemsNavigator.WooPosItemsScreenNavigationEvent.NavigateBackToItemListScreen - ) childrenToParentEventSender.sendToParent(OrderSuccessfullyPaidByCard) } From 8d9ce941b7aa447e49c88d429255b0391e19bd58 Mon Sep 17 00:00:00 2001 From: AnirudhBhat Date: Wed, 8 Jan 2025 09:56:02 +0530 Subject: [PATCH 16/27] Remove unnecessary code in test file --- .../ui/woopos/home/totals/WooPosTotalsViewModelTest.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewModelTest.kt index 08eca2b3a60..5a2071ed68d 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewModelTest.kt @@ -39,7 +39,6 @@ import com.woocommerce.android.ui.woopos.home.ParentToChildrenEvent.OrderSuccess import com.woocommerce.android.ui.woopos.home.WooPosChildrenToParentEventSender import com.woocommerce.android.ui.woopos.home.WooPosParentToChildrenEventReceiver import com.woocommerce.android.ui.woopos.home.items.WooPosItemsViewModel -import com.woocommerce.android.ui.woopos.home.items.navigation.WooPosItemsNavigator import com.woocommerce.android.ui.woopos.util.WooPosCoroutineTestRule import com.woocommerce.android.ui.woopos.util.WooPosNetworkStatus import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent @@ -81,8 +80,6 @@ class WooPosTotalsViewModelTest { private val networkStatus: WooPosNetworkStatus = mock() - private val wooPosItemsNavigator: WooPosItemsNavigator = mock() - private val childrenToParentEventSender: WooPosChildrenToParentEventSender = mock() private val resourceProvider: ResourceProvider = mock() private val cardReaderManager: CardReaderManager = mock() @@ -1251,7 +1248,6 @@ class WooPosTotalsViewModelTest { analyticsTracker = analyticsTracker, networkStatus = networkStatus, cardReaderPaymentControllerFactory = cardReaderPaymentControllerFactory, - wooPosItemsNavigator = wooPosItemsNavigator, isReceiptSendingSupported = isReceiptSendingSupported, uiStringParser = uiStringParser, savedState = savedState, From 885f43297f70d13e2acfc86f9480cb2cf446302f Mon Sep 17 00:00:00 2001 From: AnirudhBhat Date: Wed, 8 Jan 2025 09:56:22 +0530 Subject: [PATCH 17/27] Add test to verify proper navigation event is fired when payment is completed by card --- .../ui/woopos/home/WooPosHomeViewModelTest.kt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModelTest.kt index da236266e64..765656c3dad 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModelTest.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.SavedStateHandle import com.woocommerce.android.R import com.woocommerce.android.ui.woopos.home.ParentToChildrenEvent.OrderSuccessfullyPaid.PaymentMethod import com.woocommerce.android.ui.woopos.home.items.WooPosItemsViewModel.ItemClickedData +import com.woocommerce.android.ui.woopos.home.items.navigation.WooPosItemsNavigator import com.woocommerce.android.ui.woopos.util.WooPosCoroutineTestRule import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableSharedFlow @@ -30,6 +31,7 @@ class WooPosHomeViewModelTest { private val childrenToParentEventReceiver: WooPosChildrenToParentEventReceiver = mock() private val parentToChildrenEventSender: WooPosParentToChildrenEventSender = mock() + private val wooPosItemsNavigator: WooPosItemsNavigator = mock() @Test fun `given state checkout, when SystemBackClicked passed, then BackFromCheckoutToCartClicked event should be sent`() = @@ -398,9 +400,27 @@ class WooPosHomeViewModelTest { .isEqualTo(WooPosHomeState.ScreenPositionState.Checkout.FullScreenTotals) } + @Test + fun `given OrderSuccessfullyPaid by card, then redirect back to items screen`() = + runTest { + // GIVEN + whenever(childrenToParentEventReceiver.events).thenReturn( + flowOf(ChildToParentEvent.OrderSuccessfullyPaidByCard) + ) + + // WHEN + createViewModel() + + // THEN + verify(wooPosItemsNavigator).sendNavigationEvent( + WooPosItemsNavigator.WooPosItemsScreenNavigationEvent.NavigateBackToItemListScreen + ) + } + private fun createViewModel() = WooPosHomeViewModel( childrenToParentEventReceiver, parentToChildrenEventSender, + wooPosItemsNavigator, SavedStateHandle() ) } From ec0ca3c762a9929cefca76e29076621c64ef922b Mon Sep 17 00:00:00 2001 From: AnirudhBhat Date: Wed, 8 Jan 2025 09:58:05 +0530 Subject: [PATCH 18/27] Add test to verify proper navigation event is fired when payment is completed by cash --- .../ui/woopos/home/WooPosHomeViewModelTest.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModelTest.kt index 765656c3dad..2d27b82a186 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModelTest.kt @@ -417,6 +417,23 @@ class WooPosHomeViewModelTest { ) } + @Test + fun `given OrderSuccessfullyPaid by cash, then redirect back to items screen`() = + runTest { + // GIVEN + val events = MutableSharedFlow() + whenever(childrenToParentEventReceiver.events).thenReturn(events) + val viewModel = createViewModel() + + // WHEN + viewModel.onUIEvent(WooPosHomeUIEvent.OnPaymentCompletedViaCash) + + // THEN + verify(wooPosItemsNavigator).sendNavigationEvent( + WooPosItemsNavigator.WooPosItemsScreenNavigationEvent.NavigateBackToItemListScreen + ) + } + private fun createViewModel() = WooPosHomeViewModel( childrenToParentEventReceiver, parentToChildrenEventSender, From 184ac846f095569d7fa2c9671de972d587bb46c7 Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Wed, 8 Jan 2025 11:50:17 +0700 Subject: [PATCH 19/27] Add null check for _binding to prevent crash on certain cases. --- .../ui/orders/list/OrderListFragment.kt | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/list/OrderListFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/list/OrderListFragment.kt index aa4c67d6f4f..dd798bcaf97 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/list/OrderListFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/list/OrderListFragment.kt @@ -415,18 +415,27 @@ class OrderListFragment : } } tracker?.onSaveInstanceState(outState) - viewModel.orderIdAndPositionBackup = - ((binding.orderListView.ordersList.adapter as? OrderListAdapter)?.orderIdAndPosition ?: emptyMap()) - as MutableMap + + _binding?.let { binding -> + val adapter = binding.orderListView.ordersList.adapter as? OrderListAdapter + adapter?.let { + viewModel.orderIdAndPositionBackup = it.orderIdAndPosition + } + } } override fun onViewStateRestored(savedInstanceState: Bundle?) { tracker?.run { onRestoreInstanceState(savedInstanceState) - (binding.orderListView.ordersList.adapter as? OrderListAdapter)?.orderIdAndPosition = - viewModel.orderIdAndPositionBackup - if (hasSelection()) { - setItemsSelected(selection.toList(), true) + + _binding?.let { binding -> + val adapter = binding.orderListView.ordersList.adapter as? OrderListAdapter + adapter?.let { + it.orderIdAndPosition = viewModel.orderIdAndPositionBackup + if (hasSelection()) { + setItemsSelected(selection.toList(), true) + } + } } } From 1572012a87755c1c2d98776415ed561f4e5b43cc Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Wed, 8 Jan 2025 11:50:44 +0700 Subject: [PATCH 20/27] Add null check for accessing orderIdAndPosition --- .../android/ui/orders/OrderSelectionItemKeyProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/OrderSelectionItemKeyProvider.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/OrderSelectionItemKeyProvider.kt index f332adcac4f..b48e6481525 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/OrderSelectionItemKeyProvider.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/OrderSelectionItemKeyProvider.kt @@ -18,5 +18,5 @@ class OrderSelectionItemKeyProvider(private val recyclerView: RecyclerView) : } override fun getPosition(key: Long): Int = - (recyclerView.adapter as? OrderListAdapter)?.orderIdAndPosition[key] ?: RecyclerView.NO_POSITION + (recyclerView.adapter as? OrderListAdapter)?.orderIdAndPosition?.get(key) ?: RecyclerView.NO_POSITION } From 83a3bd1a7978a9b65c5895541b44f37ac8230074 Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Wed, 8 Jan 2025 12:40:10 +0700 Subject: [PATCH 21/27] Refactor to fix `Function onViewStateRestored is nested too deeply.` warning. --- .../android/ui/orders/list/OrderListFragment.kt | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/list/OrderListFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/list/OrderListFragment.kt index dd798bcaf97..0ae116830b6 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/list/OrderListFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/list/OrderListFragment.kt @@ -427,14 +427,10 @@ class OrderListFragment : override fun onViewStateRestored(savedInstanceState: Bundle?) { tracker?.run { onRestoreInstanceState(savedInstanceState) - _binding?.let { binding -> - val adapter = binding.orderListView.ordersList.adapter as? OrderListAdapter - adapter?.let { - it.orderIdAndPosition = viewModel.orderIdAndPositionBackup - if (hasSelection()) { - setItemsSelected(selection.toList(), true) - } + restoreAdapterBulkSelectionState(binding) + if (hasSelection()) { + setItemsSelected(selection.toList(), true) } } } @@ -442,6 +438,13 @@ class OrderListFragment : super.onViewStateRestored(savedInstanceState) } + private fun restoreAdapterBulkSelectionState(binding: FragmentOrderListBinding) { + val adapter = binding.orderListView.ordersList.adapter as? OrderListAdapter + if (adapter != null) { + adapter.orderIdAndPosition = viewModel.orderIdAndPositionBackup + } + } + override fun onDestroyView() { disableSearchListeners() handler.removeCallbacksAndMessages(null) From c3e55ee2ce7ddce1b44c0bf2c0d99c7cc9c1e99b Mon Sep 17 00:00:00 2001 From: Alejo Date: Wed, 8 Jan 2025 17:41:24 -0300 Subject: [PATCH 22/27] add string resources --- WooCommerce/src/main/res/values/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/WooCommerce/src/main/res/values/strings.xml b/WooCommerce/src/main/res/values/strings.xml index 8cc104203e8..76ca64ea2d4 100644 --- a/WooCommerce/src/main/res/values/strings.xml +++ b/WooCommerce/src/main/res/values/strings.xml @@ -1309,6 +1309,8 @@ We currently do not support Tracking for this carrier Something went wrong with this Shipping Label, try again later We can\'t confirm the Shipping Label purchase right now, try again later + %s (default) + Address