Updated on 2026-08-14

This commit is contained in:
Tangem 2026-05-04 17:08:11 +01:00
parent 8e16f4e332
commit c29afa8164
5 changed files with 55 additions and 77 deletions

View file

@ -7,7 +7,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.features.hotwallet.WalletBackupComponent
import com.tangem.features.hotwallet.walletbackup.entity.Action
import com.tangem.features.hotwallet.walletbackup.model.WalletBackupModel
import com.tangem.features.hotwallet.walletbackup.ui.WalletBackupContent
import dagger.assisted.Assisted
@ -27,21 +26,6 @@ internal class DefaultWalletBackupComponent @AssistedInject constructor(
WalletBackupContent(
state = state,
modifier = modifier,
onBackClick = {
model.onAction(Action.OnBack)
},
onHardwareWalletClick = {
model.onAction(Action.HardwareWallet)
},
onRecoveryPhraseClick = {
model.onAction(Action.RecoveryPhrase)
},
onGoogleDriveClick = {
model.onAction(Action.GoogleDriveBackup(isDialogShown = true))
},
onGoogleDriveFakeDoorDialogDismiss = {
model.onAction(Action.GoogleDriveBackup(isDialogShown = false))
},
)
}

View file

@ -3,10 +3,14 @@ package com.tangem.features.hotwallet.walletbackup.entity
import com.tangem.core.ui.components.label.entity.LabelUM
internal data class WalletBackupUM(
val onBackClick: () -> Unit,
val hardwareWalletOption: LabelUM?,
val recoveryPhraseOption: LabelUM?,
val googleDriveOption: LabelUM?,
val googleDriveStatus: BackupStatus,
val onRecoveryPhraseClick: () -> Unit,
val onGoogleDriveAction: (Boolean) -> Unit, // boolean is for show and hide dialog
val onHardwareWalletClick: () -> Unit,
val isGoogleDriveDialogShown: Boolean,
val isBackedUp: Boolean,
)
@ -15,11 +19,4 @@ internal sealed class BackupStatus {
object Done : BackupStatus()
object ComingSoon : BackupStatus()
object NoBackup : BackupStatus()
}
internal sealed interface Action {
data object RecoveryPhrase : Action
data object HardwareWallet : Action
data class GoogleDriveBackup(val isDialogShown: Boolean) : Action
data object OnBack : Action
}

View file

@ -16,13 +16,12 @@ import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.UnlockHotWalletContextualUseCase
import com.tangem.features.hotwallet.WalletBackupComponent
import com.tangem.features.hotwallet.walletbackup.entity.Action
import com.tangem.features.hotwallet.walletbackup.entity.BackupStatus
import com.tangem.features.hotwallet.walletbackup.entity.WalletBackupUM
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.logging.TangemLogger
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import com.tangem.utils.logging.TangemLogger
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
@ -45,6 +44,7 @@ internal class WalletBackupModel @Inject constructor(
val uiState: StateFlow<WalletBackupUM>
field = MutableStateFlow(
WalletBackupUM(
onBackClick = { router.pop() },
hardwareWalletOption = LabelUM(
text = resourceReference(R.string.common_recommended),
style = LabelStyle.ACCENT,
@ -58,7 +58,16 @@ internal class WalletBackupModel @Inject constructor(
style = LabelStyle.REGULAR,
),
googleDriveStatus = BackupStatus.ComingSoon,
onRecoveryPhraseClick = ::onRecoveryPhraseClick,
onGoogleDriveAction = { shouldShowDialog ->
if (shouldShowDialog) {
onGoogleDriveBackupClick()
} else {
dismissGoogleDriveFakeDoorDialog()
}
},
isGoogleDriveDialogShown = false,
onHardwareWalletClick = ::onHardwareWalletClick,
isBackedUp = false,
),
)
@ -90,20 +99,6 @@ internal class WalletBackupModel @Inject constructor(
trackingContextProxy.removeContext()
super.onDestroy()
}
fun onAction(action: Action) {
when (action) {
Action.RecoveryPhrase -> onRecoveryPhraseClick()
Action.HardwareWallet -> onHardwareWalletClick()
is Action.GoogleDriveBackup -> if (action.isDialogShown) {
onGoogleDriveBackupClick()
} else {
dismissGoogleDriveFakeDoorDialog()
}
Action.OnBack -> router.pop()
}
}
private fun updateBackupStatuses(userWallet: UserWallet) {
uiState.update { currentState ->
if (userWallet is UserWallet.Hot) {

View file

@ -27,17 +27,9 @@ import com.tangem.features.hotwallet.walletbackup.entity.BackupStatus
import com.tangem.features.hotwallet.walletbackup.entity.WalletBackupUM
import com.tangem.features.hotwallet.walletbackup.ui.component.GoogleDriveFakeDoorDialog
@Suppress("LongMethod", "LongParameterList")
@Suppress("LongMethod")
@Composable
internal fun WalletBackupContent(
state: WalletBackupUM,
modifier: Modifier = Modifier,
onBackClick: () -> Unit,
onHardwareWalletClick: () -> Unit,
onRecoveryPhraseClick: () -> Unit,
onGoogleDriveClick: () -> Unit,
onGoogleDriveFakeDoorDialogDismiss: () -> Unit,
) {
internal fun WalletBackupContent(state: WalletBackupUM, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.background(TangemTheme.colors.background.secondary)
@ -46,7 +38,7 @@ internal fun WalletBackupContent(
) {
AppBarWithBackButton(
text = stringResourceSafe(R.string.common_backup),
onBackClick = onBackClick,
onBackClick = state.onBackClick,
)
Column(
@ -67,7 +59,7 @@ internal fun WalletBackupContent(
title = stringResourceSafe(R.string.hw_backup_upgrade_title),
description = stringResourceSafe(R.string.hw_backup_upgrade_description),
badge = { Label(state.hardwareWalletOption) },
onClick = onHardwareWalletClick,
onClick = state.onHardwareWalletClick,
enabled = true,
backgroundColor = TangemTheme.colors.background.primary,
)
@ -91,7 +83,7 @@ internal fun WalletBackupContent(
badge = {
state.recoveryPhraseOption?.let { Label(it) }
},
onClick = onRecoveryPhraseClick,
onClick = state.onRecoveryPhraseClick,
enabled = true,
backgroundColor = TangemTheme.colors.background.primary,
)
@ -103,7 +95,9 @@ internal fun WalletBackupContent(
badge = {
state.googleDriveOption?.let { Label(it) }
},
onClick = onGoogleDriveClick,
onClick = {
state.onGoogleDriveAction(true)
},
enabled = state.googleDriveStatus != BackupStatus.ComingSoon,
backgroundColor = TangemTheme.colors.background.primary,
)
@ -111,7 +105,11 @@ internal fun WalletBackupContent(
}
}
if (state.isGoogleDriveDialogShown) {
GoogleDriveFakeDoorDialog(onDismiss = onGoogleDriveFakeDoorDialogDismiss)
GoogleDriveFakeDoorDialog(
onDismiss = {
state.onGoogleDriveAction(false)
},
)
}
}
@ -120,14 +118,7 @@ internal fun WalletBackupContent(
@Composable
private fun WalletBackupContentPreview(@PreviewParameter(WalletBackupUMProvider::class) state: WalletBackupUM) {
TangemThemePreview {
WalletBackupContent(
state = state,
onBackClick = {},
onHardwareWalletClick = {},
onRecoveryPhraseClick = {},
onGoogleDriveClick = {},
onGoogleDriveFakeDoorDialogDismiss = {},
)
WalletBackupContent(state)
}
}
@ -147,7 +138,11 @@ private class WalletBackupUMProvider : CollectionPreviewParameterProvider<Wallet
style = LabelStyle.REGULAR,
),
googleDriveStatus = BackupStatus.ComingSoon,
onBackClick = {},
onRecoveryPhraseClick = {},
onGoogleDriveAction = {},
isGoogleDriveDialogShown = false,
onHardwareWalletClick = {},
isBackedUp = false,
),
WalletBackupUM(
@ -164,7 +159,11 @@ private class WalletBackupUMProvider : CollectionPreviewParameterProvider<Wallet
style = LabelStyle.WARNING,
),
googleDriveStatus = BackupStatus.NoBackup,
onBackClick = {},
onRecoveryPhraseClick = {},
onGoogleDriveAction = {},
isGoogleDriveDialogShown = false,
onHardwareWalletClick = {},
isBackedUp = false,
),
WalletBackupUM(
@ -181,7 +180,11 @@ private class WalletBackupUMProvider : CollectionPreviewParameterProvider<Wallet
style = LabelStyle.ACCENT,
),
googleDriveStatus = BackupStatus.Done,
onBackClick = {},
onRecoveryPhraseClick = {},
onGoogleDriveAction = {},
isGoogleDriveDialogShown = false,
onHardwareWalletClick = {},
isBackedUp = false,
),
WalletBackupUM(
@ -195,7 +198,11 @@ private class WalletBackupUMProvider : CollectionPreviewParameterProvider<Wallet
style = LabelStyle.ACCENT,
),
googleDriveStatus = BackupStatus.Done,
onBackClick = {},
onRecoveryPhraseClick = {},
onGoogleDriveAction = {},
isGoogleDriveDialogShown = false,
onHardwareWalletClick = {},
isBackedUp = false,
),
),

View file

@ -14,16 +14,11 @@ import com.tangem.domain.wallets.models.GetUserWalletError
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.UnlockHotWalletContextualUseCase
import com.tangem.features.hotwallet.WalletBackupComponent
import com.tangem.features.hotwallet.walletbackup.entity.Action
import com.tangem.features.hotwallet.walletbackup.entity.BackupStatus
import com.tangem.hot.sdk.model.HotWalletId
import com.tangem.hot.sdk.model.UnlockHotWallet
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import io.mockk.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.StandardTestDispatcher
@ -136,7 +131,7 @@ internal class WalletBackupModelTest {
val model = createModel(this)
advanceUntilIdle()
model.onAction(Action.RecoveryPhrase)
model.uiState.value.onRecoveryPhraseClick()
advanceUntilIdle()
verify { analyticsEventHandler.send(match<WalletSettingsAnalyticEvents.ButtonRecoveryPhrase> { true }) }
@ -154,7 +149,7 @@ internal class WalletBackupModelTest {
val model = createModel(this)
advanceUntilIdle()
model.onAction(Action.RecoveryPhrase)
model.uiState.value.onRecoveryPhraseClick()
advanceUntilIdle()
verify { analyticsEventHandler.send(match<WalletSettingsAnalyticEvents.ButtonRecoveryPhrase> { true }) }
@ -172,7 +167,7 @@ internal class WalletBackupModelTest {
val model = createModel(this)
advanceUntilIdle()
model.onAction(Action.RecoveryPhrase)
model.uiState.value.onRecoveryPhraseClick()
advanceUntilIdle()
verify { analyticsEventHandler.send(match<WalletSettingsAnalyticEvents.ButtonRecoveryPhrase> { true }) }
@ -185,7 +180,7 @@ internal class WalletBackupModelTest {
val model = createModel(this)
advanceUntilIdle()
model.onAction(Action.RecoveryPhrase)
model.uiState.value.onRecoveryPhraseClick()
advanceUntilIdle()
verify { analyticsEventHandler.send(match<WalletSettingsAnalyticEvents.ButtonRecoveryPhrase> { true }) }
@ -204,7 +199,7 @@ internal class WalletBackupModelTest {
val model = createModel(this)
advanceUntilIdle()
model.onAction(Action.HardwareWallet)
model.uiState.value.onHardwareWalletClick()
verify { analyticsEventHandler.send(match<WalletSettingsAnalyticEvents.ButtonHardwareUpdate> { true }) }
verify {
@ -220,7 +215,7 @@ internal class WalletBackupModelTest {
val model = createModel(this)
advanceUntilIdle()
model.onAction(Action.GoogleDriveBackup(isDialogShown = true))
model.uiState.value.onGoogleDriveAction(true)
verify { analyticsEventHandler.send(match<WalletSettingsAnalyticEvents.ButtonGoogleDriveBackup> { true }) }
Assertions.assertTrue(model.uiState.value.isGoogleDriveDialogShown)
@ -230,10 +225,10 @@ internal class WalletBackupModelTest {
fun `WHEN GoogleDriveBackup with isDialogShown false THEN dialog hidden AND no analytics sent`() = runTest {
val model = createModel(this)
advanceUntilIdle()
model.onAction(Action.GoogleDriveBackup(isDialogShown = true))
model.uiState.value.onGoogleDriveAction(true)
advanceUntilIdle()
model.onAction(Action.GoogleDriveBackup(isDialogShown = false))
model.uiState.value.onGoogleDriveAction(false)
verify(exactly = 1) {
analyticsEventHandler.send(match<WalletSettingsAnalyticEvents.ButtonGoogleDriveBackup> { true })
@ -246,7 +241,7 @@ internal class WalletBackupModelTest {
val model = createModel(this)
advanceUntilIdle()
model.onAction(Action.OnBack)
model.uiState.value.onBackClick()
verify { router.pop(onComplete = any()) }
}