From a14f93ab82d530bbacac8fcc9c19eb6b65ef7155 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 2 Jun 2024 16:02:04 +0800 Subject: [PATCH] Updated on 2026-08-14 --- .../features/details/redux/DetailsAction.kt | 4 +- .../features/details/redux/DetailsReducer.kt | 25 ++++--- .../features/details/redux/DetailsState.kt | 12 ++- .../details/ui/resetcard/ResetCardFragment.kt | 11 +-- .../details/ui/resetcard/ResetCardScreen.kt | 72 +++++++++++------- .../ui/resetcard/ResetCardScreenState.kt | 49 ++++++++++-- .../ui/resetcard/ResetCardViewModel.kt | 75 +++++++++++++++---- core/res/src/main/res/values-ru/strings.xml | 6 ++ core/res/src/main/res/values/strings.xml | 6 ++ 9 files changed, 192 insertions(+), 68 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt index 05c586372d..cc3efe720c 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt @@ -26,7 +26,9 @@ sealed class DetailsAction : Action { data object Failure : ResetToFactory() data object Success : ResetToFactory() - data class LastWarningDialogVisibility(val isShown: Boolean) : ResetToFactory() + data class ShowDialog(val dialog: CardSettingsState.Dialog) : ResetToFactory() + + data object DismissDialog : ResetToFactory() } data object ScanCard : DetailsAction() diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index 117b13438c..d8f6637f7f 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -1,5 +1,6 @@ package com.tangem.tap.features.details.redux +import com.tangem.core.navigation.AppScreen import com.tangem.domain.apptheme.model.AppThemeMode import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.util.cardTypesResolver @@ -25,7 +26,7 @@ private fun internalReduce(action: Action, state: AppState): DetailsState { val detailsState = state.detailsState return when (action) { is DetailsAction.PrepareScreen -> { - handlePrepareScreen(action) + handlePrepareScreen(action, state) } is DetailsAction.PrepareCardSettingsData -> { handlePrepareCardSettingsScreen( @@ -67,9 +68,15 @@ private fun internalReduce(action: Action, state: AppState): DetailsState { } } -private fun handlePrepareScreen(action: DetailsAction.PrepareScreen): DetailsState { +private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: AppState): DetailsState { return DetailsState( scanResponse = action.scanResponse, + // If the current screen is ResetToFactory, we must save the ResetToFactory's state + cardSettingsState = if (store.state.navigationState.backStack.lastOrNull() == AppScreen.ResetToFactory) { + state.detailsState.cardSettingsState + } else { + null + }, createBackupAllowed = action.scanResponse.card.backupStatus == CardDTO.BackupStatus.NoBackup, appSettingsState = AppSettingsState( isBiometricsAvailable = tangemSdkManager.canUseBiometry, @@ -115,7 +122,7 @@ private fun handlePrepareCardSettingsScreen( null }, isShowPasswordResetRadioButton = isShowPasswordResetRadioButton, - isLastWarningDialogShown = false, + dialog = null, ) return state.copy(cardSettingsState = cardSettingsState) } @@ -183,14 +190,12 @@ private fun handleEraseWallet(action: DetailsAction.ResetToFactory, state: Detai ), ) } - is DetailsAction.ResetToFactory.LastWarningDialogVisibility -> { - state.copy( - cardSettingsState = cardSettingsState?.copy( - isLastWarningDialogShown = action.isShown, - ), - ) + is DetailsAction.ResetToFactory.ShowDialog -> { + state.copy(cardSettingsState = cardSettingsState?.copy(dialog = action.dialog)) + } + is DetailsAction.ResetToFactory.DismissDialog -> { + state.copy(cardSettingsState = cardSettingsState?.copy(dialog = null)) } - else -> state } } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt index e1c523e759..2c77d5806e 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt @@ -46,8 +46,16 @@ data class CardSettingsState( val condition2Checked: Boolean, val accessCodeRecovery: AccessCodeRecoveryState? = null, val isShowPasswordResetRadioButton: Boolean, - val isLastWarningDialogShown: Boolean, -) + val dialog: Dialog?, +) { + + sealed interface Dialog { + data object StartResetDialog : Dialog + data object ContinueResetDialog : Dialog + data object InterruptedResetDialog : Dialog + data object CompletedResetDialog : Dialog + } +} data class ManageSecurityState( val currentOption: SecurityOption = SecurityOption.LongTap, diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardFragment.kt index 5affcc7167..74424cd8ba 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardFragment.kt @@ -1,16 +1,16 @@ package com.tangem.tap.features.details.ui.resetcard import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.Modifier import androidx.fragment.app.viewModels +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.core.navigation.NavigationAction import com.tangem.core.ui.UiDependencies import com.tangem.core.ui.screen.ComposeFragment import com.tangem.tap.features.details.redux.DetailsState import com.tangem.tap.store import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.MutableStateFlow import org.rekotlin.StoreSubscriber import javax.inject.Inject @@ -22,13 +22,14 @@ internal class ResetCardFragment : ComposeFragment(), StoreSubscriber = - mutableStateOf(ResetCardScreenState.InitialState) + private var screenState = MutableStateFlow(ResetCardScreenState.InitialState) @Composable override fun ScreenContent(modifier: Modifier) { + val state = screenState.collectAsStateWithLifecycle().value + ResetCardScreen( - state = screenState.value, + state = state, onBackClick = { store.dispatch(NavigationAction.PopBackTo()) }, modifier = modifier, ) diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt index b0ccd93897..85bec44728 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt @@ -4,22 +4,23 @@ import android.content.res.Configuration import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.* import androidx.compose.foundation.layout.* -import androidx.compose.material.Icon -import androidx.compose.material.IconToggleButton -import androidx.compose.material.Text -import androidx.compose.runtime.* +import androidx.compose.material3.Icon +import androidx.compose.material3.IconToggleButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import com.tangem.core.ui.components.* -import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview import com.tangem.tap.features.details.ui.cardsettings.TextReference import com.tangem.tap.features.details.ui.cardsettings.resolveReference import com.tangem.tap.features.details.ui.common.DetailsMainButton import com.tangem.tap.features.details.ui.common.SettingsScreensScaffold import com.tangem.wallet.R +import com.tangem.tap.features.details.ui.resetcard.ResetCardScreenState.ResetCardScreenContent.Dialog as ResetCardDialog @Composable internal fun ResetCardScreen(state: ResetCardScreenState, onBackClick: () -> Unit, modifier: Modifier = Modifier) { @@ -36,7 +37,14 @@ internal fun ResetCardScreen(state: ResetCardScreenState, onBackClick: () -> Uni onBackClick = onBackClick, ) - LastWarningDialog(state = state) + when (val dialog = (state as? ResetCardScreenState.ResetCardScreenContent)?.dialog) { + is ResetCardDialog.StartReset, + is ResetCardDialog.ContinueReset, + is ResetCardDialog.InterruptedReset, + -> CommonResetDialog(dialog = dialog) + is ResetCardDialog.CompletedReset -> CompletedResetDialog(dialog = dialog) + null -> Unit + } } @Composable @@ -184,23 +192,35 @@ private fun ResetButton(enabled: Boolean, onResetButtonClick: () -> Unit) { } @Composable -private fun LastWarningDialog(state: ResetCardScreenState) { - if (state is ResetCardScreenState.ResetCardScreenContent && state.lastWarningDialog.isShown) { - BasicDialog( - title = stringResource(id = R.string.common_attention), - message = stringResource(id = R.string.card_settings_action_sheet_title), - dismissButton = DialogButton( - title = stringResource(id = R.string.card_settings_action_sheet_reset), - warning = true, - onClick = state.lastWarningDialog.onResetButtonClick, - ), - confirmButton = DialogButton( - title = stringResource(id = R.string.common_cancel), - onClick = state.lastWarningDialog.onDismiss, - ), - onDismissDialog = state.lastWarningDialog.onDismiss, - ) - } +private fun CommonResetDialog(dialog: ResetCardScreenState.ResetCardScreenContent.Dialog) { + BasicDialog( + title = stringResource(dialog.titleResId), + message = stringResource(dialog.messageResId), + dismissButton = DialogButton( + title = stringResource(id = R.string.common_cancel), + onClick = dialog.onDismiss, + ), + confirmButton = DialogButton( + title = stringResource(id = R.string.card_settings_action_sheet_reset), + warning = true, + onClick = dialog.onConfirmClick, + ), + onDismissDialog = dialog.onDismiss, + ) +} + +@Composable +private fun CompletedResetDialog(dialog: ResetCardDialog) { + BasicDialog( + title = stringResource(id = dialog.titleResId), + message = stringResource(id = dialog.messageResId), + confirmButton = DialogButton( + title = stringResource(id = R.string.common_ok), + onClick = dialog.onConfirmClick, + ), + onDismissDialog = {}, + isDismissable = false, + ) } // region Preview @@ -218,11 +238,7 @@ private fun ResetCardScreenSample(modifier: Modifier = Modifier) { onAcceptCondition1ToggleClick = {}, onAcceptCondition2ToggleClick = {}, onResetButtonClick = {}, - lastWarningDialog = ResetCardScreenState.ResetCardScreenContent.LastWarningDialog( - isShown = false, - onResetButtonClick = {}, - onDismiss = {}, - ), + dialog = null, ), onBackClick = {}, ) diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreenState.kt b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreenState.kt index d70071ed49..0bcff503bc 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreenState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreenState.kt @@ -1,6 +1,8 @@ package com.tangem.tap.features.details.ui.resetcard +import androidx.annotation.StringRes import com.tangem.tap.features.details.ui.cardsettings.TextReference +import com.tangem.wallet.R internal sealed class ResetCardScreenState { @@ -15,16 +17,51 @@ internal sealed class ResetCardScreenState { val onAcceptCondition1ToggleClick: (Boolean) -> Unit, val onAcceptCondition2ToggleClick: (Boolean) -> Unit, val onResetButtonClick: () -> Unit, - val lastWarningDialog: LastWarningDialog, + val dialog: Dialog? = null, ) : ResetCardScreenState() { val resetButtonEnabled: Boolean get() = accepted - data class LastWarningDialog( - val isShown: Boolean, - val onResetButtonClick: () -> Unit, - val onDismiss: () -> Unit, - ) + sealed class Dialog( + @StringRes val titleResId: Int, + @StringRes val messageResId: Int, + ) { + + abstract val onConfirmClick: () -> Unit + abstract val onDismiss: () -> Unit + + data class StartReset( + override val onConfirmClick: () -> Unit, + override val onDismiss: () -> Unit, + ) : Dialog( + titleResId = R.string.common_attention, + messageResId = R.string.card_settings_action_sheet_title, + ) + + data class ContinueReset( + override val onConfirmClick: () -> Unit, + override val onDismiss: () -> Unit, + ) : Dialog( + titleResId = R.string.card_settings_continue_reset_alert_title, + messageResId = R.string.card_settings_continue_reset_alert_message, + ) + + data class InterruptedReset( + override val onConfirmClick: () -> Unit, + override val onDismiss: () -> Unit, + ) : Dialog( + titleResId = R.string.card_settings_interrupted_reset_alert_title, + messageResId = R.string.card_settings_interrupted_reset_alert_message, + ) + + data class CompletedReset(override val onConfirmClick: () -> Unit) : Dialog( + titleResId = R.string.card_settings_completed_reset_alert_title, + messageResId = R.string.card_settings_completed_reset_alert_message, + ) { + + override val onDismiss: () -> Unit = {} + } + } } internal enum class WarningsToReset { diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardViewModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardViewModel.kt index e5cac17cb5..47dd7a0286 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardViewModel.kt @@ -10,7 +10,7 @@ import com.tangem.domain.wallets.usecase.DeleteWalletUseCase import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.tap.common.extensions.onUserWalletSelected import com.tangem.tap.features.details.redux.CardSettingsState -import com.tangem.tap.features.details.redux.DetailsAction +import com.tangem.tap.features.details.redux.DetailsAction.ResetToFactory import com.tangem.tap.features.details.ui.cardsettings.TextReference import com.tangem.tap.features.details.ui.resetcard.featuretoggles.ResetCardFeatureToggles import com.tangem.tap.features.details.ui.utils.toResetCardDescriptionText @@ -18,6 +18,7 @@ import com.tangem.tap.store import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import javax.inject.Inject +import com.tangem.tap.features.details.redux.CardSettingsState.Dialog as CardSettingsDialog @HiltViewModel internal class ResetCardViewModel @Inject constructor( @@ -47,28 +48,48 @@ internal class ResetCardViewModel @Inject constructor( warningsToShow = warningsToShow, acceptCondition1Checked = state?.condition1Checked ?: false, acceptCondition2Checked = state?.condition2Checked ?: false, - onAcceptCondition1ToggleClick = { store.dispatch(DetailsAction.ResetToFactory.AcceptCondition1(it)) }, - onAcceptCondition2ToggleClick = { store.dispatch(DetailsAction.ResetToFactory.AcceptCondition2(it)) }, - onResetButtonClick = { showLastWarningDialog() }, - lastWarningDialog = ResetCardScreenState.ResetCardScreenContent.LastWarningDialog( - isShown = state?.isLastWarningDialogShown ?: false, - onResetButtonClick = ::onLastWarningDialogResetClicked, - onDismiss = ::onLastWarningDialogDismiss, - ), + onAcceptCondition1ToggleClick = { store.dispatch(ResetToFactory.AcceptCondition1(it)) }, + onAcceptCondition2ToggleClick = { store.dispatch(ResetToFactory.AcceptCondition2(it)) }, + onResetButtonClick = { showDialog(CardSettingsDialog.StartResetDialog) }, + dialog = state?.dialog?.let(::createDialog), ) } - private fun showLastWarningDialog() { - store.dispatch(DetailsAction.ResetToFactory.LastWarningDialogVisibility(isShown = true)) + private fun createDialog(dialog: CardSettingsDialog): ResetCardScreenState.ResetCardScreenContent.Dialog { + return when (dialog) { + CardSettingsDialog.StartResetDialog -> { + ResetCardScreenState.ResetCardScreenContent.Dialog.StartReset( + onConfirmClick = ::onStartResetClick, + onDismiss = ::dismissDialog, + ) + } + CardSettingsDialog.ContinueResetDialog -> { + ResetCardScreenState.ResetCardScreenContent.Dialog.ContinueReset( + onConfirmClick = ::onContinueResetClick, + onDismiss = ::onContinueResetDialogDismiss, + ) + } + CardSettingsDialog.InterruptedResetDialog -> { + ResetCardScreenState.ResetCardScreenContent.Dialog.InterruptedReset( + onConfirmClick = ::onContinueResetClick, + onDismiss = ::dismissAndFinishFullReset, + ) + } + CardSettingsDialog.CompletedResetDialog -> { + ResetCardScreenState.ResetCardScreenContent.Dialog.CompletedReset( + onConfirmClick = ::dismissAndFinishFullReset, + ) + } + } } - private fun onLastWarningDialogResetClicked() { - store.dispatch(DetailsAction.ResetToFactory.LastWarningDialogVisibility(isShown = false)) + private fun onStartResetClick() { + dismissDialog() if (resetCardFeatureToggles.isFullResetEnabled) { makeFullReset() } else { - store.dispatch(DetailsAction.ResetToFactory.Proceed) + store.dispatch(ResetToFactory.Proceed) } } @@ -85,13 +106,35 @@ internal class ResetCardViewModel @Inject constructor( } } + private fun onContinueResetClick() { + dismissDialog() + + // TODO: [REDACTED_TASK_KEY] + } + + private fun onContinueResetDialogDismiss() { + dismissDialog() + + showDialog(CardSettingsDialog.InterruptedResetDialog) + } + private suspend fun resetCurrentCard(userWallet: UserWallet) = either { resetCardUseCase(userWallet.scanResponse.card).bind() deleteSavedAccessCodesUseCase(userWallet.cardId).bind() deleteWalletUseCase(userWallet.walletId).bind() } - private fun onLastWarningDialogDismiss() { - store.dispatch(DetailsAction.ResetToFactory.LastWarningDialogVisibility(isShown = false)) + private fun dismissAndFinishFullReset() { + dismissDialog() + + // TODO: [REDACTED_TASK_KEY] + } + + private fun showDialog(dialog: CardSettingsDialog) { + store.dispatch(ResetToFactory.ShowDialog(dialog)) + } + + private fun dismissDialog() { + store.dispatch(ResetToFactory.DismissDialog) } } \ No newline at end of file diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 37ebbc73ba..1d192d5741 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -48,9 +48,15 @@ Вы уверены, что хотите это сделать? Смена кода доступа Код доступа будет изменен только на данной карте + Все карты выбранного кошелька сброшены до заводских настроек, вы можете создать новый кошелек + Сброс завершён + Хотите сбросить следующую карту от этого кошелька? + Сброс карты Заводские настройки Тип безопасности Настройки карты + Рекомендуем завершить процесс сброса всех карт кошелька + Вы сбросили не все карты Помимо сетевой комиссий, сеть Cardano взимает %1$s ADA при транзакции с токеном %2$s Требования к транзакции Cardano Чтобы совершить транзакцию %1$s, внесите некоторую сумму ADA для покрытия сетевой комиссии и минимального значения ADA (рекомендуется 5 ADA) diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index f4dac4dbb5..fcfa15c334 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -47,9 +47,15 @@ Are you sure you want to do this? Change Access Code Access code will be changed on this card only + All cards in the selected wallet have been reset to factory settings. You can now create a new wallet. + Reset complete + Do you want to reset the next card in this wallet? + Card reset Reset to Factory Settings Security Mode Card settings + We recommend completing the reset process for all cards in this wallet + You haven\'t reset all your cards In addition to network fee, the Cardano network charges %1$s ADA when transacting with the %2$s token Cardano transaction requirements To make a %1$s transaction, you must deposit some ADA to cover the network fee and minimum ADA value (5 ADA recommended)