Updated on 2026-08-14

This commit is contained in:
Tangem 2026-05-04 17:48:20 +01:00
parent c29afa8164
commit b08b5df3b9
5 changed files with 53 additions and 75 deletions

View file

@ -9,9 +9,8 @@ internal data class WalletBackupUM(
val googleDriveOption: LabelUM?,
val googleDriveStatus: BackupStatus,
val onRecoveryPhraseClick: () -> Unit,
val onGoogleDriveAction: (Boolean) -> Unit, // boolean is for show and hide dialog
val onGoogleDriveClick: () -> Unit,
val onHardwareWalletClick: () -> Unit,
val isGoogleDriveDialogShown: Boolean,
val isBackedUp: Boolean,
)

View file

@ -7,10 +7,13 @@ import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.R
import com.tangem.core.ui.components.label.entity.LabelStyle
import com.tangem.core.ui.components.label.entity.LabelUM
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.DialogMessage
import com.tangem.core.ui.message.EventMessageAction
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
@ -35,6 +38,7 @@ internal class WalletBackupModel @Inject constructor(
private val router: Router,
private val trackingContextProxy: TrackingContextProxy,
private val analyticsEventHandler: AnalyticsEventHandler,
private val uiMessageSender: UiMessageSender,
) : Model() {
private val params: WalletBackupComponent.Params = paramsContainer.require()
@ -59,14 +63,7 @@ internal class WalletBackupModel @Inject constructor(
),
googleDriveStatus = BackupStatus.ComingSoon,
onRecoveryPhraseClick = ::onRecoveryPhraseClick,
onGoogleDriveAction = { shouldShowDialog ->
if (shouldShowDialog) {
onGoogleDriveBackupClick()
} else {
dismissGoogleDriveFakeDoorDialog()
}
},
isGoogleDriveDialogShown = false,
onGoogleDriveClick = ::onGoogleDriveBackupClick,
onHardwareWalletClick = ::onHardwareWalletClick,
isBackedUp = false,
),
@ -99,6 +96,7 @@ internal class WalletBackupModel @Inject constructor(
trackingContextProxy.removeContext()
super.onDestroy()
}
private fun updateBackupStatuses(userWallet: UserWallet) {
uiState.update { currentState ->
if (userWallet is UserWallet.Hot) {
@ -159,11 +157,16 @@ internal class WalletBackupModel @Inject constructor(
private fun onGoogleDriveBackupClick() {
analyticsEventHandler.send(WalletSettingsAnalyticEvents.ButtonGoogleDriveBackup())
uiState.update { state -> state.copy(isGoogleDriveDialogShown = true) }
}
private fun dismissGoogleDriveFakeDoorDialog() {
uiState.update { state -> state.copy(isGoogleDriveDialogShown = false) }
uiMessageSender.send(
DialogMessage(
title = resourceReference(id = R.string.hw_backup_google_drive_dialog_title),
message = resourceReference(id = R.string.hw_backup_google_drive_dialog_message),
firstAction = EventMessageAction(
title = resourceReference(id = R.string.common_ok),
onClick = {},
),
),
)
}
private fun showSeedPhrase(hotWallet: UserWallet.Hot) {

View file

@ -25,7 +25,6 @@ import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.hotwallet.common.ui.OptionBlock
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")
@Composable
@ -95,22 +94,13 @@ internal fun WalletBackupContent(state: WalletBackupUM, modifier: Modifier = Mod
badge = {
state.googleDriveOption?.let { Label(it) }
},
onClick = {
state.onGoogleDriveAction(true)
},
onClick = state.onGoogleDriveClick,
enabled = state.googleDriveStatus != BackupStatus.ComingSoon,
backgroundColor = TangemTheme.colors.background.primary,
)
Spacer(modifier = Modifier.size(16.dp))
}
}
if (state.isGoogleDriveDialogShown) {
GoogleDriveFakeDoorDialog(
onDismiss = {
state.onGoogleDriveAction(false)
},
)
}
}
@Preview(showBackground = true, widthDp = 360)
@ -140,8 +130,7 @@ private class WalletBackupUMProvider : CollectionPreviewParameterProvider<Wallet
googleDriveStatus = BackupStatus.ComingSoon,
onBackClick = {},
onRecoveryPhraseClick = {},
onGoogleDriveAction = {},
isGoogleDriveDialogShown = false,
onGoogleDriveClick = {},
onHardwareWalletClick = {},
isBackedUp = false,
),
@ -161,8 +150,7 @@ private class WalletBackupUMProvider : CollectionPreviewParameterProvider<Wallet
googleDriveStatus = BackupStatus.NoBackup,
onBackClick = {},
onRecoveryPhraseClick = {},
onGoogleDriveAction = {},
isGoogleDriveDialogShown = false,
onGoogleDriveClick = {},
onHardwareWalletClick = {},
isBackedUp = false,
),
@ -182,8 +170,7 @@ private class WalletBackupUMProvider : CollectionPreviewParameterProvider<Wallet
googleDriveStatus = BackupStatus.Done,
onBackClick = {},
onRecoveryPhraseClick = {},
onGoogleDriveAction = {},
isGoogleDriveDialogShown = false,
onGoogleDriveClick = {},
onHardwareWalletClick = {},
isBackedUp = false,
),
@ -200,8 +187,7 @@ private class WalletBackupUMProvider : CollectionPreviewParameterProvider<Wallet
googleDriveStatus = BackupStatus.Done,
onBackClick = {},
onRecoveryPhraseClick = {},
onGoogleDriveAction = {},
isGoogleDriveDialogShown = false,
onGoogleDriveClick = {},
onHardwareWalletClick = {},
isBackedUp = false,
),

View file

@ -1,17 +0,0 @@
package com.tangem.features.hotwallet.walletbackup.ui.component
import androidx.compose.runtime.Composable
import com.tangem.common.R
import com.tangem.core.ui.components.BasicDialog
import com.tangem.core.ui.components.DialogButtonUM
import com.tangem.core.ui.extensions.stringResourceSafe
@Composable
fun GoogleDriveFakeDoorDialog(onDismiss: () -> Unit) {
BasicDialog(
title = stringResourceSafe(id = R.string.hw_backup_google_drive_dialog_title),
message = stringResourceSafe(id = R.string.hw_backup_google_drive_dialog_message),
confirmButton = DialogButtonUM(onClick = onDismiss),
onDismissDialog = onDismiss,
)
}

View file

@ -7,6 +7,10 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.utils.TrackingContextProxy
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.R
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.DialogMessage
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
@ -37,6 +41,7 @@ internal class WalletBackupModelTest {
private val router: Router = mockk(relaxUnitFun = true)
private val trackingContextProxy: TrackingContextProxy = mockk(relaxUnitFun = true)
private val analyticsEventHandler: AnalyticsEventHandler = mockk(relaxUnitFun = true)
private val uiMessageSender: UiMessageSender = mockk(relaxUnitFun = true)
private val paramsContainer: ParamsContainer = mockk()
private val walletId = UserWalletId("011")
@ -123,7 +128,7 @@ internal class WalletBackupModelTest {
}
@Test
fun `GIVEN backed up hot wallet AND unlock success WHEN RecoveryPhrase action THEN ViewPhrase pushed`() = runTest {
fun `GIVEN backed up hot wallet AND unlock success WHEN onRecoveryPhraseClick THEN ViewPhrase pushed`() = runTest {
every { getUserWalletUseCase.invokeFlow(walletId) } returns flowOf(hotWalletBackedUp.right())
every { getUserWalletUseCase.invoke(walletId) } returns hotWalletBackedUp.right()
coEvery { unlockHotWalletContextualUseCase.invoke(hotWalletId) } returns mockk<UnlockHotWallet>().right()
@ -140,7 +145,7 @@ internal class WalletBackupModelTest {
}
@Test
fun `GIVEN backed up hot wallet AND unlock failure WHEN RecoveryPhrase action THEN ViewPhrase not pushed`() =
fun `GIVEN backed up hot wallet AND unlock failure WHEN onRecoveryPhraseClick THEN ViewPhrase not pushed`() =
runTest {
every { getUserWalletUseCase.invokeFlow(walletId) } returns flowOf(hotWalletBackedUp.right())
every { getUserWalletUseCase.invoke(walletId) } returns hotWalletBackedUp.right()
@ -160,7 +165,7 @@ internal class WalletBackupModelTest {
}
@Test
fun `GIVEN backed up cold wallet WHEN RecoveryPhrase action THEN no navigation AND no unlock`() = runTest {
fun `GIVEN backed up cold wallet WHEN onRecoveryPhraseClick THEN no navigation AND no unlock`() = runTest {
every { getUserWalletUseCase.invokeFlow(walletId) } returns flowOf(hotWalletBackedUp.right())
every { getUserWalletUseCase.invoke(walletId) } returns coldWallet.right()
@ -176,7 +181,7 @@ internal class WalletBackupModelTest {
}
@Test
fun `GIVEN not backed up wallet WHEN RecoveryPhrase action THEN WalletActivation pushed`() = runTest {
fun `GIVEN not backed up wallet WHEN onRecoveryPhraseClick THEN WalletActivation pushed`() = runTest {
val model = createModel(this)
advanceUntilIdle()
@ -195,7 +200,7 @@ internal class WalletBackupModelTest {
}
@Test
fun `WHEN HardwareWallet action THEN ButtonHardwareUpdate sent AND WalletHardwareBackup pushed`() = runTest {
fun `WHEN onHardwareWalletClick THEN ButtonHardwareUpdate sent AND WalletHardwareBackup pushed`() = runTest {
val model = createModel(this)
advanceUntilIdle()
@ -211,33 +216,34 @@ internal class WalletBackupModelTest {
}
@Test
fun `WHEN GoogleDriveBackup with isDialogShown true THEN dialog shown AND analytics sent`() = runTest {
fun `WHEN onGoogleDriveClick THEN DialogMessage sent AND analytics sent`() = runTest {
val model = createModel(this)
advanceUntilIdle()
model.uiState.value.onGoogleDriveAction(true)
model.uiState.value.onGoogleDriveClick()
verify { analyticsEventHandler.send(match<WalletSettingsAnalyticEvents.ButtonGoogleDriveBackup> { true }) }
Assertions.assertTrue(model.uiState.value.isGoogleDriveDialogShown)
}
@Test
fun `WHEN GoogleDriveBackup with isDialogShown false THEN dialog hidden AND no analytics sent`() = runTest {
val model = createModel(this)
advanceUntilIdle()
model.uiState.value.onGoogleDriveAction(true)
advanceUntilIdle()
model.uiState.value.onGoogleDriveAction(false)
verify(exactly = 1) {
analyticsEventHandler.send(match<WalletSettingsAnalyticEvents.ButtonGoogleDriveBackup> { true })
verify {
analyticsEventHandler.send(
event = match<WalletSettingsAnalyticEvents.ButtonGoogleDriveBackup> { true }
)
}
verify {
uiMessageSender.send(
match<DialogMessage> {
val isTitleCorrect = it.title == resourceReference(
id = R.string.hw_backup_google_drive_dialog_title
)
val isMessageCorrect = it.message == resourceReference(
id = R.string.hw_backup_google_drive_dialog_message
)
isTitleCorrect && isMessageCorrect
}
)
}
Assertions.assertFalse(model.uiState.value.isGoogleDriveDialogShown)
}
@Test
fun `WHEN OnBack action THEN router pop is called`() = runTest {
fun `WHEN onBackClick THEN router pop is called`() = runTest {
val model = createModel(this)
advanceUntilIdle()
@ -255,6 +261,7 @@ internal class WalletBackupModelTest {
router = router,
trackingContextProxy = trackingContextProxy,
analyticsEventHandler = analyticsEventHandler,
uiMessageSender = uiMessageSender,
)
}