Updated on 2026-08-14

This commit is contained in:
Tangem 2025-10-22 15:42:04 +04:00
parent bd33dadcfe
commit 5a62376bfa
18 changed files with 194 additions and 163 deletions

View file

@ -1,6 +1,5 @@
package com.tangem.features.account.archived
import com.tangem.common.ui.account.toUM
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
import com.tangem.core.decompose.model.Model
@ -15,11 +14,8 @@ import com.tangem.core.ui.message.EventMessageAction
import com.tangem.core.ui.message.ToastMessage
import com.tangem.core.ui.utils.showErrorDialog
import com.tangem.domain.account.models.AccountList
import com.tangem.domain.account.models.ArchivedAccount
import com.tangem.domain.account.usecase.ArchivedAccountList
import com.tangem.domain.account.usecase.GetArchivedAccountsUseCase
import com.tangem.domain.account.usecase.RecoverCryptoPortfolioUseCase
import com.tangem.domain.core.lce.Lce
import com.tangem.domain.models.account.AccountId
import com.tangem.features.account.ArchivedAccountListComponent
import com.tangem.features.account.archived.entity.AccountArchivedUM
@ -30,6 +26,7 @@ import com.tangem.utils.coroutines.JobHolder
import com.tangem.utils.coroutines.saveIn
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
import javax.inject.Inject
@ -61,25 +58,32 @@ internal class ArchivedAccountListModel @Inject constructor(
.conflate()
.distinctUntilChanged()
.onEach { lce ->
val newState = when (lce) {
is Lce.Content<ArchivedAccountList> -> umBuilder.mapContent(
accounts = lce.content,
onCloseClick = onCloseClick,
confirmRecoverDialog = { confirmRecoverDialog(it) },
)
is Lce.Error<Throwable> -> umBuilder.mapError(
throwable = lce.error,
onCloseClick = onCloseClick,
getArchivedAccounts = { getArchivedAccounts() },
)
is Lce.Loading<ArchivedAccountList> -> lce.partialContent?.let { content ->
val newState = lce.fold(
ifLoading = { content ->
content ?: return@fold null
umBuilder.mapContent(
accounts = content,
onCloseClick = onCloseClick,
confirmRecoverDialog = { confirmRecoverDialog(it) },
onRecoverClick = { recoverCryptoPortfolio(accountId = it.accountId) },
)
}
}
},
ifContent = { content ->
umBuilder.mapContent(
accounts = content,
onCloseClick = onCloseClick,
onRecoverClick = { recoverCryptoPortfolio(accountId = it.accountId) },
)
},
ifError = { error ->
umBuilder.mapError(
throwable = error,
onCloseClick = onCloseClick,
getArchivedAccounts = { getArchivedAccounts() },
)
},
)
newState?.let { _uiState.value = newState }
}
.flowOn(dispatchers.default)
@ -87,30 +91,10 @@ internal class ArchivedAccountListModel @Inject constructor(
.saveIn(getArchivedAccountsJob)
}
private fun confirmRecoverDialog(account: ArchivedAccount) {
val secondAction = EventMessageAction(
title = resourceReference(R.string.common_cancel),
onClick = {},
)
val firstAction = EventMessageAction(
title = resourceReference(R.string.account_archived_recover),
onClick = { recoverCryptoPortfolio(account.accountId) },
)
messageSender.send(
DialogMessage(
title = resourceReference(R.string.account_archived_recover_dialog_title),
message = resourceReference(
id = R.string.account_archived_recover_dialog_description,
formatArgs = wrappedList(account.name.toUM().value),
),
firstActionBuilder = { firstAction },
secondActionBuilder = { secondAction },
),
)
}
private fun recoverCryptoPortfolio(accountId: AccountId) = modelScope.launch(dispatchers.default) {
recoverCryptoPortfolioUseCase(accountId)
private fun recoverCryptoPortfolio(accountId: AccountId) = modelScope.launch {
withContext(dispatchers.default) {
recoverCryptoPortfolioUseCase(accountId)
}
.onLeft(::handleRecoverError)
.onRight {
showSuccessRecoverMessage()
@ -122,8 +106,22 @@ internal class ArchivedAccountListModel @Inject constructor(
if (error is RecoverCryptoPortfolioUseCase.Error.AccountListRequirementsNotMet &&
error.cause is AccountList.Error.ExceedsMaxAccountsCount
) {
// TODO("account") show alert that max accounts count reached
// https://www.figma.com/design/09KKG4ZVuFDZhj8WLv5rGJ/%F0%9F%9A%A7-App-experience?node-id=24765-180563&t=vk6TCy4MkYol1cPb-4
val firstAction = EventMessageAction(
title = resourceReference(R.string.common_got_it),
onClick = { },
)
messageSender.send(
DialogMessage(
title = resourceReference(R.string.account_recover_limit_dialog_title),
message = resourceReference(
id = R.string.account_recover_limit_dialog_description,
formatArgs = wrappedList(AccountList.MAX_ACCOUNTS_COUNT.toString()),
),
firstActionBuilder = { firstAction },
),
)
return
}

View file

@ -15,15 +15,15 @@ internal class AccountArchivedUMBuilder @Inject constructor() {
fun mapContent(
accounts: ArchivedAccountList,
onCloseClick: () -> Unit,
confirmRecoverDialog: (account: ArchivedAccount) -> Unit,
onRecoverClick: (account: ArchivedAccount) -> Unit,
) = AccountArchivedUM.Content(
onCloseClick = onCloseClick,
accounts = accounts
.map { account -> account.mapArchivedAccountUM(confirmRecoverDialog) }
.map { account -> account.mapArchivedAccountUM(onRecoverClick) }
.toImmutableList(),
)
fun ArchivedAccount.mapArchivedAccountUM(confirmRecoverDialog: (account: ArchivedAccount) -> Unit) =
private fun ArchivedAccount.mapArchivedAccountUM(onRecoverClick: (account: ArchivedAccount) -> Unit) =
ArchivedAccountUM(
accountId = accountId.value,
accountName = name.toUM().value,
@ -38,7 +38,7 @@ internal class AccountArchivedUMBuilder @Inject constructor() {
count = networksCount,
formatArgs = wrappedList(networksCount),
),
onClick = { confirmRecoverDialog(this) },
onClick = { onRecoverClick(this) },
)
fun mapError(

View file

@ -64,7 +64,6 @@ internal class PreviewWalletSettingsComponent : WalletSettingsComponent {
onReferralClick = {},
isManageTokensAvailable = true,
isNotificationsEnabled = true,
isNotificationsFeatureEnabled = true,
onCheckedNotificationsChanged = {},
onNotificationsDescriptionClick = {},
isNotificationsPermissionGranted = false,

View file

@ -48,11 +48,13 @@ import com.tangem.hot.sdk.model.HotWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@OptIn(ExperimentalCoroutinesApi::class)
@Suppress("LongParameterList", "LargeClass")
@ModelScoped
internal class WalletSettingsModel @Inject constructor(
@ -128,11 +130,11 @@ internal class WalletSettingsModel @Inject constructor(
}
fun combineUI(wallet: UserWallet) = combine(
getWalletNFTEnabledUseCase.invoke(params.userWalletId),
getWalletNotificationsEnabledUseCase(params.userWalletId),
isUpgradeWalletNotificationEnabledUseCase(params.userWalletId),
walletCardItemDelegate.cardItemFlow(wallet),
accountItemsDelegate.loadAccount(),
flow = getWalletNFTEnabledUseCase.invoke(params.userWalletId),
flow2 = getWalletNotificationsEnabledUseCase(params.userWalletId),
flow3 = isUpgradeWalletNotificationEnabledUseCase(params.userWalletId),
flow4 = walletCardItemDelegate.cardItemFlow(wallet),
flow5 = accountItemsDelegate.loadAccount(),
) { nftEnabled, notificationsEnabled, isUpgradeNotificationEnabled, cardItem, accountList ->
val isWalletBackedUp = when (wallet) {
is UserWallet.Hot -> wallet.backedUp
@ -145,7 +147,6 @@ internal class WalletSettingsModel @Inject constructor(
cardItem = cardItem,
isNFTEnabled = nftEnabled,
isNotificationsEnabled = notificationsEnabled,
isNotificationsFeatureEnabled = true,
isNotificationsPermissionGranted = isNotificationsPermissionGranted(),
isUpgradeNotificationEnabled = isUpgradeNotificationEnabled,
accountList = accountList,
@ -180,7 +181,6 @@ internal class WalletSettingsModel @Inject constructor(
userWallet: UserWallet,
cardItem: WalletSettingsItemUM.CardBlock,
isNFTEnabled: Boolean,
isNotificationsFeatureEnabled: Boolean,
isNotificationsEnabled: Boolean,
isNotificationsPermissionGranted: Boolean,
isUpgradeNotificationEnabled: Boolean,
@ -202,7 +202,11 @@ internal class WalletSettingsModel @Inject constructor(
is UserWallet.Cold -> userWallet.scanResponse.card.backupStatus == CardDTO.BackupStatus.NoBackup
is UserWallet.Hot -> false
},
isManageTokensAvailable = !accountsFeatureEnabled && isMultiCurrency,
isManageTokensAvailable = if (accountsFeatureEnabled) {
isMultiCurrency && accountList.count { it is WalletSettingsAccountsUM.Account } == 0
} else {
isMultiCurrency
},
isNFTFeatureEnabled = isMultiCurrency,
isNFTEnabled = isNFTEnabled,
onCheckedNFTChange = ::onCheckedNFTChange,
@ -217,7 +221,6 @@ internal class WalletSettingsModel @Inject constructor(
},
onReferralClick = { onReferralClick(userWallet) },
isNotificationsEnabled = isNotificationsEnabled,
isNotificationsFeatureEnabled = isNotificationsFeatureEnabled,
isNotificationsPermissionGranted = isNotificationsPermissionGranted,
onCheckedNotificationsChanged = ::onCheckedNotificationsChange,
onNotificationsDescriptionClick = ::onNotificationsDescriptionClick,

View file

@ -16,6 +16,7 @@ import com.tangem.domain.account.models.AccountList
import com.tangem.domain.account.models.AccountStatusList
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
@ -40,6 +41,7 @@ internal class AccountItemsDelegate @Inject constructor(
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
private val accountsFeatureToggles: AccountsFeatureToggles,
) {
val userWalletId = paramsContainer.require<WalletSettingsComponent.Params>().userWalletId
@ -51,8 +53,9 @@ internal class AccountItemsDelegate @Inject constructor(
flow = singleAccountStatusListSupplier(params),
flow2 = getSelectedAppCurrencyUseCase.invokeOrDefault(),
flow3 = getBalanceHidingSettingsUseCase.isBalanceHidden(),
) { accountStatusList, appCurrency, isBalanceHidden ->
buildUiList(accountStatusList, appCurrency, isBalanceHidden)
flow4 = isAccountsModeEnabledUseCase(),
) { accountStatusList, appCurrency, isBalanceHidden, isAccountsMode ->
buildUiList(accountStatusList, appCurrency, isBalanceHidden, isAccountsMode)
}
}
@ -60,6 +63,7 @@ internal class AccountItemsDelegate @Inject constructor(
accountStatusList: AccountStatusList,
appCurrency: AppCurrency,
isBalanceHidden: Boolean,
isAccountsMode: Boolean,
): List<WalletSettingsAccountsUM> = buildList {
fun AccountStatus.CryptoPortfolio.mapCryptoPortfolio(): WalletSettingsAccountsUM {
val accountItemUM = AccountPortfolioItemUMConverter(
@ -81,7 +85,9 @@ internal class AccountItemsDelegate @Inject constructor(
text = resourceReference(R.string.common_accounts),
).let(::add)
addAll(accounts.map(::mapAccount))
if (isAccountsMode) {
addAll(accounts.map(::mapAccount))
}
val addAccountEnabled = accounts.size < AccountList.MAX_ACCOUNTS_COUNT
val showDescription = accounts.size > 1

View file

@ -38,7 +38,6 @@ internal class ItemsBuilder @Inject constructor(
isNFTFeatureEnabled: Boolean,
isNFTEnabled: Boolean,
onCheckedNFTChange: (Boolean) -> Unit,
isNotificationsFeatureEnabled: Boolean,
isNotificationsEnabled: Boolean,
isNotificationsPermissionGranted: Boolean,
onCheckedNotificationsChanged: (Boolean) -> Unit,
@ -74,7 +73,6 @@ internal class ItemsBuilder @Inject constructor(
)
.addAll(
buildNotificationItems(
isNotificationsFeatureEnabled = isNotificationsFeatureEnabled,
isNotificationsPermissionGranted = isNotificationsPermissionGranted,
isNotificationsEnabled = isNotificationsEnabled,
onCheckedNotificationsChanged = onCheckedNotificationsChanged,
@ -103,13 +101,11 @@ internal class ItemsBuilder @Inject constructor(
}
private fun buildNotificationItems(
isNotificationsFeatureEnabled: Boolean,
isNotificationsPermissionGranted: Boolean,
isNotificationsEnabled: Boolean,
onCheckedNotificationsChanged: (Boolean) -> Unit,
onNotificationsDescriptionClick: () -> Unit,
): List<WalletSettingsItemUM> {
if (!isNotificationsFeatureEnabled) return emptyList()
return buildList {
if (!isNotificationsPermissionGranted) {
add(buildNotificationsPermissionItem())