Updated on 2026-08-14

This commit is contained in:
Tangem 2024-06-02 16:02:04 +08:00
parent ce52c600ff
commit a14f93ab82
9 changed files with 192 additions and 68 deletions

View file

@ -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()

View file

@ -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
}
}

View file

@ -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,

View file

@ -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<DetailsSta
private val viewModel: ResetCardViewModel by viewModels()
private var screenState: MutableState<ResetCardScreenState> =
mutableStateOf(ResetCardScreenState.InitialState)
private var screenState = MutableStateFlow<ResetCardScreenState>(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,
)

View file

@ -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 = {},
)

View file

@ -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 {

View file

@ -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)
}
}