Updated on 2026-08-14
This commit is contained in:
parent
0ed70b7333
commit
8d16f06c08
16 changed files with 142 additions and 55 deletions
|
|
@ -18,4 +18,5 @@ internal data class UserWalletPublicInformation(
|
||||||
val cardsInWallet: Set<String>,
|
val cardsInWallet: Set<String>,
|
||||||
val scanResponse: ScanResponse,
|
val scanResponse: ScanResponse,
|
||||||
val isMultiCurrency: Boolean,
|
val isMultiCurrency: Boolean,
|
||||||
|
val hasBackupError: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
@ -20,6 +20,7 @@ internal val UserWallet.publicInformation: UserWalletPublicInformation
|
||||||
wallets = emptyList(),
|
wallets = emptyList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
hasBackupError = hasBackupError,
|
||||||
)
|
)
|
||||||
|
|
||||||
internal fun UserWalletPublicInformation.toUserWallet(): UserWallet {
|
internal fun UserWalletPublicInformation.toUserWallet(): UserWallet {
|
||||||
|
|
@ -30,6 +31,7 @@ internal fun UserWalletPublicInformation.toUserWallet(): UserWallet {
|
||||||
cardsInWallet = cardsInWallet,
|
cardsInWallet = cardsInWallet,
|
||||||
scanResponse = scanResponse,
|
scanResponse = scanResponse,
|
||||||
isMultiCurrency = isMultiCurrency,
|
isMultiCurrency = isMultiCurrency,
|
||||||
|
hasBackupError = hasBackupError,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,13 +72,14 @@ object OnboardingHelper {
|
||||||
scanResponse: ScanResponse,
|
scanResponse: ScanResponse,
|
||||||
accessCode: String? = null,
|
accessCode: String? = null,
|
||||||
backupCardsIds: List<String>? = null,
|
backupCardsIds: List<String>? = null,
|
||||||
|
hasBackupError: Boolean = false,
|
||||||
) {
|
) {
|
||||||
Analytics.setContext(scanResponse)
|
Analytics.setContext(scanResponse)
|
||||||
scope.launch {
|
scope.launch {
|
||||||
when {
|
when {
|
||||||
// When should save user wallets, then save card without navigate to save wallet screen
|
// When should save user wallets, then save card without navigate to save wallet screen
|
||||||
store.inject(DaggerGraphState::walletsRepository).shouldSaveUserWalletsSync() -> {
|
store.inject(DaggerGraphState::walletsRepository).shouldSaveUserWalletsSync() -> {
|
||||||
proceedWithScanResponse(scanResponse, backupCardsIds)
|
proceedWithScanResponse(scanResponse, backupCardsIds, hasBackupError)
|
||||||
|
|
||||||
store.dispatchOnMain(
|
store.dispatchOnMain(
|
||||||
SaveWalletAction.ProvideBackupInfo(
|
SaveWalletAction.ProvideBackupInfo(
|
||||||
|
|
@ -92,7 +93,7 @@ object OnboardingHelper {
|
||||||
// When should not save user wallets but device has biometry and save wallet screen has not been shown,
|
// When should not save user wallets but device has biometry and save wallet screen has not been shown,
|
||||||
// then open save wallet screen
|
// then open save wallet screen
|
||||||
tangemSdkManager.canUseBiometry && preferencesStorage.shouldShowSaveUserWalletScreen -> {
|
tangemSdkManager.canUseBiometry && preferencesStorage.shouldShowSaveUserWalletScreen -> {
|
||||||
proceedWithScanResponse(scanResponse, backupCardsIds)
|
proceedWithScanResponse(scanResponse, backupCardsIds, hasBackupError)
|
||||||
|
|
||||||
delay(timeMillis = 1_200)
|
delay(timeMillis = 1_200)
|
||||||
|
|
||||||
|
|
@ -109,7 +110,7 @@ object OnboardingHelper {
|
||||||
}
|
}
|
||||||
// If device has no biometry and save wallet screen has been shown, then go through old scenario
|
// If device has no biometry and save wallet screen has been shown, then go through old scenario
|
||||||
else -> {
|
else -> {
|
||||||
proceedWithScanResponse(scanResponse, backupCardsIds)
|
proceedWithScanResponse(scanResponse, backupCardsIds, hasBackupError)
|
||||||
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet))
|
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -129,8 +130,13 @@ object OnboardingHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun proceedWithScanResponse(scanResponse: ScanResponse, backupCardsIds: List<String>?) {
|
private suspend fun proceedWithScanResponse(
|
||||||
val userWallet = UserWalletBuilder(scanResponse)
|
scanResponse: ScanResponse,
|
||||||
|
backupCardsIds: List<String>?,
|
||||||
|
hasBackupError: Boolean,
|
||||||
|
) {
|
||||||
|
val userWallet = UserWalletBuilder(scanResponse = scanResponse)
|
||||||
|
.hasBackupError(hasBackupError)
|
||||||
.backupCardsIds(backupCardsIds?.toSet())
|
.backupCardsIds(backupCardsIds?.toSet())
|
||||||
.build()
|
.build()
|
||||||
.guard {
|
.guard {
|
||||||
|
|
|
||||||
|
|
@ -9,23 +9,23 @@ import kotlinx.coroutines.CoroutineScope
|
||||||
import org.rekotlin.Action
|
import org.rekotlin.Action
|
||||||
|
|
||||||
sealed class OnboardingWalletAction : Action {
|
sealed class OnboardingWalletAction : Action {
|
||||||
object Init : OnboardingWalletAction()
|
data object Init : OnboardingWalletAction()
|
||||||
object GetToCreateWalletStep : OnboardingWalletAction()
|
data object GetToCreateWalletStep : OnboardingWalletAction()
|
||||||
object CreateWallet : OnboardingWalletAction()
|
data object CreateWallet : OnboardingWalletAction()
|
||||||
data class WalletWasCreated(
|
data class WalletWasCreated(
|
||||||
val shouldSendAnalyticsEvent: Boolean,
|
val shouldSendAnalyticsEvent: Boolean,
|
||||||
val result: CompletionResult<CreateProductWalletTaskResponse>,
|
val result: CompletionResult<CreateProductWalletTaskResponse>,
|
||||||
) : OnboardingWalletAction()
|
) : OnboardingWalletAction()
|
||||||
|
|
||||||
object Done : OnboardingWalletAction()
|
data object Done : OnboardingWalletAction()
|
||||||
data class FinishOnboarding(val scope: CoroutineScope) : OnboardingWalletAction()
|
data class FinishOnboarding(val scope: CoroutineScope) : OnboardingWalletAction()
|
||||||
|
|
||||||
object ResumeBackup : OnboardingWalletAction()
|
data object ResumeBackup : OnboardingWalletAction()
|
||||||
|
|
||||||
data class LoadArtwork(val cardArtworkUriForUnfinishedBackup: Uri? = null) : OnboardingWalletAction()
|
data class LoadArtwork(val cardArtworkUriForUnfinishedBackup: Uri? = null) : OnboardingWalletAction()
|
||||||
class SetArtworkUrl(val artworkUri: Uri?) : OnboardingWalletAction()
|
class SetArtworkUrl(val artworkUri: Uri?) : OnboardingWalletAction()
|
||||||
|
|
||||||
object OnBackPressed : OnboardingWalletAction()
|
data object OnBackPressed : OnboardingWalletAction()
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class OnboardingWallet2Action : OnboardingWalletAction() {
|
sealed class OnboardingWallet2Action : OnboardingWalletAction() {
|
||||||
|
|
@ -49,45 +49,46 @@ sealed class OnboardingWallet2Action : OnboardingWalletAction() {
|
||||||
|
|
||||||
sealed class BackupAction : Action {
|
sealed class BackupAction : Action {
|
||||||
|
|
||||||
object IntroduceBackup : BackupAction()
|
data object IntroduceBackup : BackupAction()
|
||||||
object StartBackup : BackupAction()
|
data object StartBackup : BackupAction()
|
||||||
object SkipBackup : BackupAction()
|
data object SkipBackup : BackupAction()
|
||||||
|
|
||||||
object StartAddingPrimaryCard : BackupAction()
|
data object ErrorInBackupCard : BackupAction()
|
||||||
object ScanPrimaryCard : BackupAction()
|
data object StartAddingPrimaryCard : BackupAction()
|
||||||
|
data object ScanPrimaryCard : BackupAction()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check for unfinished backup of standard Wallets
|
* Check for unfinished backup of standard Wallets
|
||||||
* See more GlobalAction.Onboarding.StartForUnfinishedBackup
|
* See more GlobalAction.Onboarding.StartForUnfinishedBackup
|
||||||
*/
|
*/
|
||||||
object CheckForUnfinishedBackup : BackupAction()
|
data object CheckForUnfinishedBackup : BackupAction()
|
||||||
|
|
||||||
object StartAddingBackupCards : BackupAction()
|
data object StartAddingBackupCards : BackupAction()
|
||||||
object AddBackupCard : BackupAction() {
|
data object AddBackupCard : BackupAction() {
|
||||||
object Success : BackupAction()
|
data object Success : BackupAction()
|
||||||
data class ChangeButtonLoading(val isLoading: Boolean) : BackupAction()
|
data class ChangeButtonLoading(val isLoading: Boolean) : BackupAction()
|
||||||
}
|
}
|
||||||
|
|
||||||
object FinishAddingBackupCards : BackupAction()
|
data object FinishAddingBackupCards : BackupAction()
|
||||||
|
|
||||||
object ShowAccessCodeInfoScreen : BackupAction()
|
data object ShowAccessCodeInfoScreen : BackupAction()
|
||||||
object ShowEnterAccessCodeScreen : BackupAction()
|
data object ShowEnterAccessCodeScreen : BackupAction()
|
||||||
data class CheckAccessCode(val accessCode: String) : BackupAction()
|
data class CheckAccessCode(val accessCode: String) : BackupAction()
|
||||||
data class SetAccessCodeError(val error: AccessCodeError?) : BackupAction()
|
data class SetAccessCodeError(val error: AccessCodeError?) : BackupAction()
|
||||||
data class SaveFirstAccessCode(val accessCode: String) : BackupAction()
|
data class SaveFirstAccessCode(val accessCode: String) : BackupAction()
|
||||||
data class SaveAccessCodeConfirmation(val accessCodeConfirmation: String) : BackupAction()
|
data class SaveAccessCodeConfirmation(val accessCodeConfirmation: String) : BackupAction()
|
||||||
object OnAccessCodeDialogClosed : BackupAction()
|
data object OnAccessCodeDialogClosed : BackupAction()
|
||||||
|
|
||||||
object PrepareToWritePrimaryCard : BackupAction()
|
data object PrepareToWritePrimaryCard : BackupAction()
|
||||||
object WritePrimaryCard : BackupAction()
|
data object WritePrimaryCard : BackupAction()
|
||||||
data class PrepareToWriteBackupCard(val cardNumber: Int) : BackupAction()
|
data class PrepareToWriteBackupCard(val cardNumber: Int) : BackupAction()
|
||||||
data class WriteBackupCard(val cardNumber: Int) : BackupAction()
|
data class WriteBackupCard(val cardNumber: Int) : BackupAction()
|
||||||
|
|
||||||
data class FinishBackup(val withAnalytics: Boolean = true) : BackupAction()
|
data class FinishBackup(val withAnalytics: Boolean = true) : BackupAction()
|
||||||
|
|
||||||
object DiscardBackup : BackupAction()
|
data object DiscardBackup : BackupAction()
|
||||||
object DiscardSavedBackup : BackupAction()
|
data object DiscardSavedBackup : BackupAction()
|
||||||
object ResumeFoundUnfinishedBackup : BackupAction()
|
data object ResumeFoundUnfinishedBackup : BackupAction()
|
||||||
|
|
||||||
data class ResetBackupCard(val cardId: String) : BackupAction()
|
data class ResetBackupCard(val cardId: String) : BackupAction()
|
||||||
}
|
}
|
||||||
|
|
@ -20,6 +20,7 @@ import com.tangem.domain.userwallets.Artwork
|
||||||
import com.tangem.domain.userwallets.UserWalletBuilder
|
import com.tangem.domain.userwallets.UserWalletBuilder
|
||||||
import com.tangem.feature.onboarding.data.model.CreateWalletResponse
|
import com.tangem.feature.onboarding.data.model.CreateWalletResponse
|
||||||
import com.tangem.feature.onboarding.presentation.wallet2.analytics.SeedPhraseSource
|
import com.tangem.feature.onboarding.presentation.wallet2.analytics.SeedPhraseSource
|
||||||
|
import com.tangem.feature.wallet.presentation.wallet.domain.BackupValidator
|
||||||
import com.tangem.operations.attestation.OnlineCardVerifier
|
import com.tangem.operations.attestation.OnlineCardVerifier
|
||||||
import com.tangem.operations.backup.BackupService
|
import com.tangem.operations.backup.BackupService
|
||||||
import com.tangem.tap.*
|
import com.tangem.tap.*
|
||||||
|
|
@ -173,6 +174,7 @@ private fun handleWalletAction(action: Action) {
|
||||||
scanResponse = updatedScanResponse,
|
scanResponse = updatedScanResponse,
|
||||||
accessCode = backupState.accessCode,
|
accessCode = backupState.accessCode,
|
||||||
backupCardsIds = backupState.backupCardIds,
|
backupCardsIds = backupState.backupCardIds,
|
||||||
|
hasBackupError = backupState.hasBackupError,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -484,6 +486,10 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
|
||||||
backupService.proceedBackup { result ->
|
backupService.proceedBackup { result ->
|
||||||
when (result) {
|
when (result) {
|
||||||
is CompletionResult.Success -> {
|
is CompletionResult.Success -> {
|
||||||
|
val backupValidator = BackupValidator()
|
||||||
|
if (!backupValidator.isValid(CardDTO(result.data))) {
|
||||||
|
store.dispatchOnMain(BackupAction.ErrorInBackupCard)
|
||||||
|
}
|
||||||
if (backupService.currentState == BackupService.State.Finished) {
|
if (backupService.currentState == BackupService.State.Finished) {
|
||||||
store.dispatchOnMain(BackupAction.FinishBackup())
|
store.dispatchOnMain(BackupAction.FinishBackup())
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,7 @@ private object BackupReducer {
|
||||||
} else {
|
} else {
|
||||||
state.copy(backupStep = BackupStep.WriteBackupCard(action.cardNumber))
|
state.copy(backupStep = BackupStep.WriteBackupCard(action.cardNumber))
|
||||||
}
|
}
|
||||||
|
is BackupAction.ErrorInBackupCard -> state.copy(hasBackupError = true)
|
||||||
is BackupAction.SkipBackup -> state.copy(backupStep = BackupStep.Finished)
|
is BackupAction.SkipBackup -> state.copy(backupStep = BackupStep.Finished)
|
||||||
is BackupAction.FinishBackup -> state.copy(backupStep = BackupStep.Finished)
|
is BackupAction.FinishBackup -> state.copy(backupStep = BackupStep.Finished)
|
||||||
BackupAction.OnAccessCodeDialogClosed -> state.copy(backupStep = BackupStep.AddBackupCards)
|
BackupAction.OnAccessCodeDialogClosed -> state.copy(backupStep = BackupStep.AddBackupCards)
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ data class BackupState(
|
||||||
val canSkipBackup: Boolean = true,
|
val canSkipBackup: Boolean = true,
|
||||||
val isInterruptedBackup: Boolean = false,
|
val isInterruptedBackup: Boolean = false,
|
||||||
val showBtnLoading: Boolean = false,
|
val showBtnLoading: Boolean = false,
|
||||||
|
val hasBackupError: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
enum class AccessCodeError {
|
enum class AccessCodeError {
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ internal class DefaultDerivationsRepositoryTest {
|
||||||
cardsInWallet = setOf(),
|
cardsInWallet = setOf(),
|
||||||
isMultiCurrency = false,
|
isMultiCurrency = false,
|
||||||
scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap()),
|
scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap()),
|
||||||
|
hasBackupError = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ class UserWalletBuilder(
|
||||||
private val getCardImageUseCase: GetCardImageUseCase = GetCardImageUseCase(),
|
private val getCardImageUseCase: GetCardImageUseCase = GetCardImageUseCase(),
|
||||||
) {
|
) {
|
||||||
private var backupCardsIds: Set<String> = emptySet()
|
private var backupCardsIds: Set<String> = emptySet()
|
||||||
|
private var hasBackupError: Boolean = false
|
||||||
|
|
||||||
private val CardDTO.isBackupNotAllowed: Boolean
|
private val CardDTO.isBackupNotAllowed: Boolean
|
||||||
get() = !this.settings.isBackupAllowed
|
get() = !this.settings.isBackupAllowed
|
||||||
|
|
@ -41,6 +42,13 @@ class UserWalletBuilder(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets if UserWallet has any backup errors (wrong curves etc). Use in onboarding
|
||||||
|
*/
|
||||||
|
fun hasBackupError(hasBackupError: Boolean) = this.apply {
|
||||||
|
this.hasBackupError = hasBackupError
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun build(): UserWallet? {
|
suspend fun build(): UserWallet? {
|
||||||
return with(scanResponse) {
|
return with(scanResponse) {
|
||||||
UserWalletIdBuilder.scanResponse(scanResponse)
|
UserWalletIdBuilder.scanResponse(scanResponse)
|
||||||
|
|
@ -53,6 +61,7 @@ class UserWalletBuilder(
|
||||||
cardsInWallet = backupCardsIds.plus(card.cardId),
|
cardsInWallet = backupCardsIds.plus(card.cardId),
|
||||||
scanResponse = this,
|
scanResponse = this,
|
||||||
isMultiCurrency = cardTypesResolver.isMultiwalletAllowed(),
|
isMultiCurrency = cardTypesResolver.isMultiwalletAllowed(),
|
||||||
|
hasBackupError = hasBackupError,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -303,7 +303,7 @@ data class CardDTO(
|
||||||
|
|
||||||
data class Active(val cardCount: Int) : BackupStatus()
|
data class Active(val cardCount: Int) : BackupStatus()
|
||||||
|
|
||||||
object NoBackup : BackupStatus()
|
data object NoBackup : BackupStatus()
|
||||||
|
|
||||||
val isActive: Boolean
|
val isActive: Boolean
|
||||||
get() = this is Active
|
get() = this is Active
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ data class UserWallet(
|
||||||
val artworkUrl: String,
|
val artworkUrl: String,
|
||||||
val cardsInWallet: Set<String>,
|
val cardsInWallet: Set<String>,
|
||||||
val isMultiCurrency: Boolean,
|
val isMultiCurrency: Boolean,
|
||||||
|
val hasBackupError: Boolean,
|
||||||
val scanResponse: ScanResponse, // TODO: Replace with [com.tangem.domain.models.scan.CardDTO]
|
val scanResponse: ScanResponse, // TODO: Replace with [com.tangem.domain.models.scan.CardDTO]
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ sealed class WalletScreenAnalyticsEvent {
|
||||||
override val oneTimeEventId: String = id + userWalletId.stringValue
|
override val oneTimeEventId: String = id + userWalletId.stringValue
|
||||||
}
|
}
|
||||||
|
|
||||||
object WalletOpened : Basic(event = "Wallet Opened")
|
data object WalletOpened : Basic(event = "Wallet Opened")
|
||||||
|
|
||||||
class CardWasScanned(source: AnalyticsParam.ScannedFrom) : Basic(
|
class CardWasScanned(source: AnalyticsParam.ScannedFrom) : Basic(
|
||||||
event = "Card Was Scanned",
|
event = "Card Was Scanned",
|
||||||
|
|
@ -45,8 +45,8 @@ sealed class WalletScreenAnalyticsEvent {
|
||||||
params: Map<String, String> = mapOf(),
|
params: Map<String, String> = mapOf(),
|
||||||
) : AnalyticsEvent(category = "Main Screen", event = event, params = params) {
|
) : AnalyticsEvent(category = "Main Screen", event = event, params = params) {
|
||||||
|
|
||||||
object ScreenOpened : MainScreen(event = "Screen opened")
|
data object ScreenOpened : MainScreen(event = "Screen opened")
|
||||||
object WalletSwipe : MainScreen(event = "Wallet Swipe")
|
data object WalletSwipe : MainScreen(event = "Wallet Swipe")
|
||||||
|
|
||||||
class EnableBiometrics(state: AnalyticsParam.OnOffState) : MainScreen(
|
class EnableBiometrics(state: AnalyticsParam.OnOffState) : MainScreen(
|
||||||
event = "Enable Biometric",
|
event = "Enable Biometric",
|
||||||
|
|
@ -58,37 +58,39 @@ sealed class WalletScreenAnalyticsEvent {
|
||||||
params = mapOf("Result" to result.value),
|
params = mapOf("Result" to result.value),
|
||||||
)
|
)
|
||||||
|
|
||||||
object NoticeBackupYourWalletTapped : MainScreen(event = "Notice - Backup Your Wallet Tapped")
|
data object NoticeBackupYourWalletTapped : MainScreen(event = "Notice - Backup Your Wallet Tapped")
|
||||||
object NoticeScanYourCardTapped : MainScreen(event = "Notice - Scan Your Card Tapped")
|
data object NoticeScanYourCardTapped : MainScreen(event = "Notice - Scan Your Card Tapped")
|
||||||
object NoticeWalletLocked : MainScreen(event = "Notice - Wallet Locked")
|
data object NoticeWalletLocked : MainScreen(event = "Notice - Wallet Locked")
|
||||||
object WalletUnlockTapped : MainScreen(event = "Notice - Wallet Unlock Tapped")
|
data object WalletUnlockTapped : MainScreen(event = "Notice - Wallet Unlock Tapped")
|
||||||
|
|
||||||
object NetworksUnreachable : MainScreen(event = "Notice - Networks Unreachable")
|
data object NetworksUnreachable : MainScreen(event = "Notice - Networks Unreachable")
|
||||||
|
|
||||||
object MissingAddresses : MainScreen(event = "Notice - Missing Addresses")
|
data object MissingAddresses : MainScreen(event = "Notice - Missing Addresses")
|
||||||
|
|
||||||
object CardSignedTransactions : MainScreen(event = "Notice - Card Signed Transactions")
|
data object CardSignedTransactions : MainScreen(event = "Notice - Card Signed Transactions")
|
||||||
|
|
||||||
object HowDoYouLikeTangem : MainScreen(event = "Notice - How Do You Like Tangem")
|
data object HowDoYouLikeTangem : MainScreen(event = "Notice - How Do You Like Tangem")
|
||||||
|
|
||||||
object ProductSampleCard : MainScreen(event = "Notice - Product Sample Card")
|
data object ProductSampleCard : MainScreen(event = "Notice - Product Sample Card")
|
||||||
|
|
||||||
object TestnetCard : MainScreen(event = "Notice - Testnet Card")
|
data object TestnetCard : MainScreen(event = "Notice - Testnet Card")
|
||||||
|
|
||||||
object DemoCard : MainScreen(event = "Notice - Demo Card")
|
data object DemoCard : MainScreen(event = "Notice - Demo Card")
|
||||||
|
|
||||||
object DevelopmentCard : MainScreen(event = "Notice - Development Card")
|
data object DevelopmentCard : MainScreen(event = "Notice - Development Card")
|
||||||
|
|
||||||
object WalletUnlock : MainScreen(event = "Notice - Wallet Unlock")
|
data object WalletUnlock : MainScreen(event = "Notice - Wallet Unlock")
|
||||||
|
|
||||||
object BackupYourWallet : MainScreen(event = "Notice - Backup Your Wallet")
|
data object BackupYourWallet : MainScreen(event = "Notice - Backup Your Wallet")
|
||||||
|
|
||||||
object UnlockAllWithBiometrics : MainScreen(event = "Button - Unlock All With Biometrics")
|
data object BackupError : MainScreen(event = "Notice - Backup Error")
|
||||||
|
|
||||||
object UnlockWithCardScan : MainScreen(event = "Button - Unlock With Card Scan")
|
data object UnlockAllWithBiometrics : MainScreen(event = "Button - Unlock All With Biometrics")
|
||||||
|
|
||||||
object EditWalletTapped : MainScreen(event = "Button - Edit Wallet Tapped")
|
data object UnlockWithCardScan : MainScreen(event = "Button - Unlock With Card Scan")
|
||||||
|
|
||||||
object DeleteWalletTapped : MainScreen(event = "Button - Delete Wallet Tapped")
|
data object EditWalletTapped : MainScreen(event = "Button - Edit Wallet Tapped")
|
||||||
|
|
||||||
|
data object DeleteWalletTapped : MainScreen(event = "Button - Delete Wallet Tapped")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -43,6 +43,7 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
|
||||||
is WalletNotification.Informational.DemoCard -> MainScreen.DemoCard
|
is WalletNotification.Informational.DemoCard -> MainScreen.DemoCard
|
||||||
is WalletNotification.Informational.MissingAddresses -> MainScreen.MissingAddresses
|
is WalletNotification.Informational.MissingAddresses -> MainScreen.MissingAddresses
|
||||||
is WalletNotification.RateApp -> MainScreen.HowDoYouLikeTangem
|
is WalletNotification.RateApp -> MainScreen.HowDoYouLikeTangem
|
||||||
|
is WalletNotification.Critical.BackupError -> MainScreen.BackupError
|
||||||
is WalletNotification.UnlockWallets -> null // See [SelectedWalletAnalyticsSender]
|
is WalletNotification.UnlockWallets -> null // See [SelectedWalletAnalyticsSender]
|
||||||
is WalletNotification.Informational.NoAccount,
|
is WalletNotification.Informational.NoAccount,
|
||||||
is WalletNotification.Warning.LowSignatures,
|
is WalletNotification.Warning.LowSignatures,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
package com.tangem.feature.wallet.presentation.wallet.domain
|
||||||
|
|
||||||
|
import com.tangem.common.card.EllipticCurve
|
||||||
|
import com.tangem.domain.common.configs.CardConfig
|
||||||
|
import com.tangem.domain.models.scan.CardDTO
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class BackupValidator @Inject constructor() {
|
||||||
|
|
||||||
|
fun isValid(cardDTO: CardDTO): Boolean {
|
||||||
|
return validateBackupStatus(cardDTO) && validateCurves(cardDTO)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun validateCurves(cardDTO: CardDTO): Boolean {
|
||||||
|
val config = CardConfig.createConfig(cardDTO)
|
||||||
|
// / Since the curve `bls12381_G2_AUG` was added later into first generation of wallets,
|
||||||
|
// we cannot determine whether this curve is missing due to an error or because the user
|
||||||
|
// did not want to recreate the wallet.
|
||||||
|
val expectedCurves = config.mandatoryCurves
|
||||||
|
.filterNot { it == EllipticCurve.Bls12381G2Aug }
|
||||||
|
val curves = cardDTO.wallets.map { it.curve }
|
||||||
|
for (expectedCurve in expectedCurves) {
|
||||||
|
val cardCurvesCount = curves.count { it == expectedCurve }
|
||||||
|
|
||||||
|
// missing curve
|
||||||
|
if (cardCurvesCount == 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// duplicated curve
|
||||||
|
if (cardCurvesCount > 1) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun validateBackupStatus(cardDTO: CardDTO): Boolean {
|
||||||
|
val backupStatus = cardDTO.backupStatus
|
||||||
|
return backupStatus != null && backupStatus !is CardDTO.BackupStatus.CardLinked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,6 +14,7 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||||
import com.tangem.domain.tokens.model.NetworkGroup
|
import com.tangem.domain.tokens.model.NetworkGroup
|
||||||
import com.tangem.domain.tokens.model.TokenList
|
import com.tangem.domain.tokens.model.TokenList
|
||||||
import com.tangem.domain.tokens.repository.PromoRepository
|
import com.tangem.domain.tokens.repository.PromoRepository
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||||
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
|
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
|
||||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
|
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
|
||||||
|
|
@ -36,6 +37,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
||||||
private val shouldShowSwapPromoWalletUseCase: ShouldShowSwapPromoWalletUseCase,
|
private val shouldShowSwapPromoWalletUseCase: ShouldShowSwapPromoWalletUseCase,
|
||||||
private val promoRepository: PromoRepository,
|
private val promoRepository: PromoRepository,
|
||||||
private val isNeedToBackupUseCase: IsNeedToBackupUseCase,
|
private val isNeedToBackupUseCase: IsNeedToBackupUseCase,
|
||||||
|
private val backupValidator: BackupValidator,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private var readyForRateAppNotification = false
|
private var readyForRateAppNotification = false
|
||||||
|
|
@ -64,7 +66,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
||||||
buildList {
|
buildList {
|
||||||
addSwapPromoNotification(shouldShowPromo, promoBanner, clickIntents)
|
addSwapPromoNotification(shouldShowPromo, promoBanner, clickIntents)
|
||||||
|
|
||||||
addCriticalNotifications(cardTypesResolver)
|
addCriticalNotifications(userWallet)
|
||||||
|
|
||||||
addInformationalNotifications(cardTypesResolver, maybeTokenList, clickIntents)
|
addInformationalNotifications(cardTypesResolver, maybeTokenList, clickIntents)
|
||||||
|
|
||||||
|
|
@ -92,7 +94,13 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MutableList<WalletNotification>.addCriticalNotifications(cardTypesResolver: CardTypesResolver) {
|
private fun MutableList<WalletNotification>.addCriticalNotifications(userWallet: UserWallet) {
|
||||||
|
val cardTypesResolver = userWallet.scanResponse.cardTypesResolver
|
||||||
|
addIf(
|
||||||
|
element = WalletNotification.Critical.BackupError,
|
||||||
|
condition = !backupValidator.isValid(userWallet.scanResponse.card) || userWallet.hasBackupError,
|
||||||
|
)
|
||||||
|
|
||||||
addIf(
|
addIf(
|
||||||
element = WalletNotification.Critical.DevCard,
|
element = WalletNotification.Critical.DevCard,
|
||||||
condition = !cardTypesResolver.isReleaseFirmwareType(),
|
condition = !cardTypesResolver.isReleaseFirmwareType(),
|
||||||
|
|
|
||||||
|
|
@ -27,15 +27,20 @@ sealed class WalletNotification(val config: NotificationConfig) {
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
object DevCard : Critical(
|
data object DevCard : Critical(
|
||||||
title = resourceReference(id = R.string.warning_developer_card_title),
|
title = resourceReference(id = R.string.warning_developer_card_title),
|
||||||
subtitle = resourceReference(id = R.string.warning_developer_card_message),
|
subtitle = resourceReference(id = R.string.warning_developer_card_message),
|
||||||
)
|
)
|
||||||
|
|
||||||
object FailedCardValidation : Critical(
|
data object FailedCardValidation : Critical(
|
||||||
title = resourceReference(id = R.string.warning_failed_to_verify_card_title),
|
title = resourceReference(id = R.string.warning_failed_to_verify_card_title),
|
||||||
subtitle = resourceReference(id = R.string.warning_failed_to_verify_card_message),
|
subtitle = resourceReference(id = R.string.warning_failed_to_verify_card_message),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data object BackupError : Critical(
|
||||||
|
title = resourceReference(R.string.warning_backup_errors_title),
|
||||||
|
subtitle = resourceReference(R.string.warning_backup_errors_message),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class Warning(
|
sealed class Warning(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue