Updated on 2026-08-14
This commit is contained in:
parent
d92c319583
commit
71e8f40909
21 changed files with 734 additions and 422 deletions
|
|
@ -21,4 +21,5 @@ dependencies {
|
|||
implementation(projects.domain.appCurrency.models)
|
||||
implementation(projects.domain.tokens)
|
||||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.account)
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
package com.tangem.features.account
|
||||
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.domain.models.account.Account
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
interface AccountSelectorComponent : ComposableBottomSheetComponent {
|
||||
|
||||
data class Params(
|
||||
val onDismiss: () -> Unit,
|
||||
val accountsBalanceFetcher: AccountsBalanceFetcher,
|
||||
val controller: AccountSelectorController,
|
||||
)
|
||||
|
||||
interface Factory {
|
||||
fun create(appComponentContext: AppComponentContext, params: Params): AccountSelectorComponent
|
||||
}
|
||||
}
|
||||
|
||||
interface AccountSelectorController {
|
||||
val selectedAccount: StateFlow<Account?>
|
||||
fun selectAccount(account: Account?)
|
||||
}
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
package com.tangem.features.account
|
||||
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.models.TotalFiatBalance
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
|
|
@ -11,7 +11,7 @@ import kotlinx.coroutines.CoroutineScope
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
interface AccountsBalanceFetcher {
|
||||
interface PortfolioFetcher {
|
||||
|
||||
val data: Flow<Data>
|
||||
|
||||
|
|
@ -21,12 +21,15 @@ interface AccountsBalanceFetcher {
|
|||
data class Data(
|
||||
val appCurrency: AppCurrency,
|
||||
val isBalanceHidden: Boolean,
|
||||
val balances: Map<UserWallet, Map<Account, AccountBalance>>,
|
||||
val balances: Map<UserWallet, PortfolioBalance>,
|
||||
)
|
||||
|
||||
data class AccountBalance(
|
||||
val balance: Lce<TokenListError, TotalFiatBalance>,
|
||||
)
|
||||
data class PortfolioBalance(
|
||||
val walletBalance: Lce<TokenListError, TotalFiatBalance>,
|
||||
val accountsBalance: AccountStatusList,
|
||||
) {
|
||||
val userWallet: UserWallet get() = accountsBalance.userWallet
|
||||
}
|
||||
|
||||
sealed interface Mode {
|
||||
data class All(val onlyMultiCurrency: Boolean) : Mode
|
||||
|
|
@ -34,6 +37,6 @@ interface AccountsBalanceFetcher {
|
|||
}
|
||||
|
||||
interface Factory {
|
||||
fun create(mode: Mode, scope: CoroutineScope): AccountsBalanceFetcher
|
||||
fun create(mode: Mode, scope: CoroutineScope): PortfolioFetcher
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.tangem.features.account
|
||||
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
interface PortfolioSelectorComponent : ComposableBottomSheetComponent, ComposableContentComponent {
|
||||
|
||||
val title: StateFlow<TextReference>
|
||||
|
||||
data class Params(
|
||||
val onDismiss: () -> Unit,
|
||||
val portfolioFetcher: PortfolioFetcher,
|
||||
val controller: PortfolioSelectorController,
|
||||
)
|
||||
|
||||
interface Factory {
|
||||
fun create(appComponentContext: AppComponentContext, params: Params): PortfolioSelectorComponent
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* if [isAccountMode] is false it's mean [selectedAccount] emit [AccountId] for Main account
|
||||
*/
|
||||
interface PortfolioSelectorController {
|
||||
val isAccountMode: Flow<Boolean>
|
||||
val selectedAccount: Flow<AccountId?>
|
||||
|
||||
fun selectAccount(accountId: AccountId?)
|
||||
fun selectedAccountWithData(portfolioFetcher: PortfolioFetcher): Flow<Pair<UserWallet, Account>?>
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ android {
|
|||
dependencies {
|
||||
/** Api */
|
||||
implementation(projects.features.account.api)
|
||||
implementation(projects.features.wallet.api)
|
||||
|
||||
/** Core modules */
|
||||
implementation(projects.core.analytics)
|
||||
|
|
@ -29,6 +30,7 @@ dependencies {
|
|||
/** Domain */
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.account)
|
||||
implementation(projects.domain.account.status)
|
||||
implementation(projects.domain.core)
|
||||
implementation(projects.domain.appCurrency)
|
||||
implementation(projects.domain.appCurrency.models)
|
||||
|
|
@ -39,6 +41,9 @@ dependencies {
|
|||
implementation(projects.domain.wallets)
|
||||
implementation(projects.domain.wallets.models)
|
||||
|
||||
/** Tangem libraries */
|
||||
implementation(tangemDeps.card.core)
|
||||
|
||||
/** Common */
|
||||
implementation(projects.common.ui)
|
||||
implementation(projects.common.routing)
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
package com.tangem.features.account.fetcher
|
||||
|
||||
import arrow.core.getOrElse
|
||||
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.core.lce.Lce
|
||||
import com.tangem.domain.models.TotalFiatBalance
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||
import com.tangem.domain.tokens.GetWalletTotalBalanceUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.features.account.AccountsBalanceFetcher
|
||||
import com.tangem.features.account.AccountsBalanceFetcher.*
|
||||
import com.tangem.features.account.PortfolioFetcher
|
||||
import com.tangem.features.account.PortfolioFetcher.*
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
|
|
@ -20,14 +21,17 @@ import kotlinx.coroutines.CoroutineScope
|
|||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
internal class DefaultAccountsBalanceFetcher @AssistedInject constructor(
|
||||
@Suppress("LongParameterList")
|
||||
internal class DefaultPortfolioFetcher @AssistedInject constructor(
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
private val getWalletTotalBalanceUseCase: GetWalletTotalBalanceUseCase,
|
||||
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
private val getWallets: GetWalletsUseCase,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
@Assisted mode: Mode,
|
||||
@Assisted private val scope: CoroutineScope,
|
||||
) : AccountsBalanceFetcher {
|
||||
) : PortfolioFetcher {
|
||||
|
||||
private val _mode = MutableStateFlow(mode)
|
||||
private val _data = MutableSharedFlow<Data>(
|
||||
|
|
@ -73,33 +77,22 @@ internal class DefaultAccountsBalanceFetcher @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun balancesForWallets(wallets: List<UserWallet>): Flow<Map<UserWallet, Map<Account, AccountBalance>>> =
|
||||
wallets.asFlow()
|
||||
.map { walletAccountsBalancesFlow(it) }
|
||||
.mapLatest { accountsBalances -> combine(accountsBalances) { pairs -> pairs.toMap() } }
|
||||
.flattenConcat()
|
||||
|
||||
private fun walletAccountsBalancesFlow(wallet: UserWallet): Flow<Pair<UserWallet, Map<Account, AccountBalance>>> =
|
||||
walletAccounts(wallet)
|
||||
.distinctUntilChanged()
|
||||
.map { list -> list.map(::accountBalanceFlow) }
|
||||
.mapLatest { balanceFlows -> combine(balanceFlows) { balances -> balances.toMap() } }
|
||||
.flattenConcat()
|
||||
.map { accountBalance -> wallet to accountBalance }
|
||||
|
||||
private fun walletAccounts(wallet: UserWallet): Flow<List<Account>> = flow {
|
||||
// todo account load accounts
|
||||
val accounts: List<Account> = Account.CryptoPortfolio
|
||||
.createMainAccount(wallet.walletId)
|
||||
.let(::listOf)
|
||||
emit(accounts)
|
||||
private fun balancesForWallets(wallets: List<UserWallet>): Flow<Map<UserWallet, PortfolioBalance>> {
|
||||
val balanceFlows = wallets.map { walletAccountsBalancesFlow(it) }
|
||||
return combine(balanceFlows) { pairs -> pairs.toMap() }
|
||||
}
|
||||
|
||||
private fun accountBalanceFlow(account: Account): Flow<Pair<Account, AccountBalance>> = flow {
|
||||
// todo account load balance
|
||||
val balance = AccountBalance(balance = Lce.Content(TotalFiatBalance.Loading))
|
||||
emit(account to balance)
|
||||
}
|
||||
private fun walletAccountsBalancesFlow(wallet: UserWallet): Flow<Pair<UserWallet, PortfolioBalance>> = combine(
|
||||
flow = accountStatusListFlow(wallet),
|
||||
flow2 = getWalletTotalBalanceUseCase(wallet.walletId),
|
||||
transform = { accountStatusList, walletBalance ->
|
||||
val portfolioBalance = PortfolioBalance(walletBalance, accountStatusList)
|
||||
wallet to portfolioBalance
|
||||
},
|
||||
)
|
||||
|
||||
private fun accountStatusListFlow(wallet: UserWallet): Flow<AccountStatusList> =
|
||||
singleAccountStatusListSupplier(SingleAccountStatusListProducer.Params(wallet.walletId))
|
||||
|
||||
private fun appCurrencyFlow(): Flow<AppCurrency> {
|
||||
return getSelectedAppCurrencyUseCase()
|
||||
|
|
@ -114,7 +107,7 @@ internal class DefaultAccountsBalanceFetcher @AssistedInject constructor(
|
|||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : AccountsBalanceFetcher.Factory {
|
||||
override fun create(mode: Mode, scope: CoroutineScope): DefaultAccountsBalanceFetcher
|
||||
interface Factory : PortfolioFetcher.Factory {
|
||||
override fun create(mode: Mode, scope: CoroutineScope): DefaultPortfolioFetcher
|
||||
}
|
||||
}
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
package com.tangem.features.account.selector
|
||||
|
||||
import com.tangem.common.ui.account.AccountCryptoPortfolioItemStateConverter
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.core.utils.getOrElse
|
||||
import com.tangem.domain.models.TotalFiatBalance
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.wallet.isLocked
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.features.account.AccountSelectorComponent
|
||||
import com.tangem.features.account.AccountsBalanceFetcher
|
||||
import com.tangem.features.account.selector.entity.AccountSelectorItemUM
|
||||
import com.tangem.features.account.selector.entity.AccountSelectorUM
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
internal class AccountSelectorModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<AccountSelectorComponent.Params>()
|
||||
private val balanceFetcher get() = params.accountsBalanceFetcher
|
||||
private val selectorController get() = params.controller
|
||||
|
||||
internal val state: StateFlow<AccountSelectorUM>
|
||||
field = MutableStateFlow<AccountSelectorUM>(emptyState())
|
||||
|
||||
init {
|
||||
balanceFetcher.data
|
||||
.map { data ->
|
||||
AccountSelectorUM(
|
||||
isSingleWallet = balanceFetcher.mode.value is AccountsBalanceFetcher.Mode.Wallet,
|
||||
items = buildUiList(data).toImmutableList(),
|
||||
)
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun buildUiList(data: AccountsBalanceFetcher.Data) = buildList {
|
||||
fun Account.accountItemState(balance: Lce<TokenListError, TotalFiatBalance>): TokenItemState {
|
||||
val totalFiatBalance = balance.getOrElse(
|
||||
ifError = { TotalFiatBalance.Failed },
|
||||
ifLoading = { TotalFiatBalance.Loading },
|
||||
)
|
||||
return when (this) {
|
||||
is Account.CryptoPortfolio -> AccountCryptoPortfolioItemStateConverter(
|
||||
appCurrency = data.appCurrency,
|
||||
account = this,
|
||||
onItemClick = { selectorController.selectAccount(it) },
|
||||
).convert(totalFiatBalance)
|
||||
}
|
||||
}
|
||||
|
||||
data.balances.forEach { wallet, accounts ->
|
||||
if (wallet.isLocked) return@forEach
|
||||
AccountSelectorItemUM.Wallet(
|
||||
id = wallet.walletId.stringValue,
|
||||
name = stringReference(wallet.name),
|
||||
).let(::add)
|
||||
|
||||
accounts.forEach { account, balance ->
|
||||
AccountSelectorItemUM.Account(
|
||||
account = account.accountItemState(balance.balance),
|
||||
isBalanceHidden = data.isBalanceHidden,
|
||||
).let(::add)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun emptyState() = AccountSelectorUM(
|
||||
items = persistentListOf(),
|
||||
balanceFetcher.mode.value is AccountsBalanceFetcher.Mode.Wallet,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
package com.tangem.features.account.selector
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.account.AccountSelectorComponent
|
||||
import com.tangem.features.account.impl.R
|
||||
import com.tangem.features.account.selector.ui.AccountSelectorContent
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
internal class DefaultAccountSelectorComponent @AssistedInject constructor(
|
||||
@Assisted appComponentContext: AppComponentContext,
|
||||
@Assisted private val params: AccountSelectorComponent.Params,
|
||||
) : AppComponentContext by appComponentContext, AccountSelectorComponent {
|
||||
|
||||
private val model: AccountSelectorModel = getOrCreateModel(params)
|
||||
|
||||
override fun dismiss() {
|
||||
params.onDismiss()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun BottomSheet() {
|
||||
val state by model.state.collectAsStateWithLifecycle()
|
||||
TangemModalBottomSheet<TangemBottomSheetConfigContent.Empty>(
|
||||
config = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = ::dismiss,
|
||||
content = TangemBottomSheetConfigContent.Empty,
|
||||
),
|
||||
onBack = ::dismiss,
|
||||
containerColor = TangemTheme.colors.background.primary,
|
||||
title = {
|
||||
TangemModalBottomSheetTitle(
|
||||
title = resourceReference(R.string.common_choose_wallet),
|
||||
startIconRes = R.drawable.ic_back_24,
|
||||
onStartClick = ::dismiss,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
AccountSelectorContent(
|
||||
state = state,
|
||||
modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : AccountSelectorComponent.Factory {
|
||||
override fun create(
|
||||
appComponentContext: AppComponentContext,
|
||||
params: AccountSelectorComponent.Params,
|
||||
): DefaultAccountSelectorComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package com.tangem.features.account.selector
|
||||
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.features.account.AccountSelectorController
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultAccountSelectorController @Inject constructor() : AccountSelectorController {
|
||||
|
||||
private val _selectedAccount: MutableStateFlow<Account?> = MutableStateFlow(null)
|
||||
override val selectedAccount: StateFlow<Account?> get() = _selectedAccount
|
||||
|
||||
override fun selectAccount(account: Account?) {
|
||||
_selectedAccount.update { account }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package com.tangem.features.account.selector
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.features.account.PortfolioSelectorComponent
|
||||
import com.tangem.features.account.selector.ui.PortfolioSelectorBS
|
||||
import com.tangem.features.account.selector.ui.PortfolioSelectorContent
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
internal class DefaultPortfolioSelectorComponent @AssistedInject constructor(
|
||||
@Assisted appComponentContext: AppComponentContext,
|
||||
@Assisted private val params: PortfolioSelectorComponent.Params,
|
||||
) : AppComponentContext by appComponentContext, PortfolioSelectorComponent {
|
||||
|
||||
private val model: PortfolioSelectorModel = getOrCreateModel(params)
|
||||
|
||||
override val title: StateFlow<TextReference>
|
||||
get() = model.state
|
||||
.map { it.title }
|
||||
.stateIn(componentScope, SharingStarted.Lazily, model.state.value.title)
|
||||
|
||||
override fun dismiss() {
|
||||
params.onDismiss()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun BottomSheet() {
|
||||
val state by model.state.collectAsStateWithLifecycle()
|
||||
PortfolioSelectorBS(state, onDismiss = ::dismiss)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.state.collectAsStateWithLifecycle()
|
||||
PortfolioSelectorContent(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : PortfolioSelectorComponent.Factory {
|
||||
override fun create(
|
||||
appComponentContext: AppComponentContext,
|
||||
params: PortfolioSelectorComponent.Params,
|
||||
): DefaultPortfolioSelectorComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package com.tangem.features.account.selector
|
||||
|
||||
import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.features.account.PortfolioSelectorController
|
||||
import com.tangem.features.account.PortfolioFetcher
|
||||
import kotlinx.coroutines.flow.*
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultPortfolioSelectorController @Inject constructor(
|
||||
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
|
||||
) : PortfolioSelectorController {
|
||||
|
||||
private val _selectedAccount: MutableStateFlow<AccountId?> = MutableStateFlow(null)
|
||||
|
||||
override val isAccountMode: Flow<Boolean> by lazy { isAccountsModeEnabledUseCase() }
|
||||
|
||||
override val selectedAccount: StateFlow<AccountId?> get() = _selectedAccount
|
||||
|
||||
override fun selectAccount(accountId: AccountId?) {
|
||||
_selectedAccount.update { accountId }
|
||||
}
|
||||
|
||||
override fun selectedAccountWithData(portfolioFetcher: PortfolioFetcher): Flow<Pair<UserWallet, Account>?> =
|
||||
combine(
|
||||
flow = _selectedAccount,
|
||||
flow2 = portfolioFetcher.data,
|
||||
transform = { accountId, data ->
|
||||
accountId ?: return@combine null
|
||||
var result: Pair<UserWallet, Account>? = null
|
||||
|
||||
data.balances.forEach { wallet, balance ->
|
||||
val accountStatuses = balance.accountsBalance.accountStatuses
|
||||
.find { accountId == it.account.accountId }
|
||||
if (accountStatuses != null) result = wallet to accountStatuses.account
|
||||
}
|
||||
|
||||
return@combine result
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
package com.tangem.features.account.selector
|
||||
|
||||
import com.tangem.common.ui.account.AccountPortfolioItemUMConverter
|
||||
import com.tangem.common.ui.userwallet.converter.UserWalletItemUMConverter
|
||||
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.features.account.PortfolioFetcher
|
||||
import com.tangem.features.account.PortfolioSelectorComponent
|
||||
import com.tangem.features.account.impl.R
|
||||
import com.tangem.features.account.selector.entity.PortfolioSelectorItemUM
|
||||
import com.tangem.features.account.selector.entity.PortfolioSelectorUM
|
||||
import com.tangem.features.wallet.utils.UserWalletImageFetcher
|
||||
import com.tangem.operations.attestation.ArtworkSize
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
internal class PortfolioSelectorModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val walletImageFetcher: UserWalletImageFetcher,
|
||||
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<PortfolioSelectorComponent.Params>()
|
||||
private val balanceFetcher get() = params.portfolioFetcher
|
||||
private val selectorController get() = params.controller
|
||||
|
||||
internal val state: StateFlow<PortfolioSelectorUM>
|
||||
field = MutableStateFlow<PortfolioSelectorUM>(emptyState())
|
||||
|
||||
init {
|
||||
combine(
|
||||
flow = isAccountsModeEnabledUseCase(),
|
||||
flow2 = loadBalanceWithArtwork(),
|
||||
transform = { isAccountsMode, (portfolioData, artworks) ->
|
||||
val uiList = buildUiList(isAccountsMode, portfolioData, artworks)
|
||||
val title = when (isAccountsMode) {
|
||||
true -> resourceReference(R.string.common_choose_account)
|
||||
false -> resourceReference(R.string.common_choose_wallet)
|
||||
}
|
||||
state.value = PortfolioSelectorUM(
|
||||
title = title,
|
||||
items = uiList.toImmutableList(),
|
||||
)
|
||||
},
|
||||
)
|
||||
.flowOn(dispatchers.default)
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun buildUiList(
|
||||
isAccountsMode: Boolean,
|
||||
portfolioData: PortfolioFetcher.Data,
|
||||
artworks: Map<UserWalletId, UserWalletItemUM.ImageState>,
|
||||
): List<PortfolioSelectorItemUM> = when (isAccountsMode) {
|
||||
true -> buildAccountsList(portfolioData, artworks)
|
||||
false -> buildWalletList(portfolioData, artworks)
|
||||
}
|
||||
|
||||
private fun buildWalletList(
|
||||
portfolioData: PortfolioFetcher.Data,
|
||||
artworks: Map<UserWalletId, UserWalletItemUM.ImageState>,
|
||||
): List<PortfolioSelectorItemUM> = buildList {
|
||||
val appCurrency = portfolioData.appCurrency
|
||||
val isBalanceHidden = portfolioData.isBalanceHidden
|
||||
val lockedWallets = mutableListOf<PortfolioSelectorItemUM>()
|
||||
portfolioData.balances.forEach { wallet, portfolio ->
|
||||
val balance = portfolio.walletBalance.getOrNull()
|
||||
val walletItemUM = UserWalletItemUMConverter(
|
||||
onClick = {
|
||||
// todo account
|
||||
// selectorController.selectAccount(portfolio.accountsBalance.mainAccount)
|
||||
},
|
||||
appCurrency = appCurrency,
|
||||
balance = balance,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
artwork = artworks[wallet.walletId],
|
||||
isAuthMode = false,
|
||||
).convert(wallet)
|
||||
if (walletItemUM.isEnabled) {
|
||||
add(PortfolioSelectorItemUM.Portfolio(walletItemUM))
|
||||
} else {
|
||||
lockedWallets.add(PortfolioSelectorItemUM.Portfolio(walletItemUM))
|
||||
}
|
||||
}
|
||||
if (lockedWallets.isNotEmpty()) {
|
||||
val lockedWalletsTitle = PortfolioSelectorItemUM.GroupTitle(
|
||||
id = "lockedWalletsTitleId",
|
||||
name = resourceReference(R.string.common_locked_wallets),
|
||||
)
|
||||
add(lockedWalletsTitle)
|
||||
addAll(lockedWallets)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildAccountsList(
|
||||
portfolioData: PortfolioFetcher.Data,
|
||||
artworks: Map<UserWalletId, UserWalletItemUM.ImageState>,
|
||||
): List<PortfolioSelectorItemUM> = buildList {
|
||||
val appCurrency = portfolioData.appCurrency
|
||||
val isBalanceHidden = portfolioData.isBalanceHidden
|
||||
val lockedWallets = mutableListOf<PortfolioSelectorItemUM>()
|
||||
portfolioData.balances.forEach { wallet, portfolio ->
|
||||
val balance = portfolio.walletBalance.getOrNull()
|
||||
val walletItemUM = UserWalletItemUMConverter(
|
||||
onClick = {
|
||||
// todo account
|
||||
// selectorController.selectAccount(portfolio.accountsBalance.mainAccount)
|
||||
},
|
||||
appCurrency = appCurrency,
|
||||
balance = balance,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
artwork = artworks[wallet.walletId],
|
||||
isAuthMode = false,
|
||||
).convert(wallet)
|
||||
if (!walletItemUM.isEnabled) {
|
||||
lockedWallets.add(PortfolioSelectorItemUM.Portfolio(walletItemUM))
|
||||
return@forEach
|
||||
}
|
||||
|
||||
val walletTitle = PortfolioSelectorItemUM.GroupTitle(
|
||||
id = "GroupTitle ${wallet.walletId.stringValue}",
|
||||
name = stringReference(wallet.name),
|
||||
)
|
||||
add(walletTitle)
|
||||
|
||||
add(PortfolioSelectorItemUM.Portfolio(walletItemUM))
|
||||
portfolio.accountsBalance.accountStatuses.forEach { accountStatus ->
|
||||
val account = accountStatus.account
|
||||
val accountBalance = when (accountStatus) {
|
||||
is AccountStatus.CryptoPortfolio -> accountStatus.tokenList.totalFiatBalance
|
||||
}
|
||||
val accountItemUM = AccountPortfolioItemUMConverter(
|
||||
onClick = { selectorController.selectAccount(account.accountId) },
|
||||
appCurrency = appCurrency,
|
||||
accountBalance = accountBalance,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
).convert(account)
|
||||
add(PortfolioSelectorItemUM.Portfolio(accountItemUM))
|
||||
}
|
||||
}
|
||||
if (lockedWallets.isNotEmpty()) {
|
||||
val lockedWalletsTitle = PortfolioSelectorItemUM.GroupTitle(
|
||||
id = "lockedWalletsTitleId",
|
||||
name = resourceReference(R.string.common_locked_wallets),
|
||||
)
|
||||
add(lockedWalletsTitle)
|
||||
addAll(lockedWallets)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadBalanceWithArtwork():
|
||||
Flow<Pair<PortfolioFetcher.Data, Map<UserWalletId, UserWalletItemUM.ImageState>>> {
|
||||
val wallets = Channel<Set<UserWallet>>()
|
||||
val portfolioFlow = balanceFetcher.data
|
||||
.onEach { wallets.trySend(it.balances.keys) }
|
||||
|
||||
val artworksFlow = wallets.receiveAsFlow()
|
||||
.distinctUntilChanged()
|
||||
.flatMapLatest { walletImageFetcher.walletsImage(wallets = it, size = ArtworkSize.SMALL) }
|
||||
|
||||
return combine(
|
||||
flow = portfolioFlow,
|
||||
flow2 = artworksFlow,
|
||||
) { portfolioData, artworks -> portfolioData to artworks }
|
||||
}
|
||||
|
||||
private fun emptyState() = PortfolioSelectorUM(
|
||||
items = persistentListOf(),
|
||||
title = TextReference.EMPTY,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
package com.tangem.features.account.selector.entity
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
data class AccountSelectorUM(
|
||||
val items: ImmutableList<AccountSelectorItemUM>,
|
||||
val isSingleWallet: Boolean,
|
||||
)
|
||||
|
||||
@Immutable
|
||||
sealed interface AccountSelectorItemUM {
|
||||
val id: String
|
||||
|
||||
data class Wallet(
|
||||
override val id: String,
|
||||
val name: TextReference,
|
||||
) : AccountSelectorItemUM
|
||||
|
||||
data class Account(
|
||||
val account: TokenItemState,
|
||||
val isBalanceHidden: Boolean,
|
||||
) : AccountSelectorItemUM {
|
||||
override val id: String = account.id
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.tangem.features.account.selector.entity
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
data class PortfolioSelectorUM(
|
||||
val title: TextReference,
|
||||
val items: ImmutableList<PortfolioSelectorItemUM>,
|
||||
)
|
||||
|
||||
@Immutable
|
||||
sealed interface PortfolioSelectorItemUM {
|
||||
val id: String
|
||||
|
||||
data class GroupTitle(
|
||||
override val id: String,
|
||||
val name: TextReference,
|
||||
) : PortfolioSelectorItemUM
|
||||
|
||||
data class Portfolio(
|
||||
val item: UserWalletItemUM,
|
||||
) : PortfolioSelectorItemUM {
|
||||
override val id: String = item.id.stringValue
|
||||
}
|
||||
}
|
||||
|
|
@ -1,146 +0,0 @@
|
|||
package com.tangem.features.account.selector.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.tangem.core.ui.components.token.AccountItemPreviewData
|
||||
import com.tangem.core.ui.components.token.TokenItem
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.account.selector.entity.AccountSelectorItemUM
|
||||
import com.tangem.features.account.selector.entity.AccountSelectorUM
|
||||
import com.tangem.features.account.selector.ui.AccountSelectorPreviewData.firstList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import java.util.UUID
|
||||
|
||||
@Composable
|
||||
internal fun AccountSelectorContent(
|
||||
state: AccountSelectorUM,
|
||||
modifier: Modifier = Modifier,
|
||||
contentPadding: PaddingValues = PaddingValues(),
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
contentPadding = contentPadding,
|
||||
) {
|
||||
val items = state.items
|
||||
itemsIndexed(
|
||||
items = items,
|
||||
key = { _, item -> item.id },
|
||||
) { index, item ->
|
||||
val previewItem = items.getOrNull(index.dec())
|
||||
val offsetModifier = when {
|
||||
previewItem == null -> Modifier
|
||||
state.isSingleWallet -> Modifier.padding(
|
||||
top = TangemTheme.dimens.spacing16,
|
||||
)
|
||||
item is AccountSelectorItemUM.Wallet -> Modifier.padding(
|
||||
top = TangemTheme.dimens.spacing16,
|
||||
)
|
||||
else -> Modifier.padding(
|
||||
top = TangemTheme.dimens.spacing8,
|
||||
)
|
||||
}
|
||||
|
||||
when (item) {
|
||||
is AccountSelectorItemUM.Account -> TokenItem(
|
||||
state = item.account,
|
||||
isBalanceHidden = item.isBalanceHidden,
|
||||
modifier = offsetModifier
|
||||
.clip(RoundedCornerShape(TangemTheme.dimens.radius14))
|
||||
.background(color = TangemTheme.colors.background.primary),
|
||||
)
|
||||
is AccountSelectorItemUM.Wallet -> WalletNameRow(
|
||||
model = item,
|
||||
modifier = offsetModifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WalletNameRow(model: AccountSelectorItemUM.Wallet, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
modifier = modifier,
|
||||
text = model.name.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun AccountSelectorContentPreview(
|
||||
@PreviewParameter(AccountSelectorPreviewStateProvider::class) params: AccountSelectorUM,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
AccountSelectorContent(
|
||||
state = params,
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.secondary),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal object AccountSelectorPreviewData {
|
||||
val firstList
|
||||
get() = buildList {
|
||||
AccountSelectorItemUM.Wallet(
|
||||
id = UUID.randomUUID().toString(),
|
||||
name = stringReference("Tangem 2.0"),
|
||||
).let(::add)
|
||||
AccountItemPreviewData.accountItem
|
||||
.let { AccountSelectorItemUM.Account(it, false) }
|
||||
.let(::add)
|
||||
AccountItemPreviewData.accountItem.copy(iconState = AccountItemPreviewData.accountLetterIcon)
|
||||
.let { AccountSelectorItemUM.Account(it, false) }
|
||||
.let(::add)
|
||||
AccountSelectorItemUM.Wallet(
|
||||
id = UUID.randomUUID().toString(),
|
||||
name = stringReference("Tangem White"),
|
||||
).let(::add)
|
||||
AccountItemPreviewData.accountItem.copy(iconState = AccountItemPreviewData.accountLetterIcon)
|
||||
.let { AccountSelectorItemUM.Account(it, false) }
|
||||
.let(::add)
|
||||
}
|
||||
}
|
||||
|
||||
internal class AccountSelectorPreviewStateProvider : CollectionPreviewParameterProvider<AccountSelectorUM>(
|
||||
buildList {
|
||||
val secondList = listOf(
|
||||
AccountItemPreviewData.accountItem,
|
||||
AccountItemPreviewData.accountItem.copy(iconState = AccountItemPreviewData.accountLetterIcon),
|
||||
).map { AccountSelectorItemUM.Account(it, false) }
|
||||
|
||||
val first = AccountSelectorUM(
|
||||
items = firstList.toImmutableList(),
|
||||
isSingleWallet = false,
|
||||
)
|
||||
val second = AccountSelectorUM(
|
||||
items = secondList.toImmutableList(),
|
||||
isSingleWallet = true,
|
||||
)
|
||||
add(first)
|
||||
add(second)
|
||||
},
|
||||
)
|
||||
|
|
@ -13,14 +13,13 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
|||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.account.impl.R
|
||||
import com.tangem.features.account.selector.entity.AccountSelectorUM
|
||||
import com.tangem.features.account.selector.entity.PortfolioSelectorUM
|
||||
|
||||
@Composable
|
||||
internal fun AccountSelectorBS(state: AccountSelectorUM, onDismiss: () -> Unit, modifier: Modifier = Modifier) {
|
||||
internal fun PortfolioSelectorBS(state: PortfolioSelectorUM, onDismiss: () -> Unit, modifier: Modifier = Modifier) {
|
||||
TangemModalBottomSheet<TangemBottomSheetConfigContent.Empty>(
|
||||
config = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
|
|
@ -32,13 +31,13 @@ internal fun AccountSelectorBS(state: AccountSelectorUM, onDismiss: () -> Unit,
|
|||
containerColor = TangemTheme.colors.background.secondary,
|
||||
title = {
|
||||
TangemModalBottomSheetTitle(
|
||||
title = resourceReference(R.string.common_choose_account),
|
||||
title = state.title,
|
||||
startIconRes = R.drawable.ic_back_24,
|
||||
onStartClick = onDismiss,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
AccountSelectorContent(
|
||||
PortfolioSelectorContent(
|
||||
state = state,
|
||||
contentPadding = PaddingValues(bottom = 16.dp),
|
||||
modifier = modifier.padding(horizontal = 16.dp),
|
||||
|
|
@ -50,9 +49,9 @@ internal fun AccountSelectorBS(state: AccountSelectorUM, onDismiss: () -> Unit,
|
|||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview(@PreviewParameter(AccountSelectorPreviewStateProvider::class) params: AccountSelectorUM) {
|
||||
private fun Preview(@PreviewParameter(PortfolioSelectorPreviewStateProvider::class) params: PortfolioSelectorUM) {
|
||||
TangemThemePreview {
|
||||
AccountSelectorBS(
|
||||
PortfolioSelectorBS(
|
||||
state = params,
|
||||
onDismiss = {},
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.secondary),
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
package com.tangem.features.account.selector.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.tangem.common.ui.account.AccountIconPreviewData
|
||||
import com.tangem.common.ui.userwallet.UserWalletItem
|
||||
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
|
||||
import com.tangem.common.ui.userwallet.state.UserWalletItemUM.ImageState
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.features.account.impl.R
|
||||
import com.tangem.features.account.selector.entity.PortfolioSelectorItemUM
|
||||
import com.tangem.features.account.selector.entity.PortfolioSelectorUM
|
||||
import com.tangem.features.account.selector.ui.PortfolioSelectorPreviewData.firstList
|
||||
import com.tangem.features.account.selector.ui.PortfolioSelectorPreviewData.lockedWalletList
|
||||
import com.tangem.features.account.selector.ui.PortfolioSelectorPreviewData.secondList
|
||||
import com.tangem.features.account.selector.ui.PortfolioSelectorPreviewData.walletList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import java.util.UUID
|
||||
|
||||
private const val DISABLED_WALLET_ALPHA = 0.5f
|
||||
|
||||
@Composable
|
||||
internal fun PortfolioSelectorContent(
|
||||
state: PortfolioSelectorUM,
|
||||
modifier: Modifier = Modifier,
|
||||
contentPadding: PaddingValues = PaddingValues(),
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
contentPadding = contentPadding,
|
||||
) {
|
||||
val items = state.items
|
||||
itemsIndexed(
|
||||
items = items,
|
||||
key = { _, item -> item.id },
|
||||
) { index, item ->
|
||||
val previewItem = items.getOrNull(index.dec())
|
||||
val offsetModifier = when {
|
||||
previewItem == null -> Modifier
|
||||
item is PortfolioSelectorItemUM.GroupTitle -> Modifier.padding(
|
||||
top = TangemTheme.dimens.spacing16,
|
||||
)
|
||||
else -> Modifier.padding(
|
||||
top = TangemTheme.dimens.spacing8,
|
||||
)
|
||||
}
|
||||
|
||||
when (item) {
|
||||
is PortfolioSelectorItemUM.Portfolio -> UserWalletItem(
|
||||
state = item.item,
|
||||
modifier = offsetModifier
|
||||
.clip(RoundedCornerShape(TangemTheme.dimens.radius14))
|
||||
.background(color = TangemTheme.colors.background.primary)
|
||||
.let { if (!item.item.isEnabled) it.alpha(DISABLED_WALLET_ALPHA) else it },
|
||||
)
|
||||
is PortfolioSelectorItemUM.GroupTitle -> WalletNameRow(
|
||||
model = item,
|
||||
modifier = offsetModifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WalletNameRow(model: PortfolioSelectorItemUM.GroupTitle, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
modifier = modifier,
|
||||
text = model.name.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview(@PreviewParameter(PortfolioSelectorPreviewStateProvider::class) params: PortfolioSelectorUM) {
|
||||
TangemThemePreview {
|
||||
PortfolioSelectorContent(
|
||||
state = params,
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.secondary),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal object PortfolioSelectorPreviewData {
|
||||
|
||||
val accountName get() = stringReference(value = "Portfolio")
|
||||
val walletName get() = stringReference(value = "Tangem 2.0")
|
||||
|
||||
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 walletItem: UserWalletItemUM
|
||||
get() = UserWalletItemUM(
|
||||
id = UserWalletId(UUID.randomUUID().toString().encodeToByteArray()),
|
||||
name = walletName,
|
||||
information = UserWalletItemUM.Information.Loaded(stringReference("12 tokens")),
|
||||
balance = UserWalletItemUM.Balance.Loaded("$726.04", false),
|
||||
isEnabled = true,
|
||||
onClick = { },
|
||||
imageState = ImageState.MobileWallet,
|
||||
label = null,
|
||||
)
|
||||
|
||||
private val lockedWalletItem: UserWalletItemUM
|
||||
get() = walletItem.copy(isEnabled = false)
|
||||
|
||||
val firstList
|
||||
get() = buildList {
|
||||
PortfolioSelectorItemUM.GroupTitle(
|
||||
id = UUID.randomUUID().toString(),
|
||||
name = stringReference("Tangem 2.0"),
|
||||
).let(::add)
|
||||
accountItem
|
||||
.let { PortfolioSelectorItemUM.Portfolio(it) }
|
||||
.let(::add)
|
||||
accountItem
|
||||
.let { PortfolioSelectorItemUM.Portfolio(it) }
|
||||
.let(::add)
|
||||
PortfolioSelectorItemUM.GroupTitle(
|
||||
id = UUID.randomUUID().toString(),
|
||||
name = stringReference("Tangem White"),
|
||||
).let(::add)
|
||||
accountItem.let { PortfolioSelectorItemUM.Portfolio(it) }
|
||||
.let(::add)
|
||||
}
|
||||
|
||||
val secondList
|
||||
get() = firstList + buildList {
|
||||
PortfolioSelectorItemUM.GroupTitle(
|
||||
id = UUID.randomUUID().toString(),
|
||||
name = resourceReference(R.string.common_locked_wallets),
|
||||
).let(::add)
|
||||
add(PortfolioSelectorItemUM.Portfolio(lockedWalletItem))
|
||||
add(PortfolioSelectorItemUM.Portfolio(lockedWalletItem))
|
||||
}
|
||||
|
||||
val walletList
|
||||
get() = buildList {
|
||||
add(PortfolioSelectorItemUM.Portfolio(walletItem))
|
||||
add(PortfolioSelectorItemUM.Portfolio(walletItem))
|
||||
}
|
||||
|
||||
val lockedWalletList
|
||||
get() = buildList {
|
||||
add(PortfolioSelectorItemUM.Portfolio(walletItem))
|
||||
val title = PortfolioSelectorItemUM.GroupTitle(
|
||||
id = UUID.randomUUID().toString(),
|
||||
name = resourceReference(R.string.common_locked_wallets),
|
||||
)
|
||||
add(title)
|
||||
add(PortfolioSelectorItemUM.Portfolio(lockedWalletItem))
|
||||
add(PortfolioSelectorItemUM.Portfolio(lockedWalletItem))
|
||||
}
|
||||
}
|
||||
|
||||
internal class PortfolioSelectorPreviewStateProvider : CollectionPreviewParameterProvider<PortfolioSelectorUM>(
|
||||
buildList {
|
||||
val first = PortfolioSelectorUM(
|
||||
title = resourceReference(R.string.common_choose_account),
|
||||
items = firstList.toImmutableList(),
|
||||
)
|
||||
val second = PortfolioSelectorUM(
|
||||
title = resourceReference(R.string.common_choose_account),
|
||||
items = secondList.toImmutableList(),
|
||||
)
|
||||
val walletListUM = PortfolioSelectorUM(
|
||||
title = resourceReference(R.string.common_choose_wallet),
|
||||
items = walletList.toImmutableList(),
|
||||
)
|
||||
val lockedWalletListUM =
|
||||
PortfolioSelectorUM(
|
||||
title = resourceReference(R.string.common_choose_wallet),
|
||||
items = lockedWalletList.toImmutableList(),
|
||||
)
|
||||
add(first)
|
||||
add(second)
|
||||
add(walletListUM)
|
||||
add(lockedWalletListUM)
|
||||
},
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue