Updated on 2026-08-14

This commit is contained in:
Tangem 2024-05-21 12:02:55 +02:00
parent 78996521c6
commit ab5fac9196
41 changed files with 366 additions and 73 deletions

View file

@ -20,6 +20,7 @@ import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
import com.tangem.domain.settings.repositories.SettingsRepository import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.domain.wallets.repository.WalletsRepository
@ -86,6 +87,8 @@ interface ApplicationEntryPoint {
fun getSaveTwinsOnboardingShownUseCase(): SaveTwinsOnboardingShownUseCase fun getSaveTwinsOnboardingShownUseCase(): SaveTwinsOnboardingShownUseCase
fun getWalletNameGenerateUseCase(): GenerateWalletNameUseCase
fun getCardRepository(): CardRepository fun getCardRepository(): CardRepository
fun getFeedbackManagerFeatureToggles(): FeedbackManagerFeatureToggles fun getFeedbackManagerFeatureToggles(): FeedbackManagerFeatureToggles

View file

@ -37,6 +37,7 @@ import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
import com.tangem.domain.settings.repositories.SettingsRepository import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.domain.wallets.repository.WalletsRepository
@ -152,6 +153,9 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
private val saveTwinsOnboardingShownUseCase: SaveTwinsOnboardingShownUseCase private val saveTwinsOnboardingShownUseCase: SaveTwinsOnboardingShownUseCase
get() = entryPoint.getSaveTwinsOnboardingShownUseCase() get() = entryPoint.getSaveTwinsOnboardingShownUseCase()
private val generateWalletNameUseCase: GenerateWalletNameUseCase
get() = entryPoint.getWalletNameGenerateUseCase()
private val cardRepository: CardRepository private val cardRepository: CardRepository
get() = entryPoint.getCardRepository() get() = entryPoint.getCardRepository()
@ -243,6 +247,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
generalUserWalletsListManager = generalUserWalletsListManager, generalUserWalletsListManager = generalUserWalletsListManager,
wasTwinsOnboardingShownUseCase = wasTwinsOnboardingShownUseCase, wasTwinsOnboardingShownUseCase = wasTwinsOnboardingShownUseCase,
saveTwinsOnboardingShownUseCase = saveTwinsOnboardingShownUseCase, saveTwinsOnboardingShownUseCase = saveTwinsOnboardingShownUseCase,
generateWalletNameUseCase = generateWalletNameUseCase,
cardRepository = cardRepository, cardRepository = cardRepository,
feedbackManagerFeatureToggles = feedbackManagerFeatureToggles, feedbackManagerFeatureToggles = feedbackManagerFeatureToggles,
tangemSdkLogger = tangemSdkLogger, tangemSdkLogger = tangemSdkLogger,

View file

@ -5,7 +5,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.ProductType import com.tangem.domain.models.scan.ProductType
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletIdBuilder import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter
import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.IntroductionProcess import com.tangem.tap.common.analytics.events.IntroductionProcess

View file

@ -6,7 +6,7 @@ import com.tangem.blockchain.common.address.Address
import com.tangem.crypto.NetworkType import com.tangem.crypto.NetworkType
import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletIdBuilder import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.tap.common.extensions.stripZeroPlainString import com.tangem.tap.common.extensions.stripZeroPlainString
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList

View file

@ -1,6 +1,8 @@
package com.tangem.tap.di.domain package com.tangem.tap.di.domain
import com.tangem.domain.card.* import com.tangem.domain.card.*
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
import com.tangem.tap.domain.scanCard.DefaultScanCardProcessor import com.tangem.tap.domain.scanCard.DefaultScanCardProcessor
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
@ -14,5 +16,11 @@ internal object CardLegacyDomainModule {
@Provides @Provides
@Singleton @Singleton
fun provideScanCardUseCase(): ScanCardProcessor = DefaultScanCardProcessor() fun provideScanCardProcessor(): ScanCardProcessor = DefaultScanCardProcessor()
@Provides
@Singleton
fun providesWalletNameGenerateUseCase(userWalletsListManager: UserWalletsListManager): GenerateWalletNameUseCase {
return GenerateWalletNameUseCase(userWalletsListManager)
}
} }

View file

@ -4,8 +4,10 @@ import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.repository.WalletAddressServiceRepository import com.tangem.domain.wallets.repository.WalletAddressServiceRepository
import com.tangem.domain.wallets.repository.WalletNamesMigrationRepository
import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.domain.wallets.usecase.* import com.tangem.domain.wallets.usecase.*
import com.tangem.feature.wallet.presentation.wallet.domain.WalletNameMigrationUseCase
import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
@ -23,6 +25,18 @@ internal object WalletsDomainModule {
return GetWalletsUseCase(userWalletsListManager = userWalletsListManager) return GetWalletsUseCase(userWalletsListManager = userWalletsListManager)
} }
@Provides
@ViewModelScoped
fun providesWalletNameMigrationUseCase(
userWalletsListManager: UserWalletsListManager,
walletNamesMigrationRepository: WalletNamesMigrationRepository,
): WalletNameMigrationUseCase {
return WalletNameMigrationUseCase(
userWalletsListManager = userWalletsListManager,
walletNamesMigrationRepository = walletNamesMigrationRepository,
)
}
@Provides @Provides
@ViewModelScoped @ViewModelScoped
fun providesGetUserWalletUseCase(userWalletsListManager: UserWalletsListManager): GetUserWalletUseCase { fun providesGetUserWalletUseCase(userWalletsListManager: UserWalletsListManager): GetUserWalletUseCase {
@ -76,6 +90,18 @@ internal object WalletsDomainModule {
return UpdateWalletUseCase(userWalletsListManager = userWalletsListManager) return UpdateWalletUseCase(userWalletsListManager = userWalletsListManager)
} }
@Provides
@ViewModelScoped
fun providesRenameWalletUseCase(userWalletsListManager: UserWalletsListManager): RenameWalletUseCase {
return RenameWalletUseCase(userWalletsListManager = userWalletsListManager)
}
@Provides
@ViewModelScoped
fun providesGetWalletsSyncUseCase(userWalletsListManager: UserWalletsListManager): GetWalletNamesUseCase {
return GetWalletNamesUseCase(userWalletsListManager = userWalletsListManager)
}
@Provides @Provides
@ViewModelScoped @ViewModelScoped
fun providesDeleteWalletUseCase(userWalletsListManager: UserWalletsListManager): DeleteWalletUseCase { fun providesDeleteWalletUseCase(userWalletsListManager: UserWalletsListManager): DeleteWalletUseCase {

View file

@ -9,7 +9,7 @@ import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.card.repository.DerivationsRepository import com.tangem.domain.card.repository.DerivationsRepository
import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.userwallets.UserWalletIdBuilder import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.operations.derivation.ExtendedPublicKeysMap import com.tangem.operations.derivation.ExtendedPublicKeysMap

View file

@ -5,7 +5,7 @@ import com.tangem.common.services.Result
import com.tangem.domain.common.TwinCardNumber import com.tangem.domain.common.TwinCardNumber
import com.tangem.domain.common.getTwinCardNumber import com.tangem.domain.common.getTwinCardNumber
import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.userwallets.Artwork import com.tangem.domain.wallets.models.Artwork
import com.tangem.operations.attestation.CardVerifyAndGetInfo import com.tangem.operations.attestation.CardVerifyAndGetInfo
import com.tangem.operations.attestation.OnlineCardVerifier import com.tangem.operations.attestation.OnlineCardVerifier
@ -20,8 +20,8 @@ suspend fun CardDTO.getOrLoadCardArtworkUrl(cardInfo: Result<CardVerifyAndGetInf
cardId.startsWith(Artwork.MARTA_CARD_ID) -> Artwork.MARTA_CARD_URL cardId.startsWith(Artwork.MARTA_CARD_ID) -> Artwork.MARTA_CARD_URL
else -> { else -> {
when (getTwinCardNumber()) { when (getTwinCardNumber()) {
TwinCardNumber.First -> Artwork.TWIN_CARD_1 TwinCardNumber.First -> Artwork.TWIN_CARD_1_URL
TwinCardNumber.Second -> Artwork.TWIN_CARD_2 TwinCardNumber.Second -> Artwork.TWIN_CARD_2_URL
else -> Artwork.DEFAULT_IMG_URL else -> Artwork.DEFAULT_IMG_URL
} }
} }

View file

@ -9,7 +9,7 @@ import com.tangem.datasource.local.token.UserTokensStore
import com.tangem.domain.common.DerivationStyleProvider import com.tangem.domain.common.DerivationStyleProvider
import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation
import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.userwallets.UserWalletIdBuilder import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.demo.DemoHelper
import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.CoroutineDispatcherProvider

View file

@ -43,6 +43,9 @@ internal class BiometricUserWalletsListManager(
.filterNotNull() .filterNotNull()
.distinctUntilChanged() .distinctUntilChanged()
override val userWalletsSync: List<UserWallet>
get() = state.value.userWallets
override val selectedUserWalletSync: UserWallet? override val selectedUserWalletSync: UserWallet?
get() = findSelectedUserWallet() get() = findSelectedUserWallet()

View file

@ -61,6 +61,9 @@ internal class GeneralUserWalletsListManager(
} }
} }
override val userWalletsSync: List<UserWallet>
get() = requireImplementation.userWalletsSync
override val selectedUserWalletSync: UserWallet? override val selectedUserWalletSync: UserWallet?
get() = requireImplementation.selectedUserWalletSync get() = requireImplementation.selectedUserWalletSync

View file

@ -26,6 +26,9 @@ internal class RuntimeUserWalletsListManager : UserWalletsListManager {
.filterNotNull() .filterNotNull()
.distinctUntilChanged() .distinctUntilChanged()
override val userWalletsSync: List<UserWallet>
get() = listOfNotNull(state.value.userWallet)
override val selectedUserWalletSync: UserWallet? override val selectedUserWalletSync: UserWallet?
get() = state.value.userWallet get() = state.value.userWallet

View file

@ -15,8 +15,8 @@ import com.tangem.domain.apptheme.model.AppThemeMode
import com.tangem.domain.common.TapWorkarounds.isTangemTwins import com.tangem.domain.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletBuilder import com.tangem.domain.wallets.builder.UserWalletBuilder
import com.tangem.domain.userwallets.UserWalletIdBuilder import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.domain.wallets.legacy.asLockable import com.tangem.domain.wallets.legacy.asLockable
import com.tangem.tap.* import com.tangem.tap.*
import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.AnalyticsParam
@ -522,7 +522,8 @@ class DetailsMiddleware {
} }
private suspend fun saveUserWalletAndPopBackToWalletScreen(scanResponse: ScanResponse): CompletionResult<Unit> { private suspend fun saveUserWalletAndPopBackToWalletScreen(scanResponse: ScanResponse): CompletionResult<Unit> {
val userWallet = UserWalletBuilder(scanResponse).build() val walletNameGenerateUseCase = store.inject(DaggerGraphState::generateWalletNameUseCase)
val userWallet = UserWalletBuilder(scanResponse, walletNameGenerateUseCase).build()
?: return CompletionResult.Failure(TangemSdkError.WalletIsNotCreated()) ?: return CompletionResult.Failure(TangemSdkError.WalletIsNotCreated())
val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager) val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager)

View file

@ -11,7 +11,7 @@ import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletBuilder import com.tangem.domain.wallets.builder.UserWalletBuilder
import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter
import com.tangem.tap.common.analytics.events.IntroductionProcess import com.tangem.tap.common.analytics.events.IntroductionProcess
import com.tangem.tap.common.analytics.events.Shop import com.tangem.tap.common.analytics.events.Shop
@ -103,7 +103,8 @@ private suspend fun readCard() {
} }
private fun proceedWithScanResponse(scanResponse: ScanResponse) = scope.launch { private fun proceedWithScanResponse(scanResponse: ScanResponse) = scope.launch {
val userWallet = UserWalletBuilder(scanResponse).build().guard { val walletNameGenerateUseCase = store.inject(DaggerGraphState::generateWalletNameUseCase)
val userWallet = UserWalletBuilder(scanResponse, walletNameGenerateUseCase).build().guard {
Timber.e("User wallet not created") Timber.e("User wallet not created")
return@launch return@launch
} }

View file

@ -12,8 +12,8 @@ import com.tangem.domain.common.util.twinsIsTwinned
import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ProductType import com.tangem.domain.models.scan.ProductType
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletBuilder import com.tangem.domain.wallets.builder.UserWalletBuilder
import com.tangem.domain.userwallets.UserWalletIdBuilder import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter
import com.tangem.tap.common.extensions.* import com.tangem.tap.common.extensions.*
import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.demo.DemoHelper
@ -142,7 +142,8 @@ object OnboardingHelper {
backupCardsIds: List<String>?, backupCardsIds: List<String>?,
hasBackupError: Boolean, hasBackupError: Boolean,
) { ) {
val userWallet = UserWalletBuilder(scanResponse = scanResponse) val walletNameGenerateUseCase = store.inject(DaggerGraphState::generateWalletNameUseCase)
val userWallet = UserWalletBuilder(scanResponse, walletNameGenerateUseCase)
.hasBackupError(hasBackupError) .hasBackupError(hasBackupError)
.backupCardsIds(backupCardsIds?.toSet()) .backupCardsIds(backupCardsIds?.toSet())
.build() .build()

View file

@ -12,7 +12,7 @@ import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.common.util.twinsIsTwinned import com.tangem.domain.common.util.twinsIsTwinned
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletIdBuilder import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.domain.wallets.legacy.asLockable import com.tangem.domain.wallets.legacy.asLockable
import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Onboarding import com.tangem.tap.common.analytics.events.Onboarding

View file

@ -18,7 +18,7 @@ import com.tangem.core.ui.extensions.setStatusBarColor
import com.tangem.datasource.asset.reader.AssetReader import com.tangem.datasource.asset.reader.AssetReader
import com.tangem.domain.common.TwinCardNumber import com.tangem.domain.common.TwinCardNumber
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.Artwork import com.tangem.domain.wallets.models.Artwork
import com.tangem.sdk.ui.widget.leapfrogWidget.LeapfrogWidget import com.tangem.sdk.ui.widget.leapfrogWidget.LeapfrogWidget
import com.tangem.tap.common.analytics.events.Onboarding import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.extensions.* import com.tangem.tap.common.extensions.*
@ -88,13 +88,13 @@ internal class OnboardingTwinsFragment : BaseOnboardingFragment<TwinCardsState>(
binding.toolbar.title = getText(R.string.twins_recreate_toolbar) binding.toolbar.title = getText(R.string.twins_recreate_toolbar)
mainBinding.onboardingTopContainer.imvTwinFrontCard.load(Artwork.TWIN_CARD_1) { mainBinding.onboardingTopContainer.imvTwinFrontCard.load(Artwork.TWIN_CARD_1_URL) {
placeholder(R.drawable.card_placeholder_black) placeholder(R.drawable.card_placeholder_black)
error(R.drawable.card_placeholder_black) error(R.drawable.card_placeholder_black)
fallback(R.drawable.card_placeholder_black) fallback(R.drawable.card_placeholder_black)
} }
mainBinding.onboardingTopContainer.imvTwinBackCard.load(Artwork.TWIN_CARD_2) { mainBinding.onboardingTopContainer.imvTwinBackCard.load(Artwork.TWIN_CARD_2_URL) {
placeholder(R.drawable.card_placeholder_white) placeholder(R.drawable.card_placeholder_white)
error(R.drawable.card_placeholder_white) error(R.drawable.card_placeholder_white)
fallback(R.drawable.card_placeholder_white) fallback(R.drawable.card_placeholder_white)

View file

@ -15,8 +15,8 @@ import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.Artwork import com.tangem.domain.wallets.models.Artwork
import com.tangem.domain.userwallets.UserWalletBuilder import com.tangem.domain.wallets.builder.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.feature.wallet.presentation.wallet.domain.BackupValidator
@ -548,7 +548,8 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
} }
if (scanResponse != null) { if (scanResponse != null) {
val userWallet = UserWalletBuilder(scanResponse) val walletNameGenerateUseCase = store.inject(DaggerGraphState::generateWalletNameUseCase)
val userWallet = UserWalletBuilder(scanResponse, walletNameGenerateUseCase)
.backupCardsIds(backupState.backupCardIds.toSet()) .backupCardsIds(backupState.backupCardIds.toSet())
.build() .build()
.guard { .guard {

View file

@ -6,7 +6,7 @@ import com.tangem.common.extensions.guard
import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.Analytics
import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.userwallets.UserWalletBuilder import com.tangem.domain.wallets.builder.UserWalletBuilder
import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWallet
import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.MainScreen import com.tangem.tap.common.analytics.events.MainScreen
@ -64,7 +64,8 @@ internal class SaveWalletMiddleware {
scope.launch { scope.launch {
val backupInfo = state.backupInfo ?: error("Backup info is null") val backupInfo = state.backupInfo ?: error("Backup info is null")
val userWallet = UserWalletBuilder(backupInfo.scanResponse) val walletNameGenerateUseCase = store.inject(DaggerGraphState::generateWalletNameUseCase)
val userWallet = UserWalletBuilder(backupInfo.scanResponse, walletNameGenerateUseCase)
.backupCardsIds(state.backupInfo.backupCardsIds) .backupCardsIds(state.backupInfo.backupCardsIds)
.hasBackupError(hasBackupError) .hasBackupError(hasBackupError)
.build() .build()

View file

@ -13,7 +13,7 @@ import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletBuilder import com.tangem.domain.wallets.builder.UserWalletBuilder
import com.tangem.domain.wallets.legacy.UserWalletsListManager.Lockable.UnlockType import com.tangem.domain.wallets.legacy.UserWalletsListManager.Lockable.UnlockType
import com.tangem.domain.wallets.legacy.unlockIfLockable import com.tangem.domain.wallets.legacy.unlockIfLockable
import com.tangem.tap.backupService import com.tangem.tap.backupService
@ -117,7 +117,9 @@ internal class WelcomeMiddleware {
) )
scanCardInternal { scanResponse -> scanCardInternal { scanResponse ->
val userWallet = UserWalletBuilder(scanResponse).build() ?: return@scanCardInternal val walletNameGenerateUseCase = store.inject(DaggerGraphState::generateWalletNameUseCase)
val userWallet = UserWalletBuilder(scanResponse, walletNameGenerateUseCase).build()
?: return@scanCardInternal
val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager) val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager)
userWalletsListManager.save(userWallet, canOverride = true) userWalletsListManager.save(userWallet, canOverride = true)

View file

@ -16,6 +16,7 @@ import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
import com.tangem.domain.settings.repositories.SettingsRepository import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.domain.wallets.repository.WalletsRepository
@ -62,6 +63,7 @@ data class DaggerGraphState(
val generalUserWalletsListManager: UserWalletsListManager? = null, val generalUserWalletsListManager: UserWalletsListManager? = null,
val wasTwinsOnboardingShownUseCase: WasTwinsOnboardingShownUseCase? = null, val wasTwinsOnboardingShownUseCase: WasTwinsOnboardingShownUseCase? = null,
val saveTwinsOnboardingShownUseCase: SaveTwinsOnboardingShownUseCase? = null, val saveTwinsOnboardingShownUseCase: SaveTwinsOnboardingShownUseCase? = null,
val generateWalletNameUseCase: GenerateWalletNameUseCase? = null,
val cardRepository: CardRepository? = null, val cardRepository: CardRepository? = null,
val feedbackManagerFeatureToggles: FeedbackManagerFeatureToggles? = null, val feedbackManagerFeatureToggles: FeedbackManagerFeatureToggles? = null,
val tangemSdkLogger: TangemSdkLogger? = null, val tangemSdkLogger: TangemSdkLogger? = null,

View file

@ -91,6 +91,8 @@ object PreferencesKeys {
val SHOULD_SAVE_ACCESS_CODES_KEY by lazy { booleanPreferencesKey(name = "saveAccessCodes") } val SHOULD_SAVE_ACCESS_CODES_KEY by lazy { booleanPreferencesKey(name = "saveAccessCodes") }
val IS_WALLET_NAMES_MIGRATION_DONE_KEY by lazy { booleanPreferencesKey(name = "isWalletNamesMigrationDone") }
fun getStart2CoinTOSAcceptedKey(region: String?) = booleanPreferencesKey(name = "start2Coin_tos_accepted_$region") fun getStart2CoinTOSAcceptedKey(region: String?) = booleanPreferencesKey(name = "start2Coin_tos_accepted_$region")
} }

View file

@ -0,0 +1,23 @@
package com.tangem.data.wallets
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getSyncOrDefault
import com.tangem.datasource.local.preferences.utils.store
import com.tangem.domain.wallets.repository.WalletNamesMigrationRepository
class DefaultWalletNamesMigrationRepository(
private val appPreferencesStore: AppPreferencesStore,
) : WalletNamesMigrationRepository {
override suspend fun isMigrationDone(): Boolean {
return appPreferencesStore.getSyncOrDefault(
key = PreferencesKeys.IS_WALLET_NAMES_MIGRATION_DONE_KEY,
default = false,
)
}
override suspend fun setMigrationDone() {
appPreferencesStore.store(PreferencesKeys.IS_WALLET_NAMES_MIGRATION_DONE_KEY, true)
}
}

View file

@ -1,9 +1,11 @@
package com.tangem.data.wallets.di package com.tangem.data.wallets.di
import com.tangem.data.wallets.DefaultWalletNamesMigrationRepository
import com.tangem.data.wallets.DefaultWalletAddressServiceRepository import com.tangem.data.wallets.DefaultWalletAddressServiceRepository
import com.tangem.data.wallets.DefaultWalletsRepository import com.tangem.data.wallets.DefaultWalletsRepository
import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.repository.WalletNamesMigrationRepository
import com.tangem.domain.wallets.repository.WalletAddressServiceRepository import com.tangem.domain.wallets.repository.WalletAddressServiceRepository
import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.domain.wallets.repository.WalletsRepository
import dagger.Module import dagger.Module
@ -29,4 +31,10 @@ internal object WalletsDataModule {
): WalletAddressServiceRepository { ): WalletAddressServiceRepository {
return DefaultWalletAddressServiceRepository(walletManagersFacade) return DefaultWalletAddressServiceRepository(walletManagersFacade)
} }
@Provides
@Singleton
fun provideMigrateNamesRepository(appPreferencesStore: AppPreferencesStore): WalletNamesMigrationRepository {
return DefaultWalletNamesMigrationRepository(appPreferencesStore)
}
} }

View file

@ -9,17 +9,16 @@ android {
} }
dependencies { dependencies {
implementation(project(":core:datasource")) implementation(projects.core.datasource)
implementation(project(":core:utils")) implementation(projects.core.utils)
implementation(project(":common")) implementation(projects.common)
implementation(project(":libs:auth")) implementation(projects.libs.auth)
implementation(projects.libs.blockchainSdk) implementation(projects.libs.blockchainSdk)
implementation(projects.domain.demo) implementation(projects.domain.demo)
implementation(projects.domain.models) implementation(projects.domain.models)
implementation(projects.domain.tokens.models) implementation(projects.domain.tokens.models)
implementation(projects.domain.txhistory.models) implementation(projects.domain.txhistory.models)
implementation(projects.domain.wallets.models) implementation(projects.domain.wallets.models)
/** Tangem libraries */ /** Tangem libraries */
implementation(deps.tangem.blockchain) { implementation(deps.tangem.blockchain) {
exclude(module = "joda-time") exclude(module = "joda-time")

View file

@ -1,36 +1,22 @@
package com.tangem.domain.userwallets package com.tangem.domain.wallets.builder
import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ProductType
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.wallets.usecase.GetCardImageUseCase
import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
class UserWalletBuilder( class UserWalletBuilder(
private val scanResponse: ScanResponse, private val scanResponse: ScanResponse,
private val generateWalletNameUseCase: GenerateWalletNameUseCase,
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 var hasBackupError: Boolean = false
private val CardDTO.isBackupNotAllowed: Boolean private val CardDTO.isBackupNotAllowed: Boolean
get() = !this.settings.isBackupAllowed get() = !settings.isBackupAllowed
private val ScanResponse.userWalletName: String
get() = when (productType) {
ProductType.Note -> "Note"
ProductType.Twins -> "Twin"
ProductType.Start2Coin -> "Start2Coin"
ProductType.Visa -> "Tangem Visa"
ProductType.Wallet,
ProductType.Wallet2,
ProductType.Ring,
-> when {
card.isBackupNotAllowed -> "Tangem card"
cardTypesResolver.isStart2Coin() -> "Start2Coin"
else -> "Wallet"
}
}
/** /**
* DANGEROUS!!! * DANGEROUS!!!
@ -56,7 +42,11 @@ class UserWalletBuilder(
?.let { ?.let {
UserWallet( UserWallet(
walletId = it, walletId = it,
name = userWalletName, name = generateWalletNameUseCase(
productType = productType,
isBackupNotAllowed = card.isBackupNotAllowed,
isStartToCoin = cardTypesResolver.isStart2Coin(),
),
artworkUrl = getCardImageUseCase.invoke(card.cardId, card.cardPublicKey), artworkUrl = getCardImageUseCase.invoke(card.cardId, card.cardPublicKey),
cardsInWallet = backupCardsIds.plus(card.cardId), cardsInWallet = backupCardsIds.plus(card.cardId),
scanResponse = this, scanResponse = this,

View file

@ -1,4 +1,4 @@
package com.tangem.domain.userwallets package com.tangem.domain.wallets.builder
import com.tangem.common.extensions.calculateSha256 import com.tangem.common.extensions.calculateSha256
import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.hexToBytes

View file

@ -18,6 +18,9 @@ interface UserWalletsListManager {
/** [Flow] with selected [UserWallet] updates */ /** [Flow] with selected [UserWallet] updates */
val selectedUserWallet: Flow<UserWallet> val selectedUserWallet: Flow<UserWallet>
/** All saved [UserWallet]s */
val userWalletsSync: List<UserWallet>
/** Selected [UserWallet] */ /** Selected [UserWallet] */
val selectedUserWalletSync: UserWallet? val selectedUserWalletSync: UserWallet?

View file

@ -1,4 +1,4 @@
package com.tangem.domain.userwallets package com.tangem.domain.wallets.models
data class Artwork(val artworkId: String) { data class Artwork(val artworkId: String) {
@ -6,9 +6,9 @@ data class Artwork(val artworkId: String) {
const val DEFAULT_IMG_URL = "https://app.tangem.com/cards/card_default.png" const val DEFAULT_IMG_URL = "https://app.tangem.com/cards/card_default.png"
const val SERGIO_CARD_URL = "https://app.tangem.com/cards/card_tg059.png" const val SERGIO_CARD_URL = "https://app.tangem.com/cards/card_tg059.png"
const val MARTA_CARD_URL = "https://app.tangem.com/cards/card_tg083.png" const val MARTA_CARD_URL = "https://app.tangem.com/cards/card_tg083.png"
const val TWIN_CARD_1_URL = "https://app.tangem.com/cards/card_tg085.png"
const val TWIN_CARD_2_URL = "https://app.tangem.com/cards/card_tg086.png"
const val SERGIO_CARD_ID = "BC01" const val SERGIO_CARD_ID = "BC01"
const val MARTA_CARD_ID = "BC02" const val MARTA_CARD_ID = "BC02"
const val TWIN_CARD_1 = "https://app.tangem.com/cards/card_tg085.png"
const val TWIN_CARD_2 = "https://app.tangem.com/cards/card_tg086.png"
} }
} }

View file

@ -2,5 +2,7 @@ package com.tangem.domain.wallets.models
sealed interface UpdateWalletError { sealed interface UpdateWalletError {
object DataError : UpdateWalletError data object DataError : UpdateWalletError
data object NameAlreadyExists : UpdateWalletError
} }

View file

@ -0,0 +1,11 @@
package com.tangem.domain.wallets.repository
/**
* Access to migrate names flag
*/
interface WalletNamesMigrationRepository {
suspend fun isMigrationDone(): Boolean
suspend fun setMigrationDone()
}

View file

@ -0,0 +1,60 @@
package com.tangem.domain.wallets.usecase
import com.tangem.domain.models.scan.ProductType
import com.tangem.domain.wallets.legacy.UserWalletsListManager
/**
* Use case for user wallet name generation
*/
class GenerateWalletNameUseCase(
private val userWalletsListManager: UserWalletsListManager,
) {
operator fun invoke(productType: ProductType, isBackupNotAllowed: Boolean, isStartToCoin: Boolean): String {
val defaultName = getDefaultName(
productType = productType,
isBackupNotAllowed = isBackupNotAllowed,
isStartToCoin = isStartToCoin,
)
val existingNames = userWalletsListManager.userWalletsSync.map { it.name }.toSet()
return suggestedWalletName(defaultName, existingNames)
}
private fun suggestedWalletName(defaultName: String, existingNames: Set<String>): String {
val startIndex = 2
if (!existingNames.contains(defaultName)) {
return defaultName
}
for (index in startIndex..MAX_WALLETS_LIMIT) {
val potentialName = "$defaultName $index"
if (!existingNames.contains(potentialName)) {
return potentialName
}
}
return defaultName
}
private fun getDefaultName(productType: ProductType, isBackupNotAllowed: Boolean, isStartToCoin: Boolean): String {
return when (productType) {
ProductType.Note -> "Note"
ProductType.Twins -> "Twin"
ProductType.Start2Coin -> "Start2Coin"
ProductType.Visa -> "Tangem Visa"
ProductType.Wallet,
ProductType.Wallet2,
ProductType.Ring,
-> when {
isBackupNotAllowed -> "Tangem card"
isStartToCoin -> "Start2Coin"
else -> "Wallet"
}
}
}
companion object {
const val MAX_WALLETS_LIMIT = 10000
}
}

View file

@ -1,9 +1,10 @@
package com.tangem.domain.userwallets package com.tangem.domain.wallets.usecase
import com.tangem.common.extensions.toHexString import com.tangem.common.extensions.toHexString
import com.tangem.common.services.Result import com.tangem.common.services.Result
import com.tangem.domain.common.TwinCardNumber import com.tangem.domain.common.TwinCardNumber
import com.tangem.domain.common.TwinsHelper import com.tangem.domain.common.TwinsHelper
import com.tangem.domain.wallets.models.Artwork
import com.tangem.operations.attestation.OnlineCardVerifier import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.operations.attestation.TangemApi import com.tangem.operations.attestation.TangemApi
@ -42,8 +43,8 @@ class GetCardImageUseCase(private val verifier: OnlineCardVerifier = OnlineCardV
cardId.startsWith(Artwork.SERGIO_CARD_ID) -> Artwork.SERGIO_CARD_URL cardId.startsWith(Artwork.SERGIO_CARD_ID) -> Artwork.SERGIO_CARD_URL
cardId.startsWith(Artwork.MARTA_CARD_ID) -> Artwork.MARTA_CARD_URL cardId.startsWith(Artwork.MARTA_CARD_ID) -> Artwork.MARTA_CARD_URL
else -> when (TwinsHelper.getTwinCardNumber(cardId)) { else -> when (TwinsHelper.getTwinCardNumber(cardId)) {
TwinCardNumber.First -> Artwork.TWIN_CARD_1 TwinCardNumber.First -> Artwork.TWIN_CARD_1_URL
TwinCardNumber.Second -> Artwork.TWIN_CARD_2 TwinCardNumber.Second -> Artwork.TWIN_CARD_2_URL
else -> Artwork.DEFAULT_IMG_URL else -> Artwork.DEFAULT_IMG_URL
} }
} }

View file

@ -0,0 +1,13 @@
package com.tangem.domain.wallets.usecase
import com.tangem.domain.wallets.legacy.UserWalletsListManager
/**
* Use case for getting list of user wallets names.
*
* @property userWalletsListManager user wallets list manager
*/
class GetWalletNamesUseCase(private val userWalletsListManager: UserWalletsListManager) {
operator fun invoke(): List<String> = userWalletsListManager.userWalletsSync.map { it.name }
}

View file

@ -0,0 +1,36 @@
package com.tangem.domain.wallets.usecase
import arrow.core.Either
import arrow.core.left
import arrow.core.raise.either
import arrow.core.right
import com.tangem.common.doOnFailure
import com.tangem.common.doOnSuccess
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.models.UpdateWalletError
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
/**
* Use case for rename user wallet
*
* @property userWalletsListManager user wallets list manager
*/
class RenameWalletUseCase(private val userWalletsListManager: UserWalletsListManager) {
suspend operator fun invoke(userWalletId: UserWalletId, name: String): Either<UpdateWalletError, UserWallet> {
val existingNames = userWalletsListManager.userWalletsSync
if (existingNames.any { it.name == name && it.walletId != userWalletId }) {
return UpdateWalletError.NameAlreadyExists.left()
}
return either {
userWalletsListManager.update(userWalletId) { it.copy(name = name) }
.doOnSuccess { return it.right() }
.doOnFailure { return UpdateWalletError.DataError.left() }
return UpdateWalletError.DataError.left()
}
}
}

View file

@ -6,7 +6,8 @@ import arrow.core.raise.ensure
import com.tangem.common.CompletionResult import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemSdkError import com.tangem.common.core.TangemSdkError
import com.tangem.domain.card.ScanCardProcessor import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.userwallets.UserWalletBuilder import com.tangem.domain.wallets.builder.UserWalletBuilder
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.SaveWalletUseCase import com.tangem.domain.wallets.usecase.SaveWalletUseCase
import javax.inject.Inject import javax.inject.Inject
@ -14,6 +15,7 @@ import javax.inject.Inject
internal class ScanCardToUnlockWalletClickHandler @Inject constructor( internal class ScanCardToUnlockWalletClickHandler @Inject constructor(
private val scanCardProcessor: ScanCardProcessor, private val scanCardProcessor: ScanCardProcessor,
private val saveWalletUseCase: SaveWalletUseCase, private val saveWalletUseCase: SaveWalletUseCase,
private val generateWalletNameUseCase: GenerateWalletNameUseCase,
) { ) {
private var scanFailsCounter = 0 private var scanFailsCounter = 0
@ -31,7 +33,7 @@ internal class ScanCardToUnlockWalletClickHandler @Inject constructor(
scanFailsCounter = 0 scanFailsCounter = 0
// If card's public key is null then user wallet will be null // If card's public key is null then user wallet will be null
val scannedWallet = UserWalletBuilder(scanResponse = result.data).build() val scannedWallet = UserWalletBuilder(result.data, generateWalletNameUseCase).build()
ensure(walletId == scannedWallet?.walletId) { ensure(walletId == scannedWallet?.walletId) {
ScanCardToUnlockWalletError.WrongCardIsScanned ScanCardToUnlockWalletError.WrongCardIsScanned

View file

@ -0,0 +1,49 @@
package com.tangem.feature.wallet.presentation.wallet.domain
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.repository.WalletNamesMigrationRepository
import timber.log.Timber
class WalletNameMigrationUseCase(
private val userWalletsListManager: UserWalletsListManager,
private val walletNamesMigrationRepository: WalletNamesMigrationRepository,
) {
suspend operator fun invoke() {
val wallets = userWalletsListManager.userWalletsSync
if (walletNamesMigrationRepository.isMigrationDone()) {
return
}
val existingNames: MutableSet<String> = mutableSetOf()
wallets.indices.forEach { i ->
val defaultName = wallets[i].name
val suggestedWalletName = suggestedWalletName(defaultName, existingNames)
if (defaultName != suggestedWalletName) {
userWalletsListManager.update(wallets[i].walletId) { it.copy(name = suggestedWalletName) }
}
Timber.tag("Migrated names").e(i.toString() + " " + suggestedWalletName)
}
walletNamesMigrationRepository.setMigrationDone()
}
private fun suggestedWalletName(defaultName: String, existingNames: MutableSet<String>): String {
val startIndex = 1
for (index in startIndex..MAX_WALLETS_LIMIT) {
val name = if (index == startIndex) defaultName else "$defaultName $index"
if (!existingNames.contains(name)) {
existingNames.add(name)
return name
}
}
return defaultName
}
companion object {
const val MAX_WALLETS_LIMIT = 10000
}
}

View file

@ -25,6 +25,7 @@ internal sealed interface WalletAlertState {
open val text: String = "" open val text: String = ""
open val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok) open val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok)
abstract val onConfirmClick: (String) -> Unit abstract val onConfirmClick: (String) -> Unit
abstract val errorTextProvider: (String) -> TextReference?
} }
data class DefaultAlert( data class DefaultAlert(
@ -36,6 +37,7 @@ internal sealed interface WalletAlertState {
data class RenameWalletAlert( data class RenameWalletAlert(
override val text: String, override val text: String,
override val onConfirmClick: (String) -> Unit, override val onConfirmClick: (String) -> Unit,
override val errorTextProvider: (String) -> TextReference?,
) : TextInput() { ) : TextInput() {
override val title: TextReference = resourceReference(id = R.string.user_wallet_list_rename_popup_title) override val title: TextReference = resourceReference(id = R.string.user_wallet_list_rename_popup_title)
override val label: TextReference = resourceReference(id = R.string.user_wallet_list_rename_popup_placeholder) override val label: TextReference = resourceReference(id = R.string.user_wallet_list_rename_popup_placeholder)

View file

@ -64,7 +64,9 @@ private fun TextInputAlert(state: WalletAlertState.TextInput, onDismiss: () -> U
fieldValue = value, fieldValue = value,
confirmButton = DialogButton( confirmButton = DialogButton(
title = state.confirmButtonText.resolveReference(), title = state.confirmButtonText.resolveReference(),
enabled = value.text.isNotEmpty() && value.text != state.text, enabled = value.text.isNotEmpty() &&
value.text != state.text &&
state.errorTextProvider(value.text) == null,
onClick = { onClick = {
state.onConfirmClick(value.text) state.onConfirmClick(value.text)
onDismiss() onDismiss()
@ -76,6 +78,8 @@ private fun TextInputAlert(state: WalletAlertState.TextInput, onDismiss: () -> U
dismissButton = DialogButton(title = stringResource(id = R.string.common_cancel), onClick = onDismiss), dismissButton = DialogButton(title = stringResource(id = R.string.common_cancel), onClick = onDismiss),
textFieldParams = AdditionalTextInputDialogParams( textFieldParams = AdditionalTextInputDialogParams(
label = state.label.resolveReference(), label = state.label.resolveReference(),
isError = state.errorTextProvider(value.text) != null,
caption = state.errorTextProvider(value.text)?.resolveReference(),
), ),
) )
} }

View file

@ -15,6 +15,7 @@ import com.tangem.feature.wallet.presentation.deeplink.WalletDeepLinksHandler
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.SelectedWalletAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.analytics.utils.SelectedWalletAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.domain.WalletNameMigrationUseCase
import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent
@ -57,6 +58,7 @@ internal class WalletViewModel @Inject constructor(
private val screenLifecycleProvider: ScreenLifecycleProvider, private val screenLifecycleProvider: ScreenLifecycleProvider,
private val selectedWalletAnalyticsSender: SelectedWalletAnalyticsSender, private val selectedWalletAnalyticsSender: SelectedWalletAnalyticsSender,
private val walletDeepLinksHandler: WalletDeepLinksHandler, private val walletDeepLinksHandler: WalletDeepLinksHandler,
private val walletNameMigrationUseCase: WalletNameMigrationUseCase,
) : ViewModel() { ) : ViewModel() {
val uiState: StateFlow<WalletScreenState> = stateHolder.uiState val uiState: StateFlow<WalletScreenState> = stateHolder.uiState
@ -71,12 +73,19 @@ internal class WalletViewModel @Inject constructor(
suggestToEnableBiometrics() suggestToEnableBiometrics()
maybeMigrateNames()
subscribeToUserWalletsUpdates() subscribeToUserWalletsUpdates()
subscribeOnBalanceHiding() subscribeOnBalanceHiding()
subscribeOnSelectedWalletFlow() subscribeOnSelectedWalletFlow()
subscribeToScreenBackgroundState() subscribeToScreenBackgroundState()
} }
private fun maybeMigrateNames() {
viewModelScope.launch {
walletNameMigrationUseCase()
}
}
fun setWalletRouter(router: InnerWalletRouter) { fun setWalletRouter(router: InnerWalletRouter) {
this.router = router this.router = router
clickIntents.initialize(router, viewModelScope) clickIntents.initialize(router, viewModelScope)

View file

@ -2,6 +2,11 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents
import arrow.core.getOrElse import arrow.core.getOrElse
import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.domain.wallets.usecase.GetWalletNamesUseCase
import com.tangem.domain.wallets.usecase.RenameWalletUseCase
import com.tangem.feature.wallet.impl.R
import com.tangem.domain.card.DeleteSavedAccessCodesUseCase import com.tangem.domain.card.DeleteSavedAccessCodesUseCase
import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.NavigationAction
@ -11,7 +16,6 @@ import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.DeleteWalletUseCase import com.tangem.domain.wallets.usecase.DeleteWalletUseCase
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.UpdateWalletUseCase
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen
import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
@ -40,9 +44,10 @@ internal class WalletCardClickIntentsImplementor @Inject constructor(
private val stateHolder: WalletStateController, private val stateHolder: WalletStateController,
private val walletEventSender: WalletEventSender, private val walletEventSender: WalletEventSender,
private val walletScreenContentLoader: WalletScreenContentLoader, private val walletScreenContentLoader: WalletScreenContentLoader,
private val renameWalletUseCase: RenameWalletUseCase,
private val getWalletNamesUseCase: GetWalletNamesUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase, private val getUserWalletUseCase: GetUserWalletUseCase,
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
private val updateWalletUseCase: UpdateWalletUseCase,
private val deleteWalletUseCase: DeleteWalletUseCase, private val deleteWalletUseCase: DeleteWalletUseCase,
private val deleteSavedAccessCodesUseCase: DeleteSavedAccessCodesUseCase, private val deleteSavedAccessCodesUseCase: DeleteSavedAccessCodesUseCase,
private val analyticsEventHandler: AnalyticsEventHandler, private val analyticsEventHandler: AnalyticsEventHandler,
@ -54,19 +59,33 @@ internal class WalletCardClickIntentsImplementor @Inject constructor(
override fun onRenameBeforeConfirmationClick(userWalletId: UserWalletId) { override fun onRenameBeforeConfirmationClick(userWalletId: UserWalletId) {
analyticsEventHandler.send(MainScreen.EditWalletTapped) analyticsEventHandler.send(MainScreen.EditWalletTapped)
walletEventSender.send( viewModelScope.launch(dispatchers.main) {
event = WalletEvent.ShowAlert( val walletNames = getWalletNamesUseCase()
state = WalletAlertState.RenameWalletAlert( val currentWalletName = stateHolder.getSelectedWallet().walletCardState.title
text = stateHolder.getSelectedWallet().walletCardState.title, walletEventSender.send(
onConfirmClick = { onRenameAfterConfirmationClick(userWalletId, it) }, event = WalletEvent.ShowAlert(
state = WalletAlertState.RenameWalletAlert(
text = currentWalletName,
onConfirmClick = { onRenameAfterConfirmationClick(userWalletId, it) },
errorTextProvider = { enteredName ->
if (walletNames.contains(enteredName) && enteredName != currentWalletName) {
resourceReference(
R.string.user_wallet_list_rename_popup_error_already_exists,
wrappedList(enteredName),
)
} else {
null
}
},
),
), ),
), )
) }
} }
override fun onRenameAfterConfirmationClick(userWalletId: UserWalletId, name: String) { override fun onRenameAfterConfirmationClick(userWalletId: UserWalletId, name: String) {
viewModelScope.launch(dispatchers.main) { viewModelScope.launch(dispatchers.main) {
updateWalletUseCase(userWalletId = userWalletId, update = { it.copy(name = name) }) renameWalletUseCase(userWalletId = userWalletId, name)
} }
} }