Updated on 2026-08-14

This commit is contained in:
Tangem 2025-10-02 20:46:41 +07:00
parent 82aae53d53
commit 38e82b9423
20 changed files with 198 additions and 142 deletions

View file

@ -33,6 +33,11 @@ dependencies {
implementation(projects.common.ui)
/* Project - Domain */
implementation(projects.domain.account.status)
implementation(projects.domain.appCurrency)
implementation(projects.domain.appCurrency.models)
implementation(projects.domain.balanceHiding)
implementation(projects.domain.balanceHiding.models)
implementation(projects.domain.legacy)
implementation(projects.domain.card)
implementation(projects.domain.models)

View file

@ -3,6 +3,7 @@ package com.tangem.feature.walletsettings.component.preview
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.tangem.common.ui.account.AccountIconPreviewData
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.common.ui.userwallet.state.UserWalletItemUM.ImageState
import com.tangem.core.analytics.DummyAnalyticsEventHandler
import com.tangem.core.decompose.navigation.DummyRouter
@ -20,9 +21,27 @@ import com.tangem.feature.walletsettings.impl.R
import com.tangem.feature.walletsettings.ui.WalletSettingsScreen
import com.tangem.feature.walletsettings.utils.ItemsBuilder
import com.tangem.hot.sdk.model.HotWalletId
import java.util.UUID
internal class PreviewWalletSettingsComponent : WalletSettingsComponent {
private val accountName get() = stringReference(value = "Main account")
private val accountItem: UserWalletItemUM
get() = UserWalletItemUM(
id = UserWalletId(UUID.randomUUID().toString().encodeToByteArray()),
name = accountName,
information = UserWalletItemUM.Information.Loaded(stringReference("12 tokens")),
balance = UserWalletItemUM.Balance.Loaded("$726.04", false),
isEnabled = true,
onClick = { },
imageState = ImageState.Account(
name = accountName,
icon = AccountIconPreviewData.randomAccountIcon(),
),
label = null,
)
private val previewState = WalletSettingsUM(
popBack = {},
items = ItemsBuilder(
@ -66,14 +85,7 @@ internal class PreviewWalletSettingsComponent : WalletSettingsComponent {
id = "accounts_header",
text = resourceReference(R.string.common_accounts),
).let(::add)
WalletSettingsAccountsUM.Account(
id = "accountId",
accountName = stringReference("Main account"),
accountIconUM = AccountIconPreviewData.randomAccountIcon(),
tokensInfo = stringReference("10 tokens"),
networksInfo = stringReference("2 networks"),
onClick = {},
).let(::add)
add(WalletSettingsAccountsUM.Account(accountItem))
WalletSettingsAccountsUM.Footer(
id = "accounts_footer",
addAccount = AddAccountUM(
@ -86,6 +98,7 @@ internal class PreviewWalletSettingsComponent : WalletSettingsComponent {
iconRes = R.drawable.ic_archive_24,
onClick = {},
),
showDescription = true,
description = resourceReference(R.string.account_reorder_description),
).let(::add)
}

View file

@ -1,7 +1,7 @@
package com.tangem.feature.walletsettings.entity
import androidx.compose.runtime.Immutable
import com.tangem.common.ui.account.CryptoPortfolioIconUM
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.common.ui.userwallet.state.UserWalletItemUM.ImageState
import com.tangem.core.ui.components.block.model.BlockUM
import com.tangem.core.ui.extensions.TextReference
@ -65,18 +65,16 @@ internal sealed class WalletSettingsAccountsUM : WalletSettingsItemUM() {
) : WalletSettingsAccountsUM()
data class Account(
override val id: String,
val accountName: TextReference,
val accountIconUM: CryptoPortfolioIconUM,
val tokensInfo: TextReference,
val networksInfo: TextReference,
val onClick: () -> Unit,
) : WalletSettingsAccountsUM()
val state: UserWalletItemUM,
) : WalletSettingsAccountsUM() {
override val id: String get() = state.id.stringValue
}
data class Footer(
override val id: String,
val addAccount: AddAccountUM,
val archivedAccounts: BlockUM,
val showDescription: Boolean,
val description: TextReference,
) : WalletSettingsAccountsUM() {

View file

@ -38,6 +38,7 @@ import com.tangem.feature.walletsettings.analytics.WalletSettingsAnalyticEvents
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.entity.WalletSettingsAccountsUM
import com.tangem.feature.walletsettings.entity.WalletSettingsItemUM
import com.tangem.feature.walletsettings.entity.WalletSettingsUM
import com.tangem.feature.walletsettings.impl.R
@ -123,7 +124,8 @@ internal class WalletSettingsModel @Inject constructor(
getWalletNotificationsEnabledUseCase(params.userWalletId),
isUpgradeWalletNotificationEnabledUseCase(params.userWalletId),
walletCardItemDelegate.cardItemFlow(wallet),
) { nftEnabled, notificationsEnabled, isUpgradeNotificationEnabled, cardItem ->
accountItemsDelegate.loadAccount(),
) { nftEnabled, notificationsEnabled, isUpgradeNotificationEnabled, cardItem, accountList ->
val isWalletBackedUp = when (wallet) {
is UserWallet.Hot -> wallet.backedUp
is UserWallet.Cold -> true
@ -138,6 +140,7 @@ internal class WalletSettingsModel @Inject constructor(
isNotificationsFeatureEnabled = true,
isNotificationsPermissionGranted = isNotificationsPermissionGranted(),
isUpgradeNotificationEnabled = isUpgradeNotificationEnabled,
accountList = accountList,
),
isWalletBackedUp = isWalletBackedUp,
)
@ -168,6 +171,7 @@ internal class WalletSettingsModel @Inject constructor(
isNotificationsEnabled: Boolean,
isNotificationsPermissionGranted: Boolean,
isUpgradeNotificationEnabled: Boolean,
accountList: List<WalletSettingsAccountsUM>,
): PersistentList<WalletSettingsItemUM> {
val isMultiCurrency = when (userWallet) {
is UserWallet.Cold -> userWallet.isMultiCurrency
@ -219,7 +223,7 @@ internal class WalletSettingsModel @Inject constructor(
walletUpgradeDismissed = isUpgradeNotificationEnabled,
onUpgradeWalletClick = ::onUpgradeWalletClick,
onDismissUpgradeWalletClick = ::onDismissUpgradeWalletClick,
accountsUM = with(accountItemsDelegate) { listOf() }, // todo account
accountsUM = accountList,
)
}

View file

@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
@ -22,9 +23,8 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.tangem.common.ui.account.AccountRow
import com.tangem.common.ui.userwallet.CardImage
import com.tangem.core.ui.components.SpacerH
import com.tangem.common.ui.userwallet.UserWalletItem
import com.tangem.core.ui.components.SpacerH8
import com.tangem.core.ui.components.TangemSwitch
import com.tangem.core.ui.components.appbar.TangemTopAppBar
@ -292,7 +292,7 @@ private fun AccountsHeader(model: WalletSettingsAccountsUM.Header, modifier: Mod
.padding(
start = TangemTheme.dimens.spacing12,
end = TangemTheme.dimens.spacing12,
top = TangemTheme.dimens.spacing8,
top = TangemTheme.dimens.spacing12,
bottom = TangemTheme.dimens.spacing4,
),
text = model.text.resolveReference(),
@ -305,21 +305,10 @@ private fun AccountsHeader(model: WalletSettingsAccountsUM.Header, modifier: Mod
@Composable
private fun AccountItem(model: WalletSettingsAccountsUM.Account, modifier: Modifier = Modifier) {
val subtitle = stringResourceSafe(
id = R.string.account_label_tokens_info,
formatArgs = arrayOf(
model.tokensInfo.resolveReference(),
model.networksInfo.resolveReference(),
),
)
AccountRow(
UserWalletItem(
state = model.state,
modifier = modifier
.background(color = TangemTheme.colors.background.primary)
.clickable(onClick = model.onClick)
.padding(12.dp),
title = model.accountName,
subtitle = stringReference(subtitle),
icon = model.accountIconUM,
.background(color = TangemTheme.colors.background.primary),
)
}
@ -336,18 +325,19 @@ private fun AccountsFooter(model: WalletSettingsAccountsUM.Footer, modifier: Mod
),
) {
AddAccountRow(model.addAccount)
SpacerH(
height = TangemTheme.dimens.size0_5,
HorizontalDivider(
thickness = TangemTheme.dimens.size0_5,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = TangemTheme.dimens.spacing12)
.background(TangemTheme.colors.stroke.primary),
.padding(horizontal = TangemTheme.dimens.spacing12),
color = TangemTheme.colors.stroke.primary,
)
BlockItem(
modifier = Modifier.fillMaxWidth(),
model = model.archivedAccounts,
)
}
if (!model.showDescription) return
SpacerH8()
Text(
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing12),

View file

@ -1,30 +1,81 @@
package com.tangem.feature.walletsettings.utils
import com.tangem.common.routing.AppRoute
import com.tangem.common.ui.account.toUM
import com.tangem.common.ui.account.AccountPortfolioItemUMConverter
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.components.block.model.BlockUM
import com.tangem.core.ui.extensions.pluralReference
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.domain.account.featuretoggle.AccountsFeatureToggles
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.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.feature.walletsettings.component.WalletSettingsComponent
import com.tangem.feature.walletsettings.entity.WalletSettingsAccountsUM
import com.tangem.feature.walletsettings.entity.WalletSettingsAccountsUM.Footer.AddAccountUM
import com.tangem.feature.walletsettings.impl.R
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOf
import javax.inject.Inject
@Suppress("LongParameterList")
@ModelScoped
internal class AccountItemsDelegate @Inject constructor(
paramsContainer: ParamsContainer,
private val router: Router,
private val messageSender: UiMessageSender,
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val accountsFeatureToggles: AccountsFeatureToggles,
) {
val userWalletId = paramsContainer.require<WalletSettingsComponent.Params>().userWalletId
fun buildUiList(userWalletId: UserWalletId, accounts: List<Account>): List<WalletSettingsAccountsUM> = buildList {
fun loadAccount(): Flow<List<WalletSettingsAccountsUM>> {
if (!accountsFeatureToggles.isFeatureEnabled) return flowOf(emptyList())
val params = SingleAccountStatusListProducer.Params(userWalletId)
return combine(
flow = singleAccountStatusListSupplier(params),
flow2 = getSelectedAppCurrencyUseCase.invokeOrDefault(),
flow3 = getBalanceHidingSettingsUseCase.isBalanceHidden(),
) { accountStatusList, appCurrency, isBalanceHidden ->
buildUiList(accountStatusList, appCurrency, isBalanceHidden)
}
}
private fun buildUiList(
accountStatusList: AccountStatusList,
appCurrency: AppCurrency,
isBalanceHidden: Boolean,
): List<WalletSettingsAccountsUM> = buildList {
fun AccountStatus.CryptoPortfolio.mapCryptoPortfolio(): WalletSettingsAccountsUM {
val accountItemUM = AccountPortfolioItemUMConverter(
onClick = { openAccountDetails(this.account) },
appCurrency = appCurrency,
accountBalance = this.tokenList.totalFiatBalance,
isBalanceHidden = isBalanceHidden,
).convert(account)
return WalletSettingsAccountsUM.Account(state = accountItemUM)
}
fun mapAccount(account: AccountStatus): WalletSettingsAccountsUM = when (account) {
is AccountStatus.CryptoPortfolio -> account.mapCryptoPortfolio()
}
val accounts = accountStatusList.accountStatuses
WalletSettingsAccountsUM.Header(
id = "accounts_header",
text = resourceReference(R.string.common_accounts),
@ -32,7 +83,8 @@ internal class AccountItemsDelegate @Inject constructor(
addAll(accounts.map(::mapAccount))
val addAccountEnabled = true // todo account
val addAccountEnabled = accounts.size < AccountList.MAX_ACCOUNTS_COUNT
val showDescription = accounts.size > 1
WalletSettingsAccountsUM.Footer(
id = "accounts_footer",
addAccount = AddAccountUM(
@ -47,33 +99,11 @@ internal class AccountItemsDelegate @Inject constructor(
iconRes = R.drawable.ic_archive_24,
onClick = { openArchivedAccounts(userWalletId) },
),
showDescription = showDescription,
description = resourceReference(R.string.account_reorder_description),
).let(::add)
}
private fun mapAccount(account: Account): WalletSettingsAccountsUM = when (account) {
is Account.CryptoPortfolio -> account.mapCryptoPortfolio()
}
private fun Account.CryptoPortfolio.mapCryptoPortfolio(): WalletSettingsAccountsUM {
return WalletSettingsAccountsUM.Account(
id = accountId.value,
accountName = accountName.toUM().value,
accountIconUM = icon.toUM(),
tokensInfo = pluralReference(
R.plurals.common_tokens_count,
count = tokensCount,
formatArgs = wrappedList(tokensCount),
),
networksInfo = pluralReference(
R.plurals.common_networks_count,
count = networksCount,
formatArgs = wrappedList(networksCount),
),
onClick = { openAccountDetails(this) },
)
}
private fun openAccountDetails(account: Account) {
router.push(AppRoute.AccountDetails(account))
}
@ -96,15 +126,10 @@ internal class AccountItemsDelegate @Inject constructor(
title = resourceReference(R.string.account_add_limit_dialog_title),
message = resourceReference(
id = R.string.account_add_limit_dialog_description,
formatArgs = wrappedList(MAX_ACCOUNT_COUNT.toString()),
formatArgs = wrappedList(AccountList.MAX_ACCOUNTS_COUNT.toString()),
),
firstActionBuilder = { firstAction },
),
)
}
companion object {
// todo account use domain const?
private const val MAX_ACCOUNT_COUNT = 20
}
}