Updated on 2026-08-14
This commit is contained in:
parent
c29afa8164
commit
b08b5df3b9
5 changed files with 53 additions and 75 deletions
|
|
@ -9,9 +9,8 @@ internal data class WalletBackupUM(
|
||||||
val googleDriveOption: LabelUM?,
|
val googleDriveOption: LabelUM?,
|
||||||
val googleDriveStatus: BackupStatus,
|
val googleDriveStatus: BackupStatus,
|
||||||
val onRecoveryPhraseClick: () -> Unit,
|
val onRecoveryPhraseClick: () -> Unit,
|
||||||
val onGoogleDriveAction: (Boolean) -> Unit, // boolean is for show and hide dialog
|
val onGoogleDriveClick: () -> Unit,
|
||||||
val onHardwareWalletClick: () -> Unit,
|
val onHardwareWalletClick: () -> Unit,
|
||||||
val isGoogleDriveDialogShown: Boolean,
|
|
||||||
val isBackedUp: Boolean,
|
val isBackedUp: Boolean,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,13 @@ import com.tangem.core.decompose.di.ModelScoped
|
||||||
import com.tangem.core.decompose.model.Model
|
import com.tangem.core.decompose.model.Model
|
||||||
import com.tangem.core.decompose.model.ParamsContainer
|
import com.tangem.core.decompose.model.ParamsContainer
|
||||||
import com.tangem.core.decompose.navigation.Router
|
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.R
|
||||||
import com.tangem.core.ui.components.label.entity.LabelStyle
|
import com.tangem.core.ui.components.label.entity.LabelStyle
|
||||||
import com.tangem.core.ui.components.label.entity.LabelUM
|
import com.tangem.core.ui.components.label.entity.LabelUM
|
||||||
import com.tangem.core.ui.extensions.resourceReference
|
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.models.wallet.UserWallet
|
||||||
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
|
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
|
||||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||||
|
|
@ -35,6 +38,7 @@ internal class WalletBackupModel @Inject constructor(
|
||||||
private val router: Router,
|
private val router: Router,
|
||||||
private val trackingContextProxy: TrackingContextProxy,
|
private val trackingContextProxy: TrackingContextProxy,
|
||||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||||
|
private val uiMessageSender: UiMessageSender,
|
||||||
) : Model() {
|
) : Model() {
|
||||||
|
|
||||||
private val params: WalletBackupComponent.Params = paramsContainer.require()
|
private val params: WalletBackupComponent.Params = paramsContainer.require()
|
||||||
|
|
@ -59,14 +63,7 @@ internal class WalletBackupModel @Inject constructor(
|
||||||
),
|
),
|
||||||
googleDriveStatus = BackupStatus.ComingSoon,
|
googleDriveStatus = BackupStatus.ComingSoon,
|
||||||
onRecoveryPhraseClick = ::onRecoveryPhraseClick,
|
onRecoveryPhraseClick = ::onRecoveryPhraseClick,
|
||||||
onGoogleDriveAction = { shouldShowDialog ->
|
onGoogleDriveClick = ::onGoogleDriveBackupClick,
|
||||||
if (shouldShowDialog) {
|
|
||||||
onGoogleDriveBackupClick()
|
|
||||||
} else {
|
|
||||||
dismissGoogleDriveFakeDoorDialog()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isGoogleDriveDialogShown = false,
|
|
||||||
onHardwareWalletClick = ::onHardwareWalletClick,
|
onHardwareWalletClick = ::onHardwareWalletClick,
|
||||||
isBackedUp = false,
|
isBackedUp = false,
|
||||||
),
|
),
|
||||||
|
|
@ -99,6 +96,7 @@ internal class WalletBackupModel @Inject constructor(
|
||||||
trackingContextProxy.removeContext()
|
trackingContextProxy.removeContext()
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateBackupStatuses(userWallet: UserWallet) {
|
private fun updateBackupStatuses(userWallet: UserWallet) {
|
||||||
uiState.update { currentState ->
|
uiState.update { currentState ->
|
||||||
if (userWallet is UserWallet.Hot) {
|
if (userWallet is UserWallet.Hot) {
|
||||||
|
|
@ -159,11 +157,16 @@ internal class WalletBackupModel @Inject constructor(
|
||||||
|
|
||||||
private fun onGoogleDriveBackupClick() {
|
private fun onGoogleDriveBackupClick() {
|
||||||
analyticsEventHandler.send(WalletSettingsAnalyticEvents.ButtonGoogleDriveBackup())
|
analyticsEventHandler.send(WalletSettingsAnalyticEvents.ButtonGoogleDriveBackup())
|
||||||
uiState.update { state -> state.copy(isGoogleDriveDialogShown = true) }
|
uiMessageSender.send(
|
||||||
}
|
DialogMessage(
|
||||||
|
title = resourceReference(id = R.string.hw_backup_google_drive_dialog_title),
|
||||||
private fun dismissGoogleDriveFakeDoorDialog() {
|
message = resourceReference(id = R.string.hw_backup_google_drive_dialog_message),
|
||||||
uiState.update { state -> state.copy(isGoogleDriveDialogShown = false) }
|
firstAction = EventMessageAction(
|
||||||
|
title = resourceReference(id = R.string.common_ok),
|
||||||
|
onClick = {},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showSeedPhrase(hotWallet: UserWallet.Hot) {
|
private fun showSeedPhrase(hotWallet: UserWallet.Hot) {
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ import com.tangem.core.ui.res.TangemThemePreview
|
||||||
import com.tangem.features.hotwallet.common.ui.OptionBlock
|
import com.tangem.features.hotwallet.common.ui.OptionBlock
|
||||||
import com.tangem.features.hotwallet.walletbackup.entity.BackupStatus
|
import com.tangem.features.hotwallet.walletbackup.entity.BackupStatus
|
||||||
import com.tangem.features.hotwallet.walletbackup.entity.WalletBackupUM
|
import com.tangem.features.hotwallet.walletbackup.entity.WalletBackupUM
|
||||||
import com.tangem.features.hotwallet.walletbackup.ui.component.GoogleDriveFakeDoorDialog
|
|
||||||
|
|
||||||
@Suppress("LongMethod")
|
@Suppress("LongMethod")
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -95,22 +94,13 @@ internal fun WalletBackupContent(state: WalletBackupUM, modifier: Modifier = Mod
|
||||||
badge = {
|
badge = {
|
||||||
state.googleDriveOption?.let { Label(it) }
|
state.googleDriveOption?.let { Label(it) }
|
||||||
},
|
},
|
||||||
onClick = {
|
onClick = state.onGoogleDriveClick,
|
||||||
state.onGoogleDriveAction(true)
|
|
||||||
},
|
|
||||||
enabled = state.googleDriveStatus != BackupStatus.ComingSoon,
|
enabled = state.googleDriveStatus != BackupStatus.ComingSoon,
|
||||||
backgroundColor = TangemTheme.colors.background.primary,
|
backgroundColor = TangemTheme.colors.background.primary,
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.size(16.dp))
|
Spacer(modifier = Modifier.size(16.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (state.isGoogleDriveDialogShown) {
|
|
||||||
GoogleDriveFakeDoorDialog(
|
|
||||||
onDismiss = {
|
|
||||||
state.onGoogleDriveAction(false)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview(showBackground = true, widthDp = 360)
|
@Preview(showBackground = true, widthDp = 360)
|
||||||
|
|
@ -140,8 +130,7 @@ private class WalletBackupUMProvider : CollectionPreviewParameterProvider<Wallet
|
||||||
googleDriveStatus = BackupStatus.ComingSoon,
|
googleDriveStatus = BackupStatus.ComingSoon,
|
||||||
onBackClick = {},
|
onBackClick = {},
|
||||||
onRecoveryPhraseClick = {},
|
onRecoveryPhraseClick = {},
|
||||||
onGoogleDriveAction = {},
|
onGoogleDriveClick = {},
|
||||||
isGoogleDriveDialogShown = false,
|
|
||||||
onHardwareWalletClick = {},
|
onHardwareWalletClick = {},
|
||||||
isBackedUp = false,
|
isBackedUp = false,
|
||||||
),
|
),
|
||||||
|
|
@ -161,8 +150,7 @@ private class WalletBackupUMProvider : CollectionPreviewParameterProvider<Wallet
|
||||||
googleDriveStatus = BackupStatus.NoBackup,
|
googleDriveStatus = BackupStatus.NoBackup,
|
||||||
onBackClick = {},
|
onBackClick = {},
|
||||||
onRecoveryPhraseClick = {},
|
onRecoveryPhraseClick = {},
|
||||||
onGoogleDriveAction = {},
|
onGoogleDriveClick = {},
|
||||||
isGoogleDriveDialogShown = false,
|
|
||||||
onHardwareWalletClick = {},
|
onHardwareWalletClick = {},
|
||||||
isBackedUp = false,
|
isBackedUp = false,
|
||||||
),
|
),
|
||||||
|
|
@ -182,8 +170,7 @@ private class WalletBackupUMProvider : CollectionPreviewParameterProvider<Wallet
|
||||||
googleDriveStatus = BackupStatus.Done,
|
googleDriveStatus = BackupStatus.Done,
|
||||||
onBackClick = {},
|
onBackClick = {},
|
||||||
onRecoveryPhraseClick = {},
|
onRecoveryPhraseClick = {},
|
||||||
onGoogleDriveAction = {},
|
onGoogleDriveClick = {},
|
||||||
isGoogleDriveDialogShown = false,
|
|
||||||
onHardwareWalletClick = {},
|
onHardwareWalletClick = {},
|
||||||
isBackedUp = false,
|
isBackedUp = false,
|
||||||
),
|
),
|
||||||
|
|
@ -200,8 +187,7 @@ private class WalletBackupUMProvider : CollectionPreviewParameterProvider<Wallet
|
||||||
googleDriveStatus = BackupStatus.Done,
|
googleDriveStatus = BackupStatus.Done,
|
||||||
onBackClick = {},
|
onBackClick = {},
|
||||||
onRecoveryPhraseClick = {},
|
onRecoveryPhraseClick = {},
|
||||||
onGoogleDriveAction = {},
|
onGoogleDriveClick = {},
|
||||||
isGoogleDriveDialogShown = false,
|
|
||||||
onHardwareWalletClick = {},
|
onHardwareWalletClick = {},
|
||||||
isBackedUp = false,
|
isBackedUp = false,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -7,6 +7,10 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||||
import com.tangem.core.analytics.utils.TrackingContextProxy
|
import com.tangem.core.analytics.utils.TrackingContextProxy
|
||||||
import com.tangem.core.decompose.model.ParamsContainer
|
import com.tangem.core.decompose.model.ParamsContainer
|
||||||
import com.tangem.core.decompose.navigation.Router
|
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.UserWallet
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
|
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
|
||||||
|
|
@ -37,6 +41,7 @@ internal class WalletBackupModelTest {
|
||||||
private val router: Router = mockk(relaxUnitFun = true)
|
private val router: Router = mockk(relaxUnitFun = true)
|
||||||
private val trackingContextProxy: TrackingContextProxy = mockk(relaxUnitFun = true)
|
private val trackingContextProxy: TrackingContextProxy = mockk(relaxUnitFun = true)
|
||||||
private val analyticsEventHandler: AnalyticsEventHandler = mockk(relaxUnitFun = true)
|
private val analyticsEventHandler: AnalyticsEventHandler = mockk(relaxUnitFun = true)
|
||||||
|
private val uiMessageSender: UiMessageSender = mockk(relaxUnitFun = true)
|
||||||
private val paramsContainer: ParamsContainer = mockk()
|
private val paramsContainer: ParamsContainer = mockk()
|
||||||
|
|
||||||
private val walletId = UserWalletId("011")
|
private val walletId = UserWalletId("011")
|
||||||
|
|
@ -123,7 +128,7 @@ internal class WalletBackupModelTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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.invokeFlow(walletId) } returns flowOf(hotWalletBackedUp.right())
|
||||||
every { getUserWalletUseCase.invoke(walletId) } returns hotWalletBackedUp.right()
|
every { getUserWalletUseCase.invoke(walletId) } returns hotWalletBackedUp.right()
|
||||||
coEvery { unlockHotWalletContextualUseCase.invoke(hotWalletId) } returns mockk<UnlockHotWallet>().right()
|
coEvery { unlockHotWalletContextualUseCase.invoke(hotWalletId) } returns mockk<UnlockHotWallet>().right()
|
||||||
|
|
@ -140,7 +145,7 @@ internal class WalletBackupModelTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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 {
|
runTest {
|
||||||
every { getUserWalletUseCase.invokeFlow(walletId) } returns flowOf(hotWalletBackedUp.right())
|
every { getUserWalletUseCase.invokeFlow(walletId) } returns flowOf(hotWalletBackedUp.right())
|
||||||
every { getUserWalletUseCase.invoke(walletId) } returns hotWalletBackedUp.right()
|
every { getUserWalletUseCase.invoke(walletId) } returns hotWalletBackedUp.right()
|
||||||
|
|
@ -160,7 +165,7 @@ internal class WalletBackupModelTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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.invokeFlow(walletId) } returns flowOf(hotWalletBackedUp.right())
|
||||||
every { getUserWalletUseCase.invoke(walletId) } returns coldWallet.right()
|
every { getUserWalletUseCase.invoke(walletId) } returns coldWallet.right()
|
||||||
|
|
||||||
|
|
@ -176,7 +181,7 @@ internal class WalletBackupModelTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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)
|
val model = createModel(this)
|
||||||
advanceUntilIdle()
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
|
@ -195,7 +200,7 @@ internal class WalletBackupModelTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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)
|
val model = createModel(this)
|
||||||
advanceUntilIdle()
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
|
@ -211,33 +216,34 @@ internal class WalletBackupModelTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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)
|
val model = createModel(this)
|
||||||
advanceUntilIdle()
|
advanceUntilIdle()
|
||||||
|
|
||||||
model.uiState.value.onGoogleDriveAction(true)
|
model.uiState.value.onGoogleDriveClick()
|
||||||
|
|
||||||
verify { analyticsEventHandler.send(match<WalletSettingsAnalyticEvents.ButtonGoogleDriveBackup> { true }) }
|
verify {
|
||||||
Assertions.assertTrue(model.uiState.value.isGoogleDriveDialogShown)
|
analyticsEventHandler.send(
|
||||||
}
|
event = match<WalletSettingsAnalyticEvents.ButtonGoogleDriveBackup> { true }
|
||||||
|
)
|
||||||
@Test
|
}
|
||||||
fun `WHEN GoogleDriveBackup with isDialogShown false THEN dialog hidden AND no analytics sent`() = runTest {
|
verify {
|
||||||
val model = createModel(this)
|
uiMessageSender.send(
|
||||||
advanceUntilIdle()
|
match<DialogMessage> {
|
||||||
model.uiState.value.onGoogleDriveAction(true)
|
val isTitleCorrect = it.title == resourceReference(
|
||||||
advanceUntilIdle()
|
id = R.string.hw_backup_google_drive_dialog_title
|
||||||
|
)
|
||||||
model.uiState.value.onGoogleDriveAction(false)
|
val isMessageCorrect = it.message == resourceReference(
|
||||||
|
id = R.string.hw_backup_google_drive_dialog_message
|
||||||
verify(exactly = 1) {
|
)
|
||||||
analyticsEventHandler.send(match<WalletSettingsAnalyticEvents.ButtonGoogleDriveBackup> { true })
|
isTitleCorrect && isMessageCorrect
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Assertions.assertFalse(model.uiState.value.isGoogleDriveDialogShown)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `WHEN OnBack action THEN router pop is called`() = runTest {
|
fun `WHEN onBackClick THEN router pop is called`() = runTest {
|
||||||
val model = createModel(this)
|
val model = createModel(this)
|
||||||
advanceUntilIdle()
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
|
@ -255,6 +261,7 @@ internal class WalletBackupModelTest {
|
||||||
router = router,
|
router = router,
|
||||||
trackingContextProxy = trackingContextProxy,
|
trackingContextProxy = trackingContextProxy,
|
||||||
analyticsEventHandler = analyticsEventHandler,
|
analyticsEventHandler = analyticsEventHandler,
|
||||||
|
uiMessageSender = uiMessageSender,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue