diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/AccountsMigrationStoreModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/AccountsMigrationStoreModule.kt new file mode 100644 index 0000000000..f97fb1dc72 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/di/AccountsMigrationStoreModule.kt @@ -0,0 +1,23 @@ +package com.tangem.datasource.di + +import com.tangem.datasource.local.accounts.AccountTokenMigrationStore +import com.tangem.datasource.local.accounts.DefaultAccountTokenMigrationStore +import com.tangem.datasource.local.datastore.RuntimeStateStore +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object AccountsMigrationStoreModule { + + @Provides + @Singleton + fun provideAccountTokenMigrationStore(): AccountTokenMigrationStore { + return DefaultAccountTokenMigrationStore( + runtimeStateStore = RuntimeStateStore(emptyMap()), + ) + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/accounts/AccountTokenMigrationStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/accounts/AccountTokenMigrationStore.kt new file mode 100644 index 0000000000..c527ef5dec --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/accounts/AccountTokenMigrationStore.kt @@ -0,0 +1,13 @@ +package com.tangem.datasource.local.accounts + +import com.tangem.domain.models.wallet.UserWalletId +import kotlinx.coroutines.flow.Flow + +interface AccountTokenMigrationStore { + + fun get(userWalletId: UserWalletId): Flow?> + + suspend fun store(userWalletId: UserWalletId, value: Pair) + + suspend fun remove(userWalletId: UserWalletId) +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/accounts/DefaultAccountTokenMigrationStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/accounts/DefaultAccountTokenMigrationStore.kt new file mode 100644 index 0000000000..920b4ed166 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/accounts/DefaultAccountTokenMigrationStore.kt @@ -0,0 +1,23 @@ +package com.tangem.datasource.local.accounts + +import com.tangem.datasource.local.datastore.RuntimeStateStore +import com.tangem.domain.models.wallet.UserWalletId +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +internal class DefaultAccountTokenMigrationStore( + private val runtimeStateStore: RuntimeStateStore>>, +) : AccountTokenMigrationStore { + + override fun get(userWalletId: UserWalletId): Flow?> { + return runtimeStateStore.get().map { it[userWalletId] } + } + + override suspend fun store(userWalletId: UserWalletId, value: Pair) { + runtimeStateStore.update { it + (userWalletId to value) } + } + + override suspend fun remove(userWalletId: UserWalletId) { + runtimeStateStore.update { it - userWalletId } + } +} \ No newline at end of file diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml index 797ee47246..211f153257 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -269,7 +269,7 @@ 無料 送信元 アドレスを同期する - もっと詳しく + はじめる トークンを取得 プロバイダーへ移動 トークンへ移動 diff --git a/data/account/src/main/kotlin/com/tangem/data/account/di/AccountDataModule.kt b/data/account/src/main/kotlin/com/tangem/data/account/di/AccountDataModule.kt index e85b397865..5dc4c61679 100644 --- a/data/account/src/main/kotlin/com/tangem/data/account/di/AccountDataModule.kt +++ b/data/account/src/main/kotlin/com/tangem/data/account/di/AccountDataModule.kt @@ -13,6 +13,7 @@ import com.tangem.data.common.account.WalletAccountsFetcher import com.tangem.data.common.account.WalletAccountsSaver import com.tangem.data.common.currency.UserTokensSaver import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.datasource.local.accounts.AccountTokenMigrationStore import com.tangem.datasource.local.datastore.RuntimeStateStore import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles @@ -75,9 +76,11 @@ internal object AccountDataModule { fun provideMainAccountTokensMigration( accountsResponseStoreFactory: AccountsResponseStoreFactory, userTokensSaver: UserTokensSaver, + accountTokenMigrationStore: AccountTokenMigrationStore, ): MainAccountTokensMigration { return DefaultMainAccountTokensMigration( accountsResponseStoreFactory = accountsResponseStoreFactory, + accountTokenMigrationStore = accountTokenMigrationStore, userTokensSaver = userTokensSaver, ) } diff --git a/data/account/src/main/kotlin/com/tangem/data/account/tokens/DefaultMainAccountTokensMigration.kt b/data/account/src/main/kotlin/com/tangem/data/account/tokens/DefaultMainAccountTokensMigration.kt index 93b3735d63..d1a0449ebc 100644 --- a/data/account/src/main/kotlin/com/tangem/data/account/tokens/DefaultMainAccountTokensMigration.kt +++ b/data/account/src/main/kotlin/com/tangem/data/account/tokens/DefaultMainAccountTokensMigration.kt @@ -14,6 +14,7 @@ import com.tangem.datasource.api.tangemTech.models.UserTokensResponse import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO import com.tangem.datasource.api.tangemTech.models.account.toUserTokensResponse +import com.tangem.datasource.local.accounts.AccountTokenMigrationStore import com.tangem.datasource.utils.getSyncOrNull import com.tangem.domain.account.tokens.MainAccountTokensMigration import com.tangem.domain.models.account.DerivationIndex @@ -32,6 +33,7 @@ import timber.log.Timber */ internal class DefaultMainAccountTokensMigration( private val accountsResponseStoreFactory: AccountsResponseStoreFactory, + private val accountTokenMigrationStore: AccountTokenMigrationStore, private val userTokensSaver: UserTokensSaver, ) : MainAccountTokensMigration { @@ -80,6 +82,12 @@ internal class DefaultMainAccountTokensMigration( store.updateData { updatedResponse } + val mainAccountName = mainAccount.name + val selectedAccountName = selectedAccount.name + if (mainAccountName != null && selectedAccountName != null) { + accountTokenMigrationStore.store(userWalletId, mainAccountName to selectedAccountName) + } + val userTokensResponse = updatedResponse.toUserTokensResponse() userTokensSaver.pushWithRetryer( userWalletId = userWalletId, diff --git a/data/account/src/test/java/com/tangem/data/account/token/DefaultMainAccountTokensMigrationTest.kt b/data/account/src/test/java/com/tangem/data/account/token/DefaultMainAccountTokensMigrationTest.kt index 5e59689b19..7a9ff370d7 100644 --- a/data/account/src/test/java/com/tangem/data/account/token/DefaultMainAccountTokensMigrationTest.kt +++ b/data/account/src/test/java/com/tangem/data/account/token/DefaultMainAccountTokensMigrationTest.kt @@ -11,6 +11,7 @@ import com.tangem.data.common.currency.UserTokensSaver import com.tangem.datasource.api.tangemTech.models.UserTokensResponse import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse import com.tangem.datasource.api.tangemTech.models.account.toUserTokensResponse +import com.tangem.datasource.local.accounts.AccountTokenMigrationStore import com.tangem.domain.models.account.AccountId import com.tangem.domain.models.account.DerivationIndex import com.tangem.domain.models.wallet.UserWalletId @@ -29,11 +30,12 @@ class DefaultMainAccountTokensMigrationTest { private val accountsResponseStoreFactory = mockk() private val accountsResponseStore = mockk() private val accountsResponseStoreFlow = MutableStateFlow(value = null) - + private val accountTokenMigrationStore = mockk(relaxUnitFun = true) private val userTokensSaver = mockk(relaxUnitFun = true) private val migration = DefaultMainAccountTokensMigration( accountsResponseStoreFactory = accountsResponseStoreFactory, + accountTokenMigrationStore = accountTokenMigrationStore, userTokensSaver = userTokensSaver, ) diff --git a/features/wallet-settings/impl/build.gradle.kts b/features/wallet-settings/impl/build.gradle.kts index 91c5e3db22..fb82ff618a 100644 --- a/features/wallet-settings/impl/build.gradle.kts +++ b/features/wallet-settings/impl/build.gradle.kts @@ -29,6 +29,7 @@ dependencies { implementation(projects.core.navigation) implementation(projects.core.analytics) implementation(projects.core.analytics.models) + implementation(projects.core.datasource) implementation(projects.common.routing) implementation(projects.common.ui) diff --git a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/component/impl/DefaultWalletSettingsComponent.kt b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/component/impl/DefaultWalletSettingsComponent.kt index 1cb8c2c145..3c054dc1b7 100644 --- a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/component/impl/DefaultWalletSettingsComponent.kt +++ b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/component/impl/DefaultWalletSettingsComponent.kt @@ -9,33 +9,52 @@ import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.extensions.compose.subscribeAsState import com.arkivanov.decompose.router.slot.childSlot import com.arkivanov.decompose.router.slot.dismiss +import com.arkivanov.essenty.lifecycle.subscribe import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.childByContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.core.ui.decompose.ComposableDialogComponent +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.message.DialogMessage +import com.tangem.core.ui.message.EventMessageAction import com.tangem.core.ui.utils.requestPermission +import com.tangem.datasource.local.accounts.AccountTokenMigrationStore +import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles import com.tangem.feature.walletsettings.component.NetworksAvailableForNotificationsComponent import com.tangem.feature.walletsettings.component.RenameWalletComponent import com.tangem.feature.walletsettings.component.WalletSettingsComponent import com.tangem.feature.walletsettings.entity.DialogConfig import com.tangem.feature.walletsettings.entity.NetworksAvailableForNotificationBSConfig +import com.tangem.feature.walletsettings.impl.R import com.tangem.feature.walletsettings.model.WalletSettingsModel import com.tangem.feature.walletsettings.ui.WalletSettingsScreen import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION +import com.tangem.utils.coroutines.JobHolder +import com.tangem.utils.coroutines.saveIn import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch internal class DefaultWalletSettingsComponent @AssistedInject constructor( @Assisted context: AppComponentContext, - @Assisted params: WalletSettingsComponent.Params, + @Assisted private val params: WalletSettingsComponent.Params, private val renameWalletComponentFactory: RenameWalletComponent.Factory, private val networksAvailableForNotificationsComponent: NetworksAvailableForNotificationsComponent.Factory, + private val accountTokenMigrationStore: AccountTokenMigrationStore, + private val accountsFeatureToggles: AccountsFeatureToggles, ) : WalletSettingsComponent, AppComponentContext by context { private val model: WalletSettingsModel = getOrCreateModel(params) + private val accountMigrationJobHolder = JobHolder() + private val bottomSheetSlot = childSlot( source = model.bottomSheetNavigation, serializer = null, @@ -52,6 +71,17 @@ internal class DefaultWalletSettingsComponent @AssistedInject constructor( childFactory = ::dialogChild, ) + init { + lifecycle.subscribe( + onResume = { + if (accountsFeatureToggles.isFeatureEnabled) { + showMigrationAlertIfNeeded() + } + }, + onPause = { accountMigrationJobHolder.cancel() }, + ) + } + @Composable override fun Content(modifier: Modifier) { val state by model.state.collectAsStateWithLifecycle() @@ -106,6 +136,36 @@ internal class DefaultWalletSettingsComponent @AssistedInject constructor( ), ) + private fun showMigrationAlertIfNeeded() { + accountTokenMigrationStore.get(params.userWalletId) + .distinctUntilChanged() + .filterNotNull() + .onEach { (fromAccount, toAccount) -> + messageSender.send( + DialogMessage( + title = resourceReference(R.string.accounts_migration_alert_title), + message = resourceReference( + R.string.accounts_migration_alert_message, + wrappedList(fromAccount, toAccount), + ), + firstActionBuilder = { + EventMessageAction( + title = resourceReference(R.string.common_got_it), + onClick = { /* no-op */ }, + ) + }, + onDismissRequest = { + componentScope.launch { + accountTokenMigrationStore.remove(params.userWalletId) + } + }, + ), + ) + } + .launchIn(componentScope) + .saveIn(accountMigrationJobHolder) + } + @AssistedFactory interface Factory : WalletSettingsComponent.Factory { override fun create(