From 9c1b3fbdb35cf1eb184a850aaa43f2edd04de921 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Mon, 8 May 2023 14:38:53 +1000 Subject: [PATCH 01/44] Add close account button --- .../AccountSettingsFragment.kt | 19 +++++++ .../components/CloseAccountButton.kt | 50 +++++++++++++++++++ WordPress/src/main/res/values/strings.xml | 1 + 3 files changed, 70 insertions(+) create mode 100644 WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/CloseAccountButton.kt diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt index 8f3995ca627e..a605aa0e3fa9 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt @@ -14,6 +14,8 @@ import android.view.View import android.view.ViewGroup import android.widget.ListView import android.widget.TextView +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.view.ViewCompat import com.google.android.material.snackbar.BaseTransientBottomBar @@ -21,6 +23,7 @@ import com.google.android.material.snackbar.Snackbar import org.wordpress.android.R import org.wordpress.android.WordPress import org.wordpress.android.ui.accounts.signup.BaseUsernameChangerFullScreenDialogFragment +import org.wordpress.android.ui.compose.theme.AppTheme import org.wordpress.android.ui.pages.SnackbarMessageHolder import org.wordpress.android.ui.prefs.DetailListPreference import org.wordpress.android.ui.prefs.EditTextPreferenceWithValidation @@ -41,6 +44,7 @@ import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.C import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.EmailSettingsUiState import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.PrimarySiteSettingsUiState import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.UserNameSettingsUiState +import org.wordpress.android.ui.prefs.accountsettings.components.CloseAccountButton import org.wordpress.android.ui.utils.UiHelpers import org.wordpress.android.util.AppLog import org.wordpress.android.util.AppLog.T.SETTINGS @@ -153,6 +157,21 @@ class AccountSettingsFragment : PreferenceFragmentLifeCycleOwner(), return coordinatorView } + @Deprecated("Deprecated") + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + (view.findViewById(android.R.id.list) as? ListView)?.let { listView -> + listView.addFooterView(ComposeView(context).apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + AppTheme { + CloseAccountButton() + } + } + }) + } + } + @Deprecated("Deprecated") override fun onStart() { super.onStart() diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/CloseAccountButton.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/CloseAccountButton.kt new file mode 100644 index 000000000000..998c8d2d1269 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/CloseAccountButton.kt @@ -0,0 +1,50 @@ +package org.wordpress.android.ui.prefs.accountsettings.components + +import android.content.res.Configuration +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import org.wordpress.android.R +import org.wordpress.android.ui.compose.theme.AppTheme + +@Composable +fun CloseAccountButton(onClick: () -> Unit = {}) = Button( + elevation = ButtonDefaults.elevation( + defaultElevation = 0.dp, + pressedElevation = 0.dp, + ), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.Transparent, + contentColor = MaterialTheme.colors.error, + disabledBackgroundColor = Color.Transparent, + disabledContentColor = MaterialTheme.colors.error, + ), + modifier = Modifier + .fillMaxWidth(), + onClick = onClick, +) { + Text( + text = stringResource(R.string.close_account), + modifier = Modifier + .padding(10.dp), + ) +} + +@Preview +@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +fun PreviewCloseAccountButton() { + AppTheme { + CloseAccountButton() + } +} + diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml index 239c38357cb5..fdd0396cc462 100644 --- a/WordPress/src/main/res/values/strings.xml +++ b/WordPress/src/main/res/values/strings.xml @@ -2754,6 +2754,7 @@ Export email sent! Export your content Your posts, pages, and settings will be emailed to you at %s. + Close Account Plan From 9dd2b373d118a9344cc02b30994dfa8cb50070c4 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Tue, 9 May 2023 08:28:38 +1000 Subject: [PATCH 02/44] Add ui state and methods for account closure dialog --- .../accountsettings/AccountSettingsViewModel.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt index bc5b5ac9eb85..077d053e5f9a 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt @@ -18,6 +18,8 @@ import org.wordpress.android.fluxc.store.AccountStore.AccountErrorType.SETTINGS_ import org.wordpress.android.fluxc.store.AccountStore.OnAccountChanged import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.pages.SnackbarMessageHolder +import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState.Dismissed +import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState.Opened import org.wordpress.android.ui.prefs.accountsettings.usecase.FetchAccountSettingsUseCase import org.wordpress.android.ui.prefs.accountsettings.usecase.GetAccountUseCase import org.wordpress.android.ui.prefs.accountsettings.usecase.GetSitesUseCase @@ -47,6 +49,8 @@ class AccountSettingsViewModel @Inject constructor( private var fetchNewSettingsJob: Job? = null private var _accountSettingsUiState = MutableStateFlow(getAccountSettingsUiState(true)) val accountSettingsUiState: StateFlow = _accountSettingsUiState.asStateFlow() + private var _accountClosureUiState = MutableStateFlow(Dismissed) + val accountClosureUiState: StateFlow = _accountClosureUiState init { viewModelScope.launch { @@ -288,6 +292,18 @@ class AccountSettingsViewModel @Inject constructor( val toastMessage: String? ) + sealed class AccountClosureUiState { + object Dismissed: AccountClosureUiState() + data class Opened(val username: String?): AccountClosureUiState() + } + + fun openAccountClosureDialog() { + _accountClosureUiState.value = Opened(username = getAccountUseCase.account.userName) + } + fun dismissAccountClosureDialog() { + _accountClosureUiState.value = Dismissed + } + override fun onCleared() { pushAccountSettingsUseCase.onCleared() super.onCleared() From 7edd583a07c9a88f570422434967911892885436 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Tue, 9 May 2023 08:32:39 +1000 Subject: [PATCH 03/44] Add account closure dialog This still needs some styling work. --- .../AccountSettingsFragment.kt | 4 +- .../components/AccountClosureDialog.kt | 92 +++++++++++++++++++ .../components/AccountClosureUi.kt | 18 ++++ WordPress/src/main/res/values/strings.xml | 2 + 4 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt create mode 100644 WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt index a605aa0e3fa9..495d2322edf8 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt @@ -44,7 +44,7 @@ import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.C import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.EmailSettingsUiState import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.PrimarySiteSettingsUiState import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.UserNameSettingsUiState -import org.wordpress.android.ui.prefs.accountsettings.components.CloseAccountButton +import org.wordpress.android.ui.prefs.accountsettings.components.AccountClosureUi import org.wordpress.android.ui.utils.UiHelpers import org.wordpress.android.util.AppLog import org.wordpress.android.util.AppLog.T.SETTINGS @@ -165,7 +165,7 @@ class AccountSettingsFragment : PreferenceFragmentLifeCycleOwner(), setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { AppTheme { - CloseAccountButton() + AccountClosureUi(viewModel) } } }) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt new file mode 100644 index 000000000000..68828f3f2725 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt @@ -0,0 +1,92 @@ +package org.wordpress.android.ui.prefs.accountsettings.components + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.Button +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.material.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import org.wordpress.android.R +import org.wordpress.android.ui.compose.theme.AppTheme + +@Composable +fun AccountClosureDialog( + onDismissRequest: () -> Unit, + currentUsername: String, +) { + var username by remember { mutableStateOf("") } + val padding = 10.dp + Dialog(onDismissRequest = onDismissRequest) { + Column( + modifier = Modifier + .background(MaterialTheme.colors.background) + .padding(padding) + ) { + Text( + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center, + text = stringResource(R.string.account_closure_dialog_title), + ) + Text(stringResource(R.string.account_closure_dialog_message)) + TextField( + modifier = Modifier + .padding(vertical = padding) + .fillMaxWidth(), + value = username, + onValueChange = { username = it }, + ) + Row( + modifier = Modifier + .padding(vertical = padding) + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceAround, + ) { + Button( + modifier = Modifier.weight(1f), + onClick = onDismissRequest, + ) { + Text("Cancel") + } + Spacer(Modifier.size(padding)) + Button( + modifier = Modifier.weight(1f), + enabled = username.isNotEmpty() && username == currentUsername, + onClick = {}, + ) { + Text("Confirm") + } + } + } + } +} + +@Preview +@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +fun PreviewAccountClosureDialog() { + AppTheme { + AccountClosureDialog( + onDismissRequest = {}, + currentUsername = "previewUser" + ) + } +} + diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt new file mode 100644 index 000000000000..6ed6b3830fd9 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt @@ -0,0 +1,18 @@ +package org.wordpress.android.ui.prefs.accountsettings.components + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel + +@Composable +fun AccountClosureUi(viewModel: AccountSettingsViewModel) { + val uiState = viewModel.accountClosureUiState.collectAsState() + + CloseAccountButton(onClick = { viewModel.openAccountClosureDialog() }) + (uiState.value as? AccountSettingsViewModel.AccountClosureUiState.Opened)?.username?.let { currentUsername -> + AccountClosureDialog( + onDismissRequest = { viewModel.dismissAccountClosureDialog() }, + currentUsername, + ) + } +} diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml index fdd0396cc462..9143f902d424 100644 --- a/WordPress/src/main/res/values/strings.xml +++ b/WordPress/src/main/res/values/strings.xml @@ -2755,6 +2755,8 @@ Export your content Your posts, pages, and settings will be emailed to you at %s. Close Account + To confirm, please re-enter your username before closing. + Confirm Close Account… Plan From 8fc1bb968c0423f7bdbde0d70b1b33382a33eca7 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Tue, 9 May 2023 14:39:47 +1000 Subject: [PATCH 04/44] Extract flat button components This is to reduce some of the repetition. --- .../accountsettings/components/FlatButtons.kt | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/FlatButtons.kt diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/FlatButtons.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/FlatButtons.kt new file mode 100644 index 000000000000..9580cecea0d2 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/FlatButtons.kt @@ -0,0 +1,54 @@ +package org.wordpress.android.ui.prefs.accountsettings.components + +import androidx.compose.material.Button +import androidx.compose.material.ButtonColors +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.OutlinedButton +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import org.wordpress.android.ui.compose.theme.AppColor + +@Composable +fun FlatButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + colors: ButtonColors = ButtonDefaults.buttonColors( + contentColor = AppColor.White, + ), + enabled: Boolean = true, +) = Button( + modifier = modifier, + onClick = onClick, + colors = colors, + elevation = ButtonDefaults.elevation( + defaultElevation = 0.dp, + pressedElevation = 0.dp, + ), + enabled = enabled, +) { + Text(text) +} + +@Composable +fun FlatOutlinedButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + colors: ButtonColors = ButtonDefaults.buttonColors(), + enabled: Boolean = true, +) = OutlinedButton( + modifier = modifier, + onClick = onClick, + colors = colors, + elevation = ButtonDefaults.elevation( + defaultElevation = 0.dp, + pressedElevation = 0.dp, + ), + enabled = enabled, +) { + Text(text) +} From ea6edb68d4032d36e134d85437f78d2e95a0e686 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Tue, 9 May 2023 14:42:07 +1000 Subject: [PATCH 05/44] Make some minor style adjustments to the dialog --- .../components/AccountClosureDialog.kt | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt index 68828f3f2725..e1706a687ed4 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.Button +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.material.TextField @@ -19,7 +20,9 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -37,13 +40,17 @@ fun AccountClosureDialog( Dialog(onDismissRequest = onDismissRequest) { Column( modifier = Modifier + .clip(shape = RoundedCornerShape(padding)) .background(MaterialTheme.colors.background) - .padding(padding) + .padding(padding), ) { Text( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .padding(bottom = padding), textAlign = TextAlign.Center, text = stringResource(R.string.account_closure_dialog_title), + fontWeight = FontWeight.Bold, ) Text(stringResource(R.string.account_closure_dialog_message)) TextField( @@ -85,8 +92,7 @@ fun PreviewAccountClosureDialog() { AppTheme { AccountClosureDialog( onDismissRequest = {}, - currentUsername = "previewUser" + currentUsername = "previewUser", ) } } - From 6f824ad4c85d0c53c825b820a279d7bfd9cb6814 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Tue, 9 May 2023 14:43:47 +1000 Subject: [PATCH 06/44] Use extracted button components in dialog --- .../components/AccountClosureDialog.kt | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt index e1706a687ed4..7d612242ca86 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt @@ -9,9 +9,10 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.material.Button import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.ButtonDefaults import androidx.compose.material.MaterialTheme +import androidx.compose.material.OutlinedButton import androidx.compose.material.Text import androidx.compose.material.TextField import androidx.compose.runtime.Composable @@ -21,6 +22,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign @@ -66,20 +68,26 @@ fun AccountClosureDialog( .fillMaxWidth(), horizontalArrangement = Arrangement.SpaceAround, ) { - Button( - modifier = Modifier.weight(1f), + FlatOutlinedButton( + text = stringResource(R.string.cancel), onClick = onDismissRequest, - ) { - Text("Cancel") - } + modifier = Modifier.weight(1f), + colors = ButtonDefaults.buttonColors( + contentColor = MaterialTheme.colors.onSurface, + backgroundColor = Color.Transparent, + ), + ) Spacer(Modifier.size(padding)) - Button( + FlatOutlinedButton( + text = stringResource(R.string.confirm), modifier = Modifier.weight(1f), enabled = username.isNotEmpty() && username == currentUsername, onClick = {}, - ) { - Text("Confirm") - } + colors = ButtonDefaults.buttonColors( + contentColor = MaterialTheme.colors.error, + backgroundColor = Color.Transparent, + ), + ) } } } From b3971f7bc4068b1280ffb5a55bd624d971ae2e60 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Tue, 9 May 2023 14:44:23 +1000 Subject: [PATCH 07/44] Add ui state for atomic sites to the model --- .../accountsettings/AccountSettingsViewModel.kt | 17 ++++++++++++++--- .../accountsettings/usecase/GetSitesUseCase.kt | 2 ++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt index 077d053e5f9a..679528bb3477 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt @@ -19,7 +19,8 @@ import org.wordpress.android.fluxc.store.AccountStore.OnAccountChanged import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.pages.SnackbarMessageHolder import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState.Dismissed -import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState.Opened +import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState.Opened.Atomic +import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState.Opened.Default import org.wordpress.android.ui.prefs.accountsettings.usecase.FetchAccountSettingsUseCase import org.wordpress.android.ui.prefs.accountsettings.usecase.GetAccountUseCase import org.wordpress.android.ui.prefs.accountsettings.usecase.GetSitesUseCase @@ -294,11 +295,21 @@ class AccountSettingsViewModel @Inject constructor( sealed class AccountClosureUiState { object Dismissed: AccountClosureUiState() - data class Opened(val username: String?): AccountClosureUiState() + + sealed class Opened: AccountClosureUiState() { + data class Default(val username: String?): Opened() + object Atomic: Opened() + } } fun openAccountClosureDialog() { - _accountClosureUiState.value = Opened(username = getAccountUseCase.account.userName) + launch { + _accountClosureUiState.value = if (getSitesUseCase.getAtomic().isNotEmpty()) { + Atomic + } else { + Default(username = getAccountUseCase.account.userName) + } + } } fun dismissAccountClosureDialog() { _accountClosureUiState.value = Dismissed diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/usecase/GetSitesUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/usecase/GetSitesUseCase.kt index e038552d89b1..25bfd0033554 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/usecase/GetSitesUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/usecase/GetSitesUseCase.kt @@ -15,4 +15,6 @@ class GetSitesUseCase @Inject constructor( suspend fun get(): List = withContext(ioDispatcher) { siteStore.sitesAccessedViaWPComRest } + + suspend fun getAtomic() = get().filter { it.isWPComAtomic } } From 01b77244f8fa11d9f3fe5af66ccdc0c535341881 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Tue, 9 May 2023 14:45:05 +1000 Subject: [PATCH 08/44] Add ineligible dialog This provides a button to contact support when account closure is not immediately possible due to active purchases on the account. --- .../components/IneligibleClosureDialog.kt | 79 +++++++++++++++++++ WordPress/src/main/res/values/strings.xml | 2 + 2 files changed, 81 insertions(+) create mode 100644 WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/IneligibleClosureDialog.kt diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/IneligibleClosureDialog.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/IneligibleClosureDialog.kt new file mode 100644 index 000000000000..97a58ca99631 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/IneligibleClosureDialog.kt @@ -0,0 +1,79 @@ +package org.wordpress.android.ui.prefs.accountsettings.components + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import org.wordpress.android.R +import org.wordpress.android.ui.compose.theme.AppTheme + +@Composable +fun IneligibleClosureDialog( + onDismissRequest: () -> Unit, +) { + val padding = 10.dp + Dialog(onDismissRequest = onDismissRequest) { + Column( + modifier = Modifier + .clip(shape = RoundedCornerShape(padding)) + .background(MaterialTheme.colors.background) + .padding(padding), + ) { + Text( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = padding), + textAlign = TextAlign.Center, + text = stringResource(R.string.account_closure_dialog_title), + fontWeight = FontWeight.Bold, + ) + Text(stringResource(R.string.account_closure_dialog_active_purchases)) + Spacer(Modifier.size(padding)) + FlatOutlinedButton( + text = stringResource(R.string.dismiss), + onClick = onDismissRequest, + modifier = Modifier.fillMaxWidth(), + colors = ButtonDefaults.buttonColors( + contentColor = MaterialTheme.colors.primary, + backgroundColor = Color.Transparent, + ), + ) + Spacer(Modifier.size(padding)) + FlatButton( + text = stringResource(R.string.contact_support), + onClick = {}, + modifier = Modifier.fillMaxWidth(), + ) + } + } +} + +@Preview +@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +fun PreviewIneligibleClosureDialog() { + AppTheme { + IneligibleClosureDialog( + onDismissRequest = {}, + ) + } +} diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml index 9143f902d424..74ea7d7cbb9e 100644 --- a/WordPress/src/main/res/values/strings.xml +++ b/WordPress/src/main/res/values/strings.xml @@ -111,6 +111,7 @@ Update Now Status & Visibility Free + Dismiss Not now Skip @@ -2757,6 +2758,7 @@ Close Account To confirm, please re-enter your username before closing. Confirm Close Account… + This user account cannot be closed immediately because it has active purchases. Please contact our support team to finish deleting the account. Plan From e78ca07ca6e7fa3f20cb87711b6af0d9a34a0c5c Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Tue, 9 May 2023 14:46:53 +1000 Subject: [PATCH 09/44] Add ineligible dialog to the ui --- .../components/AccountClosureUi.kt | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt index 6ed6b3830fd9..12a1d152dca4 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt @@ -3,16 +3,26 @@ package org.wordpress.android.ui.prefs.accountsettings.components import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel +import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState.Opened @Composable fun AccountClosureUi(viewModel: AccountSettingsViewModel) { val uiState = viewModel.accountClosureUiState.collectAsState() CloseAccountButton(onClick = { viewModel.openAccountClosureDialog() }) - (uiState.value as? AccountSettingsViewModel.AccountClosureUiState.Opened)?.username?.let { currentUsername -> - AccountClosureDialog( - onDismissRequest = { viewModel.dismissAccountClosureDialog() }, - currentUsername, - ) + + (uiState.value as? Opened)?.let { + when(it) { + is Opened.Default -> it.username?.let { currentUsername -> + AccountClosureDialog( + onDismissRequest = { viewModel.dismissAccountClosureDialog() }, + currentUsername, + ) + } + + Opened.Atomic -> IneligibleClosureDialog( + onDismissRequest = { viewModel.dismissAccountClosureDialog() }, + ) + } } } From a75a374ed362be9a1dc1bdb1b377ea39e7726692 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Tue, 9 May 2023 15:18:15 +1000 Subject: [PATCH 10/44] Add account closure origin for help requests --- .../java/org/wordpress/android/ui/accounts/HelpActivity.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/HelpActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/HelpActivity.kt index 1550cb5839eb..4cc5c18c3a18 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/HelpActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/HelpActivity.kt @@ -363,7 +363,8 @@ class HelpActivity : LocaleAwareActivity() { JETPACK_MIGRATION_HELP("origin:jetpack-migration-help"), JETPACK_INSTALL_FULL_PLUGIN_ONBOARDING("origin:jp-install-full-plugin-overlay"), JETPACK_INSTALL_FULL_PLUGIN_ERROR("origin:jp-install-full-plugin-error"), - JETPACK_REMOTE_INSTALL_PLUGIN_ERROR("origin:jp-remote-install-plugin-error"); + JETPACK_REMOTE_INSTALL_PLUGIN_ERROR("origin:jp-remote-install-plugin-error"), + ACCOUNT_CLOSURE_DIALOG("origin:account-closure-dialog"); override fun toString(): String { return stringValue From 114db4e69df246f20e88fe1addc8c4eb811d2ba9 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Tue, 9 May 2023 15:19:45 +1000 Subject: [PATCH 11/44] Wire up contact support button to launch HelpActivity --- .../prefs/accountsettings/AccountSettingsFragment.kt | 11 ++++++++++- .../accountsettings/components/AccountClosureUi.kt | 3 ++- .../components/IneligibleClosureDialog.kt | 4 +++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt index 495d2322edf8..45856a6923e5 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt @@ -22,6 +22,8 @@ import com.google.android.material.snackbar.BaseTransientBottomBar import com.google.android.material.snackbar.Snackbar import org.wordpress.android.R import org.wordpress.android.WordPress +import org.wordpress.android.ui.ActivityLauncher +import org.wordpress.android.ui.accounts.HelpActivity import org.wordpress.android.ui.accounts.signup.BaseUsernameChangerFullScreenDialogFragment import org.wordpress.android.ui.compose.theme.AppTheme import org.wordpress.android.ui.pages.SnackbarMessageHolder @@ -165,7 +167,7 @@ class AccountSettingsFragment : PreferenceFragmentLifeCycleOwner(), setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { AppTheme { - AccountClosureUi(viewModel) + AccountClosureUi(viewModel, ::viewHelp) } } }) @@ -368,4 +370,11 @@ class AccountSettingsFragment : PreferenceFragmentLifeCycleOwner(), } } } + + fun viewHelp() = ActivityLauncher.viewHelp( + context, + HelpActivity.Origin.ACCOUNT_CLOSURE_DIALOG, + null, + null, + ) } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt index 12a1d152dca4..817fbb614f91 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt @@ -6,7 +6,7 @@ import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState.Opened @Composable -fun AccountClosureUi(viewModel: AccountSettingsViewModel) { +fun AccountClosureUi(viewModel: AccountSettingsViewModel, onHelpRequested: () -> Unit) { val uiState = viewModel.accountClosureUiState.collectAsState() CloseAccountButton(onClick = { viewModel.openAccountClosureDialog() }) @@ -22,6 +22,7 @@ fun AccountClosureUi(viewModel: AccountSettingsViewModel) { Opened.Atomic -> IneligibleClosureDialog( onDismissRequest = { viewModel.dismissAccountClosureDialog() }, + onHelpRequested = onHelpRequested, ) } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/IneligibleClosureDialog.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/IneligibleClosureDialog.kt index 97a58ca99631..c5e8fbc8e4c0 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/IneligibleClosureDialog.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/IneligibleClosureDialog.kt @@ -29,6 +29,7 @@ import org.wordpress.android.ui.compose.theme.AppTheme @Composable fun IneligibleClosureDialog( onDismissRequest: () -> Unit, + onHelpRequested: () -> Unit, ) { val padding = 10.dp Dialog(onDismissRequest = onDismissRequest) { @@ -60,7 +61,7 @@ fun IneligibleClosureDialog( Spacer(Modifier.size(padding)) FlatButton( text = stringResource(R.string.contact_support), - onClick = {}, + onClick = onHelpRequested, modifier = Modifier.fillMaxWidth(), ) } @@ -74,6 +75,7 @@ fun PreviewIneligibleClosureDialog() { AppTheme { IneligibleClosureDialog( onDismissRequest = {}, + onHelpRequested = {}, ) } } From 8900ab391db5eb2a61174d05f12e2743136993e1 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Tue, 9 May 2023 16:08:29 +1000 Subject: [PATCH 12/44] Add pending state to button --- .../accountsettings/components/FlatButtons.kt | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/FlatButtons.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/FlatButtons.kt index 9580cecea0d2..bf87ea77bc34 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/FlatButtons.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/FlatButtons.kt @@ -1,8 +1,11 @@ package org.wordpress.android.ui.prefs.accountsettings.components +import androidx.compose.foundation.layout.size import androidx.compose.material.Button import androidx.compose.material.ButtonColors import androidx.compose.material.ButtonDefaults +import androidx.compose.material.CircularProgressIndicator +import androidx.compose.material.LocalContentColor import androidx.compose.material.OutlinedButton import androidx.compose.material.Text import androidx.compose.runtime.Composable @@ -40,6 +43,7 @@ fun FlatOutlinedButton( modifier: Modifier = Modifier, colors: ButtonColors = ButtonDefaults.buttonColors(), enabled: Boolean = true, + isPending: Boolean = false, ) = OutlinedButton( modifier = modifier, onClick = onClick, @@ -48,7 +52,14 @@ fun FlatOutlinedButton( defaultElevation = 0.dp, pressedElevation = 0.dp, ), - enabled = enabled, + enabled = enabled && !isPending, ) { - Text(text) + if (isPending) { + CircularProgressIndicator( + strokeWidth = 2.dp, + modifier = Modifier.size(20.dp), + ) + } else { + Text(text) + } } From 0fc22ea04fc12d5eaa02e16d7d0cec50351547da Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Tue, 9 May 2023 16:11:32 +1000 Subject: [PATCH 13/44] Add pending flag to dialog state --- .../ui/prefs/accountsettings/AccountSettingsViewModel.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt index 679528bb3477..a5bb205adb46 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt @@ -297,7 +297,8 @@ class AccountSettingsViewModel @Inject constructor( object Dismissed: AccountClosureUiState() sealed class Opened: AccountClosureUiState() { - data class Default(val username: String?): Opened() + data class Default(val username: String?, val isPending: Boolean = false): Opened() + object Atomic: Opened() } } From 99c8a6c785e836c9acc9e37fa4c5ba6a68864bcf Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Tue, 9 May 2023 16:11:53 +1000 Subject: [PATCH 14/44] Add fake account closure implementation This resumes the normal dialog after a three second delay for the purpose of testing the ui, and will be wired up to the real implementation after the FluxC changes are available. --- .../accountsettings/AccountSettingsViewModel.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt index a5bb205adb46..d5380da504fc 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -316,6 +317,19 @@ class AccountSettingsViewModel @Inject constructor( _accountClosureUiState.value = Dismissed } + fun closeAccount() { + (accountClosureUiState.value as? Default)?.let { uiState -> + _accountClosureUiState.value = uiState.copy(isPending = true) + + launch { + delay(3000) + (accountClosureUiState.value as? Default)?.let { uiState -> + _accountClosureUiState.value = uiState.copy(isPending = false) + } + } + } + } + override fun onCleared() { pushAccountSettingsUseCase.onCleared() super.onCleared() From 1eca8a0bdac408cd73b83862c24dc6e8a2a3d8c7 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Tue, 9 May 2023 16:14:47 +1000 Subject: [PATCH 15/44] Wire up the confirm button to the close account function --- .../accountsettings/components/AccountClosureDialog.kt | 8 ++++++-- .../prefs/accountsettings/components/AccountClosureUi.kt | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt index 7d612242ca86..c715425e2737 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt @@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.ButtonDefaults import androidx.compose.material.MaterialTheme -import androidx.compose.material.OutlinedButton import androidx.compose.material.Text import androidx.compose.material.TextField import androidx.compose.runtime.Composable @@ -36,6 +35,8 @@ import org.wordpress.android.ui.compose.theme.AppTheme fun AccountClosureDialog( onDismissRequest: () -> Unit, currentUsername: String, + onConfirm: () -> Unit, + isPending: Boolean, ) { var username by remember { mutableStateOf("") } val padding = 10.dp @@ -82,7 +83,8 @@ fun AccountClosureDialog( text = stringResource(R.string.confirm), modifier = Modifier.weight(1f), enabled = username.isNotEmpty() && username == currentUsername, - onClick = {}, + isPending = isPending, + onClick = onConfirm, colors = ButtonDefaults.buttonColors( contentColor = MaterialTheme.colors.error, backgroundColor = Color.Transparent, @@ -101,6 +103,8 @@ fun PreviewAccountClosureDialog() { AccountClosureDialog( onDismissRequest = {}, currentUsername = "previewUser", + onConfirm = {}, + isPending = false, ) } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt index 817fbb614f91..a53b8347b8bb 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt @@ -17,6 +17,8 @@ fun AccountClosureUi(viewModel: AccountSettingsViewModel, onHelpRequested: () -> AccountClosureDialog( onDismissRequest = { viewModel.dismissAccountClosureDialog() }, currentUsername, + onConfirm = { viewModel.closeAccount() }, + isPending = it.isPending, ) } From 48dcb30028072df6ed874e1dcde4e0e970e5dd54 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Tue, 9 May 2023 12:29:08 +0200 Subject: [PATCH 16/44] Remove unused imports --- .../ui/prefs/accountsettings/components/AccountClosureDialog.kt | 1 - .../android/ui/prefs/accountsettings/components/FlatButtons.kt | 1 - .../prefs/accountsettings/components/IneligibleClosureDialog.kt | 2 -- 3 files changed, 4 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt index 7d612242ca86..d8cadff59b38 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt @@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.ButtonDefaults import androidx.compose.material.MaterialTheme -import androidx.compose.material.OutlinedButton import androidx.compose.material.Text import androidx.compose.material.TextField import androidx.compose.runtime.Composable diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/FlatButtons.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/FlatButtons.kt index 9580cecea0d2..c8e127a1268d 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/FlatButtons.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/FlatButtons.kt @@ -7,7 +7,6 @@ import androidx.compose.material.OutlinedButton import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import org.wordpress.android.ui.compose.theme.AppColor diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/IneligibleClosureDialog.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/IneligibleClosureDialog.kt index c5e8fbc8e4c0..a94a11894e74 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/IneligibleClosureDialog.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/IneligibleClosureDialog.kt @@ -2,9 +2,7 @@ package org.wordpress.android.ui.prefs.accountsettings.components import android.content.res.Configuration import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding From 05634366447c0a0e571a4dad1d9f06ed48c87b1e Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Wed, 10 May 2023 06:30:31 +1000 Subject: [PATCH 17/44] Remove unnecessary type-guard in fake closeAccount function --- .../ui/prefs/accountsettings/AccountSettingsViewModel.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt index d5380da504fc..8dfb88e34683 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt @@ -323,9 +323,7 @@ class AccountSettingsViewModel @Inject constructor( launch { delay(3000) - (accountClosureUiState.value as? Default)?.let { uiState -> - _accountClosureUiState.value = uiState.copy(isPending = false) - } + _accountClosureUiState.value = uiState.copy(isPending = false) } } } From f478167260753de9a9d1be7db37c70ff33aec038 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Tue, 9 May 2023 22:31:30 +0200 Subject: [PATCH 18/44] Refactor: remove unused imports --- .../android/ui/prefs/accountsettings/components/FlatButtons.kt | 2 -- .../prefs/accountsettings/components/IneligibleClosureDialog.kt | 2 -- 2 files changed, 4 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/FlatButtons.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/FlatButtons.kt index bf87ea77bc34..654e91273f1c 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/FlatButtons.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/FlatButtons.kt @@ -5,12 +5,10 @@ import androidx.compose.material.Button import androidx.compose.material.ButtonColors import androidx.compose.material.ButtonDefaults import androidx.compose.material.CircularProgressIndicator -import androidx.compose.material.LocalContentColor import androidx.compose.material.OutlinedButton import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import org.wordpress.android.ui.compose.theme.AppColor diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/IneligibleClosureDialog.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/IneligibleClosureDialog.kt index c5e8fbc8e4c0..a94a11894e74 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/IneligibleClosureDialog.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/IneligibleClosureDialog.kt @@ -2,9 +2,7 @@ package org.wordpress.android.ui.prefs.accountsettings.components import android.content.res.Configuration import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding From 8bb341fda3faba16e199e8c6880dd133a3f7cacf Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Tue, 9 May 2023 22:32:00 +0200 Subject: [PATCH 19/44] Update: add inline magic number detekt suppression temporarily --- .../android/ui/prefs/accountsettings/AccountSettingsViewModel.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt index 8dfb88e34683..5e708de1edd3 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt @@ -322,6 +322,7 @@ class AccountSettingsViewModel @Inject constructor( _accountClosureUiState.value = uiState.copy(isPending = true) launch { + @Suppress("MagicNumber") delay(3000) _accountClosureUiState.value = uiState.copy(isPending = false) } From 73f9e046a8ec1f02568ffccddb8614d1411dcf07 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Thu, 11 May 2023 16:10:07 +1000 Subject: [PATCH 20/44] Update FluxC version to reference PR artifact --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index fc6a0b13761c..825b049bb042 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ ext { automatticTracksVersion = '2.2.0' gutenbergMobileVersion = 'v1.94.0' wordPressAztecVersion = 'v1.6.3' - wordPressFluxCVersion = 'trunk-2d2bf4a52d3d1bcc434529a3700213c376206f7f' + wordPressFluxCVersion = '2727-b7e4de36a4f5d321a38dad2bc8436ecbec32d571' wordPressLoginVersion = '1.3.0' wordPressPersistentEditTextVersion = '1.0.2' wordPressUtilsVersion = '3.6.1' From cf8f5ed70b9a876b1348b15fd4243d2ef43fb386 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Wed, 10 May 2023 13:35:54 +1000 Subject: [PATCH 21/44] Extract a common dialog component for normal and error flows This will be useful to avoid visual jank from switching dialogs when an error is encountered on the backend. Also, it removes some duplication, and separates concerns. --- .../components/AccountClosureDialog.kt | 86 +-------------- .../components/AccountClosureUi.kt | 30 +++--- .../components/DialogErrorUi.kt | 69 ++++++++++++ .../accountsettings/components/DialogUi.kt | 100 ++++++++++++++++++ .../components/IneligibleClosureDialog.kt | 79 -------------- 5 files changed, 189 insertions(+), 175 deletions(-) create mode 100644 WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogErrorUi.kt create mode 100644 WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogUi.kt delete mode 100644 WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/IneligibleClosureDialog.kt diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt index c715425e2737..3e3013a1d3f4 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureDialog.kt @@ -1,44 +1,22 @@ package org.wordpress.android.ui.prefs.accountsettings.components -import android.content.res.Configuration import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.ButtonDefaults import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.material.TextField import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog -import org.wordpress.android.R -import org.wordpress.android.ui.compose.theme.AppTheme @Composable fun AccountClosureDialog( onDismissRequest: () -> Unit, - currentUsername: String, - onConfirm: () -> Unit, - isPending: Boolean, + content: @Composable ColumnScope.() -> Unit, ) { - var username by remember { mutableStateOf("") } val padding = 10.dp Dialog(onDismissRequest = onDismissRequest) { Column( @@ -46,65 +24,7 @@ fun AccountClosureDialog( .clip(shape = RoundedCornerShape(padding)) .background(MaterialTheme.colors.background) .padding(padding), - ) { - Text( - modifier = Modifier - .fillMaxWidth() - .padding(bottom = padding), - textAlign = TextAlign.Center, - text = stringResource(R.string.account_closure_dialog_title), - fontWeight = FontWeight.Bold, - ) - Text(stringResource(R.string.account_closure_dialog_message)) - TextField( - modifier = Modifier - .padding(vertical = padding) - .fillMaxWidth(), - value = username, - onValueChange = { username = it }, - ) - Row( - modifier = Modifier - .padding(vertical = padding) - .fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceAround, - ) { - FlatOutlinedButton( - text = stringResource(R.string.cancel), - onClick = onDismissRequest, - modifier = Modifier.weight(1f), - colors = ButtonDefaults.buttonColors( - contentColor = MaterialTheme.colors.onSurface, - backgroundColor = Color.Transparent, - ), - ) - Spacer(Modifier.size(padding)) - FlatOutlinedButton( - text = stringResource(R.string.confirm), - modifier = Modifier.weight(1f), - enabled = username.isNotEmpty() && username == currentUsername, - isPending = isPending, - onClick = onConfirm, - colors = ButtonDefaults.buttonColors( - contentColor = MaterialTheme.colors.error, - backgroundColor = Color.Transparent, - ), - ) - } - } - } -} - -@Preview -@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) -@Composable -fun PreviewAccountClosureDialog() { - AppTheme { - AccountClosureDialog( - onDismissRequest = {}, - currentUsername = "previewUser", - onConfirm = {}, - isPending = false, + content = content, ) } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt index a53b8347b8bb..8a5a3edfade2 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt @@ -12,20 +12,24 @@ fun AccountClosureUi(viewModel: AccountSettingsViewModel, onHelpRequested: () -> CloseAccountButton(onClick = { viewModel.openAccountClosureDialog() }) (uiState.value as? Opened)?.let { - when(it) { - is Opened.Default -> it.username?.let { currentUsername -> - AccountClosureDialog( - onDismissRequest = { viewModel.dismissAccountClosureDialog() }, - currentUsername, - onConfirm = { viewModel.closeAccount() }, - isPending = it.isPending, - ) - } + AccountClosureDialog( + onDismissRequest = { viewModel.dismissAccountClosureDialog() }, + ) { + when(it) { + is Opened.Default -> it.username?.let { currentUsername -> + DialogUi( + currentUsername = currentUsername, + isPending = it.isPending, + onCancel = { viewModel.dismissAccountClosureDialog() }, + onConfirm = { viewModel.closeAccount() }, + ) + } - Opened.Atomic -> IneligibleClosureDialog( - onDismissRequest = { viewModel.dismissAccountClosureDialog() }, - onHelpRequested = onHelpRequested, - ) + Opened.Atomic -> DialogErrorUi( + onDismissRequest = { viewModel.dismissAccountClosureDialog() }, + onHelpRequested = onHelpRequested, + ) + } } } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogErrorUi.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogErrorUi.kt new file mode 100644 index 000000000000..57c0dedee10a --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogErrorUi.kt @@ -0,0 +1,69 @@ +package org.wordpress.android.ui.prefs.accountsettings.components + +import android.content.res.Configuration +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import org.wordpress.android.R +import org.wordpress.android.ui.compose.theme.AppTheme + +@Composable +fun DialogErrorUi( + onDismissRequest: () -> Unit, + onHelpRequested: () -> Unit, +) { + val padding = 10.dp + Text( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = padding), + textAlign = TextAlign.Center, + text = stringResource(R.string.account_closure_dialog_title), + fontWeight = FontWeight.Bold, + ) + Text(stringResource(R.string.account_closure_dialog_active_purchases)) + Spacer(Modifier.size(padding)) + FlatOutlinedButton( + text = stringResource(R.string.dismiss), + onClick = onDismissRequest, + modifier = Modifier.fillMaxWidth(), + colors = ButtonDefaults.buttonColors( + contentColor = MaterialTheme.colors.primary, + backgroundColor = Color.Transparent, + ), + ) + Spacer(Modifier.size(padding)) + FlatButton( + text = stringResource(R.string.contact_support), + onClick = onHelpRequested, + modifier = Modifier.fillMaxWidth(), + ) +} + +@Preview +@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +fun PreviewIneligibleClosureDialog() { + AppTheme { + AccountClosureDialog( + onDismissRequest = {}, + ) { + DialogErrorUi( + onDismissRequest = {}, + onHelpRequested = {}, + ) + } + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogUi.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogUi.kt new file mode 100644 index 000000000000..5813ae2d10d5 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogUi.kt @@ -0,0 +1,100 @@ +package org.wordpress.android.ui.prefs.accountsettings.components + +import android.content.res.Configuration +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.material.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import org.wordpress.android.R +import org.wordpress.android.ui.compose.theme.AppTheme + +@Composable +fun DialogUi( + currentUsername: String, + isPending: Boolean, + onCancel: () -> Unit, + onConfirm: () -> Unit, +) { + var username by remember { mutableStateOf("") } + val padding = 10.dp + Text( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = padding), + textAlign = TextAlign.Center, + text = stringResource(R.string.account_closure_dialog_title), + fontWeight = FontWeight.Bold, + ) + Text(stringResource(R.string.account_closure_dialog_message)) + TextField( + modifier = Modifier + .padding(vertical = padding) + .fillMaxWidth(), + value = username, + onValueChange = { username = it }, + ) + Row( + modifier = Modifier + .padding(vertical = padding) + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceAround, + ) { + FlatOutlinedButton( + text = stringResource(R.string.cancel), + onClick = onCancel, + modifier = Modifier.weight(1f), + colors = ButtonDefaults.buttonColors( + contentColor = MaterialTheme.colors.onSurface, + backgroundColor = Color.Transparent, + ), + ) + Spacer(Modifier.size(padding)) + FlatOutlinedButton( + text = stringResource(R.string.confirm), + modifier = Modifier.weight(1f), + enabled = username.isNotEmpty() && username == currentUsername, + isPending = isPending, + onClick = onConfirm, + colors = ButtonDefaults.buttonColors( + contentColor = MaterialTheme.colors.error, + backgroundColor = Color.Transparent, + ), + ) + } +} + +@Preview +@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +fun PreviewAccountClosureDialog() { + AppTheme { + AccountClosureDialog( + onDismissRequest = {}, + ) { + DialogUi( + currentUsername = "previewUser", + isPending = false, + onConfirm = {}, + onCancel = {}, + ) + } + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/IneligibleClosureDialog.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/IneligibleClosureDialog.kt deleted file mode 100644 index a94a11894e74..000000000000 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/IneligibleClosureDialog.kt +++ /dev/null @@ -1,79 +0,0 @@ -package org.wordpress.android.ui.prefs.accountsettings.components - -import android.content.res.Configuration -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.ButtonDefaults -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.Dialog -import org.wordpress.android.R -import org.wordpress.android.ui.compose.theme.AppTheme - -@Composable -fun IneligibleClosureDialog( - onDismissRequest: () -> Unit, - onHelpRequested: () -> Unit, -) { - val padding = 10.dp - Dialog(onDismissRequest = onDismissRequest) { - Column( - modifier = Modifier - .clip(shape = RoundedCornerShape(padding)) - .background(MaterialTheme.colors.background) - .padding(padding), - ) { - Text( - modifier = Modifier - .fillMaxWidth() - .padding(bottom = padding), - textAlign = TextAlign.Center, - text = stringResource(R.string.account_closure_dialog_title), - fontWeight = FontWeight.Bold, - ) - Text(stringResource(R.string.account_closure_dialog_active_purchases)) - Spacer(Modifier.size(padding)) - FlatOutlinedButton( - text = stringResource(R.string.dismiss), - onClick = onDismissRequest, - modifier = Modifier.fillMaxWidth(), - colors = ButtonDefaults.buttonColors( - contentColor = MaterialTheme.colors.primary, - backgroundColor = Color.Transparent, - ), - ) - Spacer(Modifier.size(padding)) - FlatButton( - text = stringResource(R.string.contact_support), - onClick = onHelpRequested, - modifier = Modifier.fillMaxWidth(), - ) - } - } -} - -@Preview -@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) -@Composable -fun PreviewIneligibleClosureDialog() { - AppTheme { - IneligibleClosureDialog( - onDismissRequest = {}, - onHelpRequested = {}, - ) - } -} From dd105e21b36cb0005ebb196ed092e9ba4940358f Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Thu, 11 May 2023 15:37:49 +1000 Subject: [PATCH 22/44] Disable cancel button while account closure is pending --- .../android/ui/prefs/accountsettings/components/DialogUi.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogUi.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogUi.kt index 5813ae2d10d5..f4f41a3477f0 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogUi.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogUi.kt @@ -64,7 +64,9 @@ fun DialogUi( colors = ButtonDefaults.buttonColors( contentColor = MaterialTheme.colors.onSurface, backgroundColor = Color.Transparent, + disabledContentColor = MaterialTheme.colors.onSurface, ), + isPending = isPending, ) Spacer(Modifier.size(padding)) FlatOutlinedButton( From 780799d58bfbfa69bb16f5026c83ca79de93b407 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Thu, 11 May 2023 15:56:33 +1000 Subject: [PATCH 23/44] Add account closure error message strings --- WordPress/src/main/res/values/strings.xml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml index 7929e3793e9e..916cbf73a18f 100644 --- a/WordPress/src/main/res/values/strings.xml +++ b/WordPress/src/main/res/values/strings.xml @@ -2759,7 +2759,13 @@ Close Account To confirm, please re-enter your username before closing. Confirm Close Account… - This user account cannot be closed immediately because it has active purchases. Please contact our support team to finish deleting the account. + Couldn\'t close account automatically" + You\'re not authorized to close the account. + This user account cannot be closed immediately because it has active purchases. Please contact our support team to finish deleting the account. + This user account cannot be closed if there are unresolved chargebacks. + This user account cannot be closed while it has active subscriptions. + This user account cannot be closed while it has active purchases. + An error occurred while closing account. Plan From f84e2721f461a0c830fb229e876d5f15f22d7899 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Thu, 11 May 2023 16:40:16 +1000 Subject: [PATCH 24/44] Add dialog error ui --- .../components/DialogErrorUi.kt | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogErrorUi.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogErrorUi.kt index 57c0dedee10a..da774b41a0e3 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogErrorUi.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogErrorUi.kt @@ -17,23 +17,40 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.wordpress.android.R +import org.wordpress.android.fluxc.network.rest.wpcom.account.CloseAccountResult.ErrorType +import org.wordpress.android.fluxc.network.rest.wpcom.account.CloseAccountResult.ErrorType.UNAUTHORIZED +import org.wordpress.android.fluxc.network.rest.wpcom.account.CloseAccountResult.ErrorType.ATOMIC_SITE +import org.wordpress.android.fluxc.network.rest.wpcom.account.CloseAccountResult.ErrorType.CHARGEBACKED_SITE +import org.wordpress.android.fluxc.network.rest.wpcom.account.CloseAccountResult.ErrorType.ACTIVE_SUBSCRIPTIONS +import org.wordpress.android.fluxc.network.rest.wpcom.account.CloseAccountResult.ErrorType.ACTIVE_MEMBERSHIPS +import org.wordpress.android.fluxc.network.rest.wpcom.account.CloseAccountResult.ErrorType.INVALID_TOKEN +import org.wordpress.android.fluxc.network.rest.wpcom.account.CloseAccountResult.ErrorType.UNKNOWN import org.wordpress.android.ui.compose.theme.AppTheme @Composable fun DialogErrorUi( onDismissRequest: () -> Unit, onHelpRequested: () -> Unit, + errorType: ErrorType, ) { val padding = 10.dp + val messageId = when(errorType) { + UNAUTHORIZED -> R.string.account_closure_dialog_error_unauthorized + ATOMIC_SITE -> R.string.account_closure_dialog_error_atomic_site + CHARGEBACKED_SITE -> R.string.account_closure_dialog_error_chargebacked_site + ACTIVE_SUBSCRIPTIONS -> R.string.account_closure_dialog_error_active_subscriptions + ACTIVE_MEMBERSHIPS -> R.string.account_closure_dialog_error_active_memberships + INVALID_TOKEN, UNKNOWN -> R.string.account_closure_dialog_error_unknown + } Text( modifier = Modifier .fillMaxWidth() .padding(bottom = padding), textAlign = TextAlign.Center, - text = stringResource(R.string.account_closure_dialog_title), + text = stringResource(R.string.account_closure_dialog_error_title), fontWeight = FontWeight.Bold, ) - Text(stringResource(R.string.account_closure_dialog_active_purchases)) + Text(stringResource(messageId)) Spacer(Modifier.size(padding)) FlatOutlinedButton( text = stringResource(R.string.dismiss), @@ -63,6 +80,7 @@ fun PreviewIneligibleClosureDialog() { DialogErrorUi( onDismissRequest = {}, onHelpRequested = {}, + errorType = ATOMIC_SITE, ) } } From 8bc21dbbb78cea08757edc7f95ca0c83431a4138 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Thu, 11 May 2023 16:43:28 +1000 Subject: [PATCH 25/44] Add error handling to the view model --- .../AccountSettingsViewModel.kt | 29 +++++++++++++------ .../components/AccountClosureUi.kt | 3 +- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt index 5e708de1edd3..cce03ea01f18 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt @@ -5,13 +5,15 @@ import androidx.lifecycle.viewModelScope import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Job -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import org.wordpress.android.R +import org.wordpress.android.fluxc.network.rest.wpcom.account.AccountRestClient +import org.wordpress.android.fluxc.network.rest.wpcom.account.CloseAccountResult +import org.wordpress.android.fluxc.network.rest.wpcom.account.closeAccount import org.wordpress.android.fluxc.store.AccountStore.AccountError import org.wordpress.android.fluxc.store.AccountStore.AccountErrorType.SETTINGS_FETCH_GENERIC_ERROR import org.wordpress.android.fluxc.store.AccountStore.AccountErrorType.SETTINGS_FETCH_REAUTHORIZATION_REQUIRED_ERROR @@ -20,7 +22,7 @@ import org.wordpress.android.fluxc.store.AccountStore.OnAccountChanged import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.pages.SnackbarMessageHolder import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState.Dismissed -import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState.Opened.Atomic +import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState.Opened.Error import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState.Opened.Default import org.wordpress.android.ui.prefs.accountsettings.usecase.FetchAccountSettingsUseCase import org.wordpress.android.ui.prefs.accountsettings.usecase.GetAccountUseCase @@ -46,7 +48,8 @@ class AccountSettingsViewModel @Inject constructor( private val pushAccountSettingsUseCase: PushAccountSettingsUseCase, private val getAccountUseCase: GetAccountUseCase, private val getSitesUseCase: GetSitesUseCase, - private val optimisticUpdateHandler: AccountSettingsOptimisticUpdateHandler + private val optimisticUpdateHandler: AccountSettingsOptimisticUpdateHandler, + private val accountRestClient: AccountRestClient, ) : ScopedViewModel(mainDispatcher) { private var fetchNewSettingsJob: Job? = null private var _accountSettingsUiState = MutableStateFlow(getAccountSettingsUiState(true)) @@ -299,15 +302,14 @@ class AccountSettingsViewModel @Inject constructor( sealed class Opened: AccountClosureUiState() { data class Default(val username: String?, val isPending: Boolean = false): Opened() - - object Atomic: Opened() + data class Error(val errorType: CloseAccountResult.ErrorType): Opened() } } fun openAccountClosureDialog() { launch { _accountClosureUiState.value = if (getSitesUseCase.getAtomic().isNotEmpty()) { - Atomic + Error(CloseAccountResult.ErrorType.ATOMIC_SITE) } else { Default(username = getAccountUseCase.account.userName) } @@ -322,9 +324,18 @@ class AccountSettingsViewModel @Inject constructor( _accountClosureUiState.value = uiState.copy(isPending = true) launch { - @Suppress("MagicNumber") - delay(3000) - _accountClosureUiState.value = uiState.copy(isPending = false) + accountRestClient.closeAccount( + onResult = { + when(it) { + is CloseAccountResult.Success -> { + _accountClosureUiState.value = Dismissed + } + is CloseAccountResult.Failure -> { + _accountClosureUiState.value = Error(it.error.errorType) + } + } + } + ) } } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt index 8a5a3edfade2..87f0a0a04fae 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt @@ -25,9 +25,10 @@ fun AccountClosureUi(viewModel: AccountSettingsViewModel, onHelpRequested: () -> ) } - Opened.Atomic -> DialogErrorUi( + is Opened.Error -> DialogErrorUi( onDismissRequest = { viewModel.dismissAccountClosureDialog() }, onHelpRequested = onHelpRequested, + it.errorType, ) } } From f4d8ec090e76fb306c25860cc2b7f5c359ad4bff Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Thu, 11 May 2023 17:11:38 +1000 Subject: [PATCH 26/44] Add string for successful account closure --- WordPress/src/main/res/values/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml index 916cbf73a18f..060a689b67c3 100644 --- a/WordPress/src/main/res/values/strings.xml +++ b/WordPress/src/main/res/values/strings.xml @@ -2766,6 +2766,7 @@ This user account cannot be closed while it has active subscriptions. This user account cannot be closed while it has active purchases. An error occurred while closing account. + Account closed. Plan From 74ca2b538f5ca3229bf740bf498ce935d879c14d Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Thu, 11 May 2023 17:12:59 +1000 Subject: [PATCH 27/44] Add dialog success ui --- .../components/DialogSuccessUi.kt | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogSuccessUi.kt diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogSuccessUi.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogSuccessUi.kt new file mode 100644 index 000000000000..4ad07c9635b5 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogSuccessUi.kt @@ -0,0 +1,57 @@ +package org.wordpress.android.ui.prefs.accountsettings.components + +import android.content.res.Configuration +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import org.wordpress.android.R +import org.wordpress.android.ui.compose.theme.AppTheme + +@Composable +fun DialogSuccessUi( + onDismissRequest: () -> Unit, +) { + val padding = 10.dp + Text( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = padding), + textAlign = TextAlign.Center, + text = stringResource(R.string.account_closure_dialog_success_message), + fontWeight = FontWeight.Bold, + ) + FlatOutlinedButton( + text = stringResource(R.string.ok), + onClick = onDismissRequest, + modifier = Modifier.fillMaxWidth(), + colors = ButtonDefaults.buttonColors( + contentColor = MaterialTheme.colors.primary, + backgroundColor = Color.Transparent, + ), + ) +} + +@Preview +@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +fun PreviewDialogSuccessUi() { + AppTheme { + AccountClosureDialog( + onDismissRequest = {}, + ) { + DialogSuccessUi( + onDismissRequest = {}, + ) + } + } +} From a99b3016a67396fde33ac7873cad89d6cd32c34d Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Thu, 11 May 2023 17:13:43 +1000 Subject: [PATCH 28/44] Log user out after successful account closure This should also take care of removing the local app data associated with the user account. --- .../ui/prefs/accountsettings/AccountSettingsFragment.kt | 6 +++++- .../ui/prefs/accountsettings/AccountSettingsViewModel.kt | 1 + .../prefs/accountsettings/components/AccountClosureUi.kt | 9 ++++++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt index 45856a6923e5..9f1df9199a40 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt @@ -167,7 +167,7 @@ class AccountSettingsFragment : PreferenceFragmentLifeCycleOwner(), setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { AppTheme { - AccountClosureUi(viewModel, ::viewHelp) + AccountClosureUi(viewModel, ::viewHelp, ::signOut) } } }) @@ -377,4 +377,8 @@ class AccountSettingsFragment : PreferenceFragmentLifeCycleOwner(), null, null, ) + + fun signOut() { + (activity.application as? WordPress)?.wordPressComSignOut() + } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt index cce03ea01f18..246232f4e6ef 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt @@ -303,6 +303,7 @@ class AccountSettingsViewModel @Inject constructor( sealed class Opened: AccountClosureUiState() { data class Default(val username: String?, val isPending: Boolean = false): Opened() data class Error(val errorType: CloseAccountResult.ErrorType): Opened() + object Success: Opened() } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt index 87f0a0a04fae..7d72974b17d0 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt @@ -6,7 +6,11 @@ import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState.Opened @Composable -fun AccountClosureUi(viewModel: AccountSettingsViewModel, onHelpRequested: () -> Unit) { +fun AccountClosureUi( + viewModel: AccountSettingsViewModel, + onHelpRequested: () -> Unit, + onAccountClosed: () -> Unit, +) { val uiState = viewModel.accountClosureUiState.collectAsState() CloseAccountButton(onClick = { viewModel.openAccountClosureDialog() }) @@ -30,6 +34,9 @@ fun AccountClosureUi(viewModel: AccountSettingsViewModel, onHelpRequested: () -> onHelpRequested = onHelpRequested, it.errorType, ) + is Opened.Success -> DialogSuccessUi( + onDismissRequest = onAccountClosed + ) } } } From c96d5310eda1cbcde2269abdc54bb740588d999f Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Thu, 11 May 2023 17:15:56 +1000 Subject: [PATCH 29/44] Fix the names of composable preview functions --- .../ui/prefs/accountsettings/components/DialogErrorUi.kt | 2 +- .../android/ui/prefs/accountsettings/components/DialogUi.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogErrorUi.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogErrorUi.kt index da774b41a0e3..4db61e0eeccf 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogErrorUi.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogErrorUi.kt @@ -72,7 +72,7 @@ fun DialogErrorUi( @Preview @Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable -fun PreviewIneligibleClosureDialog() { +fun PreviewDialogErrorUi() { AppTheme { AccountClosureDialog( onDismissRequest = {}, diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogUi.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogUi.kt index f4f41a3477f0..9d7a72e97fee 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogUi.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogUi.kt @@ -86,7 +86,7 @@ fun DialogUi( @Preview @Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable -fun PreviewAccountClosureDialog() { +fun PreviewDialogUi() { AppTheme { AccountClosureDialog( onDismissRequest = {}, From 8118fa444b204a542a320df0f99bdde779053b0d Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Thu, 11 May 2023 17:22:18 +1000 Subject: [PATCH 30/44] Make cancel button disabled without spinner while pending --- .../android/ui/prefs/accountsettings/components/DialogUi.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogUi.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogUi.kt index 9d7a72e97fee..de7806818814 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogUi.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogUi.kt @@ -66,7 +66,7 @@ fun DialogUi( backgroundColor = Color.Transparent, disabledContentColor = MaterialTheme.colors.onSurface, ), - isPending = isPending, + enabled = !isPending, ) Spacer(Modifier.size(padding)) FlatOutlinedButton( From d1323efdfb6b88e6863abfb6509a12d2f8ff6198 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Thu, 11 May 2023 20:11:22 +1000 Subject: [PATCH 31/44] Refactor user events to make logout smoother --- .../AccountSettingsFragment.kt | 28 ++++++++++++--- .../AccountSettingsViewModel.kt | 34 +++++++++++++++++-- .../components/AccountClosureUi.kt | 12 +++---- 3 files changed, 61 insertions(+), 13 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt index 9f1df9199a40..23a99b5d25d3 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt @@ -18,8 +18,11 @@ import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.view.ViewCompat +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.repeatOnLifecycle import com.google.android.material.snackbar.BaseTransientBottomBar import com.google.android.material.snackbar.Snackbar +import kotlinx.coroutines.launch import org.wordpress.android.R import org.wordpress.android.WordPress import org.wordpress.android.ui.ActivityLauncher @@ -46,6 +49,7 @@ import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.C import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.EmailSettingsUiState import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.PrimarySiteSettingsUiState import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.UserNameSettingsUiState +import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.Companion.AccountClosureAction import org.wordpress.android.ui.prefs.accountsettings.components.AccountClosureUi import org.wordpress.android.ui.utils.UiHelpers import org.wordpress.android.util.AppLog @@ -167,7 +171,7 @@ class AccountSettingsFragment : PreferenceFragmentLifeCycleOwner(), setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { AppTheme { - AccountClosureUi(viewModel, ::viewHelp, ::signOut) + AccountClosureUi(viewModel) } } }) @@ -184,6 +188,11 @@ class AccountSettingsFragment : PreferenceFragmentLifeCycleOwner(), this.lifecycleScope.launchWhenStarted { viewModel.accountSettingsUiState.collect { updateAccountSettings(it) } } + this.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.userActionEvents.collect { handleUserAction(it) } + } + } } private fun updateAccountSettings(accountSettingsUiState: AccountSettingsUiState) { @@ -371,14 +380,25 @@ class AccountSettingsFragment : PreferenceFragmentLifeCycleOwner(), } } - fun viewHelp() = ActivityLauncher.viewHelp( + private fun handleUserAction(action: AccountClosureAction) { + when (action) { + AccountClosureAction.HELP_VIEWED -> viewHelp() + AccountClosureAction.ACCOUNT_CLOSED -> signOut() + AccountClosureAction.USER_LOGGED_OUT -> { + ActivityLauncher.showMainActivity(context, true) + } + } + } + private fun viewHelp() = ActivityLauncher.viewHelp( context, HelpActivity.Origin.ACCOUNT_CLOSURE_DIALOG, null, null, ) - fun signOut() { - (activity.application as? WordPress)?.wordPressComSignOut() + private fun signOut() { + (activity.application as? WordPress)?.let { + viewModel.signOutWordPress(it) + } } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt index 246232f4e6ef..509d48de4d83 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt @@ -5,12 +5,16 @@ import androidx.lifecycle.viewModelScope import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.wordpress.android.R +import org.wordpress.android.WordPress import org.wordpress.android.fluxc.network.rest.wpcom.account.AccountRestClient import org.wordpress.android.fluxc.network.rest.wpcom.account.CloseAccountResult import org.wordpress.android.fluxc.network.rest.wpcom.account.closeAccount @@ -19,11 +23,13 @@ import org.wordpress.android.fluxc.store.AccountStore.AccountErrorType.SETTINGS_ import org.wordpress.android.fluxc.store.AccountStore.AccountErrorType.SETTINGS_FETCH_REAUTHORIZATION_REQUIRED_ERROR import org.wordpress.android.fluxc.store.AccountStore.AccountErrorType.SETTINGS_POST_ERROR import org.wordpress.android.fluxc.store.AccountStore.OnAccountChanged +import org.wordpress.android.modules.BG_THREAD import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.pages.SnackbarMessageHolder import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState.Dismissed import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState.Opened.Error import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState.Opened.Default +import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState.Opened.Success import org.wordpress.android.ui.prefs.accountsettings.usecase.FetchAccountSettingsUseCase import org.wordpress.android.ui.prefs.accountsettings.usecase.GetAccountUseCase import org.wordpress.android.ui.prefs.accountsettings.usecase.GetSitesUseCase @@ -44,6 +50,7 @@ class AccountSettingsViewModel @Inject constructor( private val resourceProvider: ResourceProvider, networkUtilsWrapper: NetworkUtilsWrapper, @Named(UI_THREAD) private val mainDispatcher: CoroutineDispatcher, + @Named(BG_THREAD) private val bgDispatcher: CoroutineDispatcher, private val fetchAccountSettingsUseCase: FetchAccountSettingsUseCase, private val pushAccountSettingsUseCase: PushAccountSettingsUseCase, private val getAccountUseCase: GetAccountUseCase, @@ -56,6 +63,8 @@ class AccountSettingsViewModel @Inject constructor( val accountSettingsUiState: StateFlow = _accountSettingsUiState.asStateFlow() private var _accountClosureUiState = MutableStateFlow(Dismissed) val accountClosureUiState: StateFlow = _accountClosureUiState + private var _userActionEvents = MutableSharedFlow() + val userActionEvents: SharedFlow = _userActionEvents init { viewModelScope.launch { @@ -309,7 +318,7 @@ class AccountSettingsViewModel @Inject constructor( fun openAccountClosureDialog() { launch { - _accountClosureUiState.value = if (getSitesUseCase.getAtomic().isNotEmpty()) { + _accountClosureUiState.value = if (false) { Error(CloseAccountResult.ErrorType.ATOMIC_SITE) } else { Default(username = getAccountUseCase.account.userName) @@ -329,7 +338,7 @@ class AccountSettingsViewModel @Inject constructor( onResult = { when(it) { is CloseAccountResult.Success -> { - _accountClosureUiState.value = Dismissed + _accountClosureUiState.value = Success } is CloseAccountResult.Failure -> { _accountClosureUiState.value = Error(it.error.errorType) @@ -341,8 +350,29 @@ class AccountSettingsViewModel @Inject constructor( } } + fun signOutWordPress(application: WordPress) { + launch { + withContext(bgDispatcher) { + application.wordPressComSignOut() + userAction(AccountClosureAction.USER_LOGGED_OUT) + } + } + } + + fun userAction(action: AccountClosureAction) { + launch { + _userActionEvents.emit(action) + } + } + override fun onCleared() { pushAccountSettingsUseCase.onCleared() super.onCleared() } + + companion object { + enum class AccountClosureAction { + HELP_VIEWED, ACCOUNT_CLOSED, USER_LOGGED_OUT; + } + } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt index 7d72974b17d0..4a595644280b 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/AccountClosureUi.kt @@ -4,13 +4,11 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState.Opened +import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.Companion.AccountClosureAction.ACCOUNT_CLOSED +import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.Companion.AccountClosureAction.HELP_VIEWED @Composable -fun AccountClosureUi( - viewModel: AccountSettingsViewModel, - onHelpRequested: () -> Unit, - onAccountClosed: () -> Unit, -) { +fun AccountClosureUi(viewModel: AccountSettingsViewModel) { val uiState = viewModel.accountClosureUiState.collectAsState() CloseAccountButton(onClick = { viewModel.openAccountClosureDialog() }) @@ -31,11 +29,11 @@ fun AccountClosureUi( is Opened.Error -> DialogErrorUi( onDismissRequest = { viewModel.dismissAccountClosureDialog() }, - onHelpRequested = onHelpRequested, + onHelpRequested = { viewModel.userAction(HELP_VIEWED) }, it.errorType, ) is Opened.Success -> DialogSuccessUi( - onDismissRequest = onAccountClosed + onDismissRequest = { viewModel.userAction(ACCOUNT_CLOSED) } ) } } From 939b22cd6bb996e1890ea2f7832eb2ae49c09184 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Thu, 11 May 2023 20:54:23 +1000 Subject: [PATCH 32/44] Autofocus username input when account closure dialog opens --- .../ui/prefs/accountsettings/components/DialogUi.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogUi.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogUi.kt index de7806818814..d10ce9ca8d1a 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogUi.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/components/DialogUi.kt @@ -12,11 +12,14 @@ import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.material.TextField 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.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight @@ -34,6 +37,7 @@ fun DialogUi( onConfirm: () -> Unit, ) { var username by remember { mutableStateOf("") } + val focusRequester = remember { FocusRequester() } val padding = 10.dp Text( modifier = Modifier @@ -47,7 +51,8 @@ fun DialogUi( TextField( modifier = Modifier .padding(vertical = padding) - .fillMaxWidth(), + .fillMaxWidth() + .focusRequester(focusRequester), value = username, onValueChange = { username = it }, ) @@ -81,6 +86,10 @@ fun DialogUi( ), ) } + + LaunchedEffect(Unit) { + focusRequester.requestFocus() + } } @Preview From b6bcdd9436ce8d2149775c12cb4403939222248d Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Tue, 16 May 2023 13:46:45 +1000 Subject: [PATCH 33/44] Add account closure analytics events --- .../org/wordpress/android/analytics/AnalyticsTracker.java | 2 ++ .../wordpress/android/analytics/AnalyticsTrackerNosara.java | 4 ++++ 2 files changed, 6 insertions(+) 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 62b39abc9ebf..d1c3e752b8b6 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 @@ -377,6 +377,8 @@ public enum Stat { // This stat is part of a funnel that provides critical information. Before // making ANY modification to this stat please refer to: p4qSXL-35X-p2 CREATED_ACCOUNT, + CLOSE_ACCOUNT_FAILED, + CLOSED_ACCOUNT, ACCOUNT_LOGOUT, SHARED_ITEM, SHARED_ITEM_READER, 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 2badec984577..9148758029dc 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 @@ -1037,6 +1037,10 @@ public static String getEventNameForStat(AnalyticsTracker.Stat stat) { // This stat is part of a funnel that provides critical information. Before // making ANY modification to this stat please refer to: p4qSXL-35X-p2 return "account_created"; + case CLOSE_ACCOUNT_FAILED: + return "close_account_failed"; + case CLOSED_ACCOUNT: + return "closed_account"; case SHARED_ITEM: return "item_shared"; case SHARED_ITEM_READER: From 58af67dcddac7ade3c1cbc7612c4abdd11d8bdf8 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Tue, 16 May 2023 13:51:57 +1000 Subject: [PATCH 34/44] Add account closure tracking functions --- .../AccountSettingsAnalyticsTracker.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsAnalyticsTracker.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsAnalyticsTracker.kt index 2d790c8c4f1c..85fff677cf5b 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsAnalyticsTracker.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsAnalyticsTracker.kt @@ -1,6 +1,8 @@ package org.wordpress.android.ui.prefs.accountsettings import org.wordpress.android.analytics.AnalyticsTracker.Stat +import org.wordpress.android.analytics.AnalyticsTracker.Stat.CLOSED_ACCOUNT +import org.wordpress.android.analytics.AnalyticsTracker.Stat.CLOSE_ACCOUNT_FAILED import org.wordpress.android.analytics.AnalyticsTracker.Stat.SETTINGS_DID_CHANGE import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsEvent.EMAIL_CHANGED import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsEvent.PASSWORD_CHANGED @@ -17,6 +19,7 @@ private const val SOURCE_ACCOUNT_SETTINGS = "account_settings" private const val TRACK_PROPERTY_FIELD_NAME = "field_name" private const val TRACK_PROPERTY_PAGE = "page" private const val TRACK_PROPERTY_PAGE_ACCOUNT_SETTINGS = "account_settings" +private const val KEY_ACCOUNT_CLOSURE_ERROR_CODE = "error_code" enum class AccountSettingsEvent(val trackProperty: String? = null) { EMAIL_CHANGED("email"), @@ -50,4 +53,16 @@ class AccountSettingsAnalyticsTracker @Inject constructor(private val analyticsT props[SOURCE] = SOURCE_ACCOUNT_SETTINGS analyticsTracker.track(stat, props) } + + fun trackAccountClosureFailure(errorCode: String?) { + mutableMapOf().apply { + put(KEY_ACCOUNT_CLOSURE_ERROR_CODE, errorCode ?: "unknown") + }.let { props -> + analyticsTracker.track(CLOSE_ACCOUNT_FAILED, props) + } + } + + fun trackAccountClosureSuccess() { + analyticsTracker.track(CLOSED_ACCOUNT) + } } From d8dea834da7b404f37d767e31e3ba7a4bec2ceeb Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Tue, 16 May 2023 13:53:47 +1000 Subject: [PATCH 35/44] Track success and failure of account closure --- .../AccountSettingsFragment.kt | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt index 23a99b5d25d3..550eb0758aa6 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsFragment.kt @@ -45,6 +45,7 @@ import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsEvent.USERN import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsEvent.USERNAME_CHANGE_SCREEN_DISPLAYED import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsEvent.WEB_ADDRESS_CHANGED import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountSettingsUiState +import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.ChangePasswordSettingsUiState import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.EmailSettingsUiState import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.PrimarySiteSettingsUiState @@ -100,6 +101,7 @@ class AccountSettingsFragment : PreferenceFragmentLifeCycleOwner(), addPreferencesFromResource(R.xml.account_settings) bindPreferences() setUpListeners() + observeAccountClosureEvents() emailPreference.configure( inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS, validationType = EMAIL @@ -127,6 +129,30 @@ class AccountSettingsFragment : PreferenceFragmentLifeCycleOwner(), changePasswordPreference.summary = EMPTY_STRING } + private fun observeAccountClosureEvents() { + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.userActionEvents.collect { handleUserAction(it) } + } + } + lifecycleScope.launch { + // Using `CREATED` state here prevents tracking duplicate events + repeatOnLifecycle(Lifecycle.State.CREATED) { + viewModel.accountClosureUiState.collect { + when (it) { + is AccountClosureUiState.Opened.Error -> { + analyticsTracker.trackAccountClosureFailure(it.errorType.token) + } + is AccountClosureUiState.Opened.Success -> { + analyticsTracker.trackAccountClosureSuccess() + } + else -> {} + } + } + } + } + } + private fun setUpListeners() { usernamePreference.onPreferenceClickListener = this@AccountSettingsFragment primarySitePreference.onPreferenceChangeListener = this@AccountSettingsFragment @@ -188,11 +214,6 @@ class AccountSettingsFragment : PreferenceFragmentLifeCycleOwner(), this.lifecycleScope.launchWhenStarted { viewModel.accountSettingsUiState.collect { updateAccountSettings(it) } } - this.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.userActionEvents.collect { handleUserAction(it) } - } - } } private fun updateAccountSettings(accountSettingsUiState: AccountSettingsUiState) { From 15cd55c1f7868fa0573c34bf89f75037f29f33ee Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Wed, 17 May 2023 14:50:38 +1000 Subject: [PATCH 36/44] Re-enable pre-flight check for atomic sites This was hard-coded as false to help with testing other flows. --- .../ui/prefs/accountsettings/AccountSettingsViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt index 509d48de4d83..0f3eaee2d829 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt @@ -318,7 +318,7 @@ class AccountSettingsViewModel @Inject constructor( fun openAccountClosureDialog() { launch { - _accountClosureUiState.value = if (false) { + _accountClosureUiState.value = if (getSitesUseCase.getAtomic().isNotEmpty()) { Error(CloseAccountResult.ErrorType.ATOMIC_SITE) } else { Default(username = getAccountUseCase.account.userName) From 4d0eba1d2b1f3f2fb8aa5737bed004ce0a8fbba2 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Wed, 17 May 2023 16:26:55 +1000 Subject: [PATCH 37/44] Update FluxC reference --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index cac0e6418ca4..a14832568cc9 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ ext { automatticTracksVersion = '2.2.0' gutenbergMobileVersion = 'v1.95.0' wordPressAztecVersion = 'v1.6.3' - wordPressFluxCVersion = '2727-b7e4de36a4f5d321a38dad2bc8436ecbec32d571' + wordPressFluxCVersion = 'trunk-3fe318a6de3463bf2444b0d798067546e9a18db0' wordPressLoginVersion = '1.3.0' wordPressPersistentEditTextVersion = '1.0.2' wordPressUtilsVersion = '3.6.1' From f3fab89d24cf58f3a89f41b846b3e7c137263156 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Wed, 17 May 2023 19:58:31 +0200 Subject: [PATCH 38/44] Fix compilation issues in account settings vm --- .../ui/prefs/accountsettings/AccountSettingsViewModelTest.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModelTest.kt index 8c148c9aa906..abeb02b627f8 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModelTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModelTest.kt @@ -437,11 +437,13 @@ class AccountSettingsViewModelTest : BaseUnitTest() { resourceProvider, networkUtilsWrapper, testDispatcher(), + testDispatcher(), fetchAccountSettingsUseCase, pushAccountSettingsUseCase, getAccountUseCase, getSitesUseCase, - optimisticUpdateHandler + optimisticUpdateHandler, + mock(), ) } From 488dc3fe6de38d55268e7d2206c7f5cef34bd267 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Thu, 18 May 2023 08:23:21 +1000 Subject: [PATCH 39/44] Add test for getSitesUseCase --- .../accountsettings/GetSitesUseCaseTest.kt | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/GetSitesUseCaseTest.kt diff --git a/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/GetSitesUseCaseTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/GetSitesUseCaseTest.kt new file mode 100644 index 000000000000..1514c7080106 --- /dev/null +++ b/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/GetSitesUseCaseTest.kt @@ -0,0 +1,48 @@ +package org.wordpress.android.ui.prefs.accountsettings + +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.mock +import org.mockito.kotlin.whenever +import org.wordpress.android.BaseUnitTest +import org.wordpress.android.fluxc.model.SiteModel +import org.wordpress.android.fluxc.store.SiteStore +import org.wordpress.android.ui.prefs.accountsettings.usecase.GetSitesUseCase + +@ExperimentalCoroutinesApi +class GetSitesUseCaseTest: BaseUnitTest() { + + private lateinit var useCase: GetSitesUseCase + + @Mock + private lateinit var siteStore: SiteStore + + val mockAtomicSite: SiteModel = mock() + val mockNonAtomicSite: SiteModel = mock() + + @Before + fun setUp() = test { + useCase = GetSitesUseCase( + testDispatcher(), + siteStore, + ) + whenever(mockAtomicSite.isWPComAtomic).thenReturn(true) + whenever(mockNonAtomicSite.isWPComAtomic).thenReturn(false) + whenever(siteStore.sitesAccessedViaWPComRest).thenReturn(listOf( + mockAtomicSite, + mockNonAtomicSite, + )) + } + + @Test + fun `getAtomic filters for Atomic sites`() { + test { + val atomicSites = useCase.getAtomic() + assertThat(atomicSites.size).isEqualTo(1) + assertThat(atomicSites.first()).isEqualTo(mockAtomicSite) + } + } +} From 19d7cc6ca058d87a5107ab26a565181d15a1e82e Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Thu, 18 May 2023 08:27:07 +1000 Subject: [PATCH 40/44] Fix typo in test name --- .../ui/prefs/accountsettings/AccountSettingsViewModelTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModelTest.kt index abeb02b627f8..a18542ca40c2 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModelTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModelTest.kt @@ -80,7 +80,7 @@ class AccountSettingsViewModelTest : BaseUnitTest() { } @Test - fun `The initial primarysite is shown from cached account settings`() = test { + fun `The initial primary site is shown from cached account settings`() = test { uiState.primarySiteSettingsUiState.primarySite?.siteId?.let { assertThat(it).isEqualTo(getAccountUseCase.account.primarySiteId) } From 93f9741b49c1d1b8572effd0057abc442a70accd Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Thu, 18 May 2023 10:29:12 +1000 Subject: [PATCH 41/44] Extract account closure function to a use case class This is useful for mocking the implementation in unit tests --- .../accountsettings/AccountSettingsViewModel.kt | 7 +++---- .../accountsettings/usecase/AccountClosureUseCase.kt | 12 ++++++++++++ .../accountsettings/AccountSettingsViewModelTest.kt | 5 ++++- 3 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/usecase/AccountClosureUseCase.kt diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt index 0f3eaee2d829..743af4cdb045 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModel.kt @@ -15,9 +15,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.wordpress.android.R import org.wordpress.android.WordPress -import org.wordpress.android.fluxc.network.rest.wpcom.account.AccountRestClient import org.wordpress.android.fluxc.network.rest.wpcom.account.CloseAccountResult -import org.wordpress.android.fluxc.network.rest.wpcom.account.closeAccount import org.wordpress.android.fluxc.store.AccountStore.AccountError import org.wordpress.android.fluxc.store.AccountStore.AccountErrorType.SETTINGS_FETCH_GENERIC_ERROR import org.wordpress.android.fluxc.store.AccountStore.AccountErrorType.SETTINGS_FETCH_REAUTHORIZATION_REQUIRED_ERROR @@ -30,6 +28,7 @@ import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.A import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState.Opened.Error import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState.Opened.Default import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState.Opened.Success +import org.wordpress.android.ui.prefs.accountsettings.usecase.AccountClosureUseCase import org.wordpress.android.ui.prefs.accountsettings.usecase.FetchAccountSettingsUseCase import org.wordpress.android.ui.prefs.accountsettings.usecase.GetAccountUseCase import org.wordpress.android.ui.prefs.accountsettings.usecase.GetSitesUseCase @@ -56,7 +55,7 @@ class AccountSettingsViewModel @Inject constructor( private val getAccountUseCase: GetAccountUseCase, private val getSitesUseCase: GetSitesUseCase, private val optimisticUpdateHandler: AccountSettingsOptimisticUpdateHandler, - private val accountRestClient: AccountRestClient, + private val accountClosureUseCase: AccountClosureUseCase, ) : ScopedViewModel(mainDispatcher) { private var fetchNewSettingsJob: Job? = null private var _accountSettingsUiState = MutableStateFlow(getAccountSettingsUiState(true)) @@ -334,7 +333,7 @@ class AccountSettingsViewModel @Inject constructor( _accountClosureUiState.value = uiState.copy(isPending = true) launch { - accountRestClient.closeAccount( + accountClosureUseCase.closeAccount( onResult = { when(it) { is CloseAccountResult.Success -> { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/usecase/AccountClosureUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/usecase/AccountClosureUseCase.kt new file mode 100644 index 000000000000..a1a93f413a9a --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/accountsettings/usecase/AccountClosureUseCase.kt @@ -0,0 +1,12 @@ +package org.wordpress.android.ui.prefs.accountsettings.usecase + +import org.wordpress.android.fluxc.network.rest.wpcom.account.AccountRestClient +import org.wordpress.android.fluxc.network.rest.wpcom.account.CloseAccountResult +import org.wordpress.android.fluxc.network.rest.wpcom.account.closeAccount +import javax.inject.Inject + +class AccountClosureUseCase @Inject constructor( + private val accountRestClient: AccountRestClient, +) { + fun closeAccount(onResult: (CloseAccountResult) -> Unit) = accountRestClient.closeAccount(onResult) +} diff --git a/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModelTest.kt index a18542ca40c2..44953cdf4fdf 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModelTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModelTest.kt @@ -54,6 +54,9 @@ class AccountSettingsViewModelTest : BaseUnitTest() { @Mock private lateinit var account: AccountModel + @Mock + lateinit var accountClosureUseCase: AccountClosureUseCase + private val siteViewModels = mutableListOf().apply { add(SiteUiModel("HappyDay", 1L, "http://happyday.wordpress.com")) add(SiteUiModel("WonderLand", 2L, "http://wonderland.wordpress.com")) @@ -443,7 +446,7 @@ class AccountSettingsViewModelTest : BaseUnitTest() { getAccountUseCase, getSitesUseCase, optimisticUpdateHandler, - mock(), + accountClosureUseCase, ) } From 6c216d58c99a306b2143f289502b51b86bdf1376 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Thu, 18 May 2023 10:31:07 +1000 Subject: [PATCH 42/44] Add unit tests for account closure ui state --- .../AccountSettingsViewModelTest.kt | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModelTest.kt index 44953cdf4fdf..f10122ea79c7 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModelTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/AccountSettingsViewModelTest.kt @@ -1,5 +1,6 @@ package org.wordpress.android.ui.prefs.accountsettings +import junit.framework.TestCase.assertTrue import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.toList @@ -8,17 +9,21 @@ 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.R.string import org.wordpress.android.fluxc.model.AccountModel import org.wordpress.android.fluxc.model.SiteModel +import org.wordpress.android.fluxc.network.rest.wpcom.account.CloseAccountResult import org.wordpress.android.fluxc.store.AccountStore.AccountError import org.wordpress.android.fluxc.store.AccountStore.AccountErrorType.GENERIC_ERROR import org.wordpress.android.fluxc.store.AccountStore.OnAccountChanged +import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountClosureUiState import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.AccountSettingsUiState import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel.SiteUiModel +import org.wordpress.android.ui.prefs.accountsettings.usecase.AccountClosureUseCase import org.wordpress.android.ui.prefs.accountsettings.usecase.FetchAccountSettingsUseCase import org.wordpress.android.ui.prefs.accountsettings.usecase.GetAccountUseCase import org.wordpress.android.ui.prefs.accountsettings.usecase.GetSitesUseCase @@ -410,6 +415,31 @@ class AccountSettingsViewModelTest : BaseUnitTest() { .isEqualTo(false) } + @Test + fun `When account closure succeeds, then the closure dialog should be in the success state`() = test { + mockAccountClosureWithResult(CloseAccountResult.Success) + viewModel.closeAccount() + assertTrue(viewModel.accountClosureUiState.value is AccountClosureUiState.Opened.Success) + } + + @Test + fun `When account closure fails, then the closure dialog should be in the error state`() = test { + mockAccountClosureWithResult(CloseAccountResult.Failure(CloseAccountResult.Error( + CloseAccountResult.ErrorType.UNKNOWN, + "unknown", + ))) + viewModel.closeAccount() + assertTrue(viewModel.accountClosureUiState.value is AccountClosureUiState.Opened.Error) + } + + @Test + fun `When there is an Atomic site, then the closure dialog should open in the error state`() = test { + val mockAtomicSite: SiteModel = mock() + whenever(getSitesUseCase.getAtomic()).thenReturn(listOf(mockAtomicSite)) + viewModel.openAccountClosureDialog() + assertTrue(viewModel.accountClosureUiState.value is AccountClosureUiState.Opened.Error) + } + // Helper Methods private fun testUiStateChanges( block: suspend CoroutineScope.() -> T @@ -450,6 +480,15 @@ class AccountSettingsViewModelTest : BaseUnitTest() { ) } + private suspend fun mockAccountClosureWithResult(result: CloseAccountResult) { + whenever(getSitesUseCase.getAtomic()).thenReturn(emptyList()) + whenever(accountClosureUseCase.closeAccount(any())).thenAnswer { + val completion = it.getArgument<((CloseAccountResult) -> Unit)>(0) + completion.invoke(result) + } + viewModel.openAccountClosureDialog() + } + private suspend fun mockSites(siteViewModels: List) { val sites = siteViewModels.map { SiteModel().apply { From bdf5833a2fff66ea97ff7170228a921b979d2b1d Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Thu, 18 May 2023 12:20:10 +0300 Subject: [PATCH 43/44] Removes empty line to fix lint issue --- .../android/ui/prefs/accountsettings/GetSitesUseCaseTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/GetSitesUseCaseTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/GetSitesUseCaseTest.kt index 1514c7080106..7075baa7da40 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/GetSitesUseCaseTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/prefs/accountsettings/GetSitesUseCaseTest.kt @@ -14,7 +14,6 @@ import org.wordpress.android.ui.prefs.accountsettings.usecase.GetSitesUseCase @ExperimentalCoroutinesApi class GetSitesUseCaseTest: BaseUnitTest() { - private lateinit var useCase: GetSitesUseCase @Mock From 6e4955644118e73a8dd30eb1ba4193f394eec06c Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Fri, 19 May 2023 13:32:15 +1000 Subject: [PATCH 44/44] Add release note --- RELEASE-NOTES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 212a6e0f5f39..479bc3f21d4a 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -2,7 +2,7 @@ 22.5 ----- - +* [*] Adds a button to enable account closure from the account settings screen [https://github.com/wordpress-mobile/WordPress-Android/pull/18412] 22.4 -----