Updated on 2026-08-14
This commit is contained in:
parent
b92bb00c7f
commit
5e6bf6aaae
14 changed files with 172 additions and 109 deletions
|
|
@ -19,6 +19,7 @@ class AccountPortfolioItemUMConverter(
|
|||
private val appCurrency: AppCurrency? = null,
|
||||
private val accountBalance: TotalFiatBalance? = null,
|
||||
private val isBalanceHidden: Boolean = false,
|
||||
private val isEnabled: Boolean = true,
|
||||
private val endIcon: UserWalletItemUM.EndIcon = UserWalletItemUM.EndIcon.None,
|
||||
) : Converter<Account, UserWalletItemUM> {
|
||||
|
||||
|
|
@ -29,7 +30,7 @@ class AccountPortfolioItemUMConverter(
|
|||
name = value.accountName.toUM().value,
|
||||
information = getInfo(value),
|
||||
balance = getBalanceInfo(),
|
||||
isEnabled = true,
|
||||
isEnabled = isEnabled,
|
||||
endIcon = endIcon,
|
||||
onClick = { onClick(value.accountId) },
|
||||
imageState = getImageState(value),
|
||||
|
|
|
|||
|
|
@ -57,45 +57,53 @@ fun UserWalletItem(
|
|||
onClick = state.onClick,
|
||||
enabled = state.isEnabled,
|
||||
) {
|
||||
Row(
|
||||
UserWalletItemRow(
|
||||
state = state,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = TangemTheme.dimens.size68)
|
||||
.padding(all = TangemTheme.dimens.spacing12),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
CardImage(state.imageState)
|
||||
NameAndInfo(
|
||||
modifier = Modifier.weight(1f),
|
||||
name = state.name,
|
||||
information = state.information,
|
||||
balance = state.balance,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
when (state.endIcon) {
|
||||
UserWalletItemUM.EndIcon.None -> Unit
|
||||
UserWalletItemUM.EndIcon.Arrow -> {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_chevron_right_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
UserWalletItemUM.EndIcon.Checkmark -> {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_check_24),
|
||||
tint = TangemTheme.colors.icon.accent,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
UserWalletItemUM.EndIcon.Warning -> {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_alert_circle_24),
|
||||
tint = TangemTheme.colors.icon.warning,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
@Composable
|
||||
fun UserWalletItemRow(state: UserWalletItemUM, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
CardImage(state.imageState)
|
||||
NameAndInfo(
|
||||
modifier = Modifier.weight(1f),
|
||||
name = state.name,
|
||||
information = state.information,
|
||||
balance = state.balance,
|
||||
)
|
||||
|
||||
when (state.endIcon) {
|
||||
UserWalletItemUM.EndIcon.None -> Unit
|
||||
UserWalletItemUM.EndIcon.Arrow -> {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_chevron_right_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
UserWalletItemUM.EndIcon.Checkmark -> {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_check_24),
|
||||
tint = TangemTheme.colors.icon.accent,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
UserWalletItemUM.EndIcon.Warning -> {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_alert_circle_24),
|
||||
tint = TangemTheme.colors.icon.warning,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,15 +2,15 @@ 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.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* How to use see [PortfolioSelectorComponent]
|
||||
*/
|
||||
interface PortfolioFetcher {
|
||||
|
||||
val data: Flow<Data>
|
||||
|
|
@ -25,10 +25,11 @@ interface PortfolioFetcher {
|
|||
)
|
||||
|
||||
data class PortfolioBalance(
|
||||
val walletBalance: Lce<TokenListError, TotalFiatBalance>,
|
||||
val userWallet: UserWallet,
|
||||
val accountsBalance: AccountStatusList,
|
||||
) {
|
||||
val userWalletId: UserWalletId get() = accountsBalance.userWalletId
|
||||
val walletBalance get() = accountsBalance.totalFiatBalance
|
||||
val userWalletId: UserWalletId get() = userWallet.walletId
|
||||
}
|
||||
|
||||
sealed interface Mode {
|
||||
|
|
@ -36,6 +37,9 @@ interface PortfolioFetcher {
|
|||
data class Wallet(val walletId: UserWalletId) : Mode
|
||||
}
|
||||
|
||||
/**
|
||||
* @param[mode] supports runtime change [PortfolioFetcher.updateMode]
|
||||
*/
|
||||
interface Factory {
|
||||
fun create(mode: Mode, scope: CoroutineScope): PortfolioFetcher
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,27 +8,55 @@ import com.tangem.domain.models.account.AccountId
|
|||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* How to use
|
||||
* 1) Create and keep instance of [PortfolioFetcher] and [PortfolioSelectorController] in your Feature Model
|
||||
* 2) Provide them via [Params]
|
||||
* 3) Now you have a bridge between your Feature and PortfolioSelector
|
||||
* 4) Initial state is unselected. Select yourself [PortfolioSelectorController.selectAccount]
|
||||
* or offer users to select
|
||||
*
|
||||
* 5) Listen [PortfolioSelectorController.selectedAccount] or [PortfolioSelectorController.selectedAccountWithData]
|
||||
*
|
||||
* Note:
|
||||
* - Supports [PortfolioSelectorComponent.BottomSheet] and [PortfolioSelectorComponent.Content] modes
|
||||
*/
|
||||
interface PortfolioSelectorComponent : ComposableBottomSheetComponent, ComposableContentComponent {
|
||||
|
||||
val title: StateFlow<TextReference>
|
||||
|
||||
data class Params(
|
||||
val onDismiss: () -> Unit,
|
||||
val portfolioFetcher: PortfolioFetcher,
|
||||
val controller: PortfolioSelectorController,
|
||||
val bsCallback: BottomSheetCallback? = null,
|
||||
)
|
||||
|
||||
interface BottomSheetCallback {
|
||||
val onDismiss: () -> Unit
|
||||
val onBack: () -> Unit
|
||||
}
|
||||
|
||||
interface Factory : ComponentFactory<Params, PortfolioSelectorComponent>
|
||||
}
|
||||
|
||||
/**
|
||||
* How to use see [PortfolioSelectorComponent]
|
||||
*
|
||||
* if [isAccountMode] is false it's mean [selectedAccount] emit [AccountId] for Main account
|
||||
*/
|
||||
interface PortfolioSelectorController {
|
||||
val isAccountMode: Flow<Boolean>
|
||||
val selectedAccount: StateFlow<AccountId?>
|
||||
val selectedAccount: Flow<AccountId?>
|
||||
val selectedAccountSync: AccountId?
|
||||
|
||||
/**
|
||||
* for some Feature specific filtering
|
||||
* combine and update with your Feature data and [PortfolioFetcher.data]
|
||||
*/
|
||||
val isEnabled: MutableStateFlow<(UserWallet, AccountStatus) -> Boolean>
|
||||
|
||||
fun selectAccount(accountId: AccountId?)
|
||||
fun selectedAccountWithData(portfolioFetcher: PortfolioFetcher): Flow<Pair<UserWallet, AccountStatus>?>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
|||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
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.PortfolioFetcher
|
||||
import com.tangem.features.account.PortfolioFetcher.*
|
||||
|
|
@ -23,7 +22,6 @@ import kotlinx.coroutines.flow.*
|
|||
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,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
@ -80,14 +78,8 @@ internal class DefaultPortfolioFetcher @AssistedInject constructor(
|
|||
return combine(balanceFlows) { pairs -> pairs.toMap() }
|
||||
}
|
||||
|
||||
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 walletAccountsBalancesFlow(wallet: UserWallet): Flow<Pair<UserWallet, PortfolioBalance>> =
|
||||
accountStatusListFlow(wallet).map { wallet to PortfolioBalance(wallet, it) }
|
||||
|
||||
private fun accountStatusListFlow(wallet: UserWallet): Flow<AccountStatusList> =
|
||||
singleAccountStatusListSupplier(SingleAccountStatusListProducer.Params(wallet.walletId))
|
||||
|
|
|
|||
|
|
@ -31,13 +31,13 @@ internal class DefaultPortfolioSelectorComponent @AssistedInject constructor(
|
|||
.stateIn(componentScope, SharingStarted.Lazily, model.state.value.title)
|
||||
|
||||
override fun dismiss() {
|
||||
params.onDismiss()
|
||||
params.bsCallback?.onDismiss()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun BottomSheet() {
|
||||
val state by model.state.collectAsStateWithLifecycle()
|
||||
PortfolioSelectorBS(state, onDismiss = ::dismiss)
|
||||
PortfolioSelectorBS(state = state, onDismiss = ::dismiss, onBack = { params.bsCallback?.onBack() })
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -4,23 +4,33 @@ import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
|
|||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.features.account.PortfolioSelectorController
|
||||
import com.tangem.features.account.PortfolioFetcher
|
||||
import kotlinx.coroutines.flow.*
|
||||
import com.tangem.features.account.PortfolioSelectorController
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultPortfolioSelectorController @Inject constructor(
|
||||
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
|
||||
) : PortfolioSelectorController {
|
||||
|
||||
private val _selectedAccount: MutableStateFlow<AccountId?> = MutableStateFlow(null)
|
||||
private val _selectedAccount: MutableSharedFlow<AccountId?> = MutableSharedFlow(
|
||||
replay = 1,
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST,
|
||||
)
|
||||
|
||||
override val isAccountMode: Flow<Boolean> by lazy { isAccountsModeEnabledUseCase() }
|
||||
// without StateFlow and distinctUntilChanged to allow reselect and correct navigation
|
||||
override val selectedAccount: Flow<AccountId?> get() = _selectedAccount
|
||||
override val selectedAccountSync: AccountId? get() = _selectedAccount.replayCache.firstOrNull()
|
||||
|
||||
override val selectedAccount: StateFlow<AccountId?> get() = _selectedAccount
|
||||
override val isEnabled: MutableStateFlow<(UserWallet, AccountStatus) -> Boolean> = MutableStateFlow { _, _ -> true }
|
||||
|
||||
override fun selectAccount(accountId: AccountId?) {
|
||||
_selectedAccount.update { accountId }
|
||||
_selectedAccount.tryEmit(accountId)
|
||||
}
|
||||
|
||||
override fun selectedAccountWithData(portfolioFetcher: PortfolioFetcher): Flow<Pair<UserWallet, AccountStatus>?> =
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ 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
|
||||
|
||||
|
|
@ -45,9 +44,11 @@ internal class PortfolioSelectorModel @Inject constructor(
|
|||
init {
|
||||
combine(
|
||||
flow = isAccountsModeEnabledUseCase(),
|
||||
flow2 = loadBalanceWithArtwork(),
|
||||
transform = { isAccountsMode, (portfolioData, artworks) ->
|
||||
val uiList = buildUiList(isAccountsMode, portfolioData, artworks)
|
||||
flow2 = balanceFetcher.data,
|
||||
flow3 = walletImageFetcher.allWallets(ArtworkSize.SMALL),
|
||||
flow4 = selectorController.isEnabled,
|
||||
transform = { isAccountsMode, portfolioData, artworks, isEnabled ->
|
||||
val uiList = buildUiList(isAccountsMode, portfolioData, artworks, isEnabled)
|
||||
val title = when (isAccountsMode) {
|
||||
true -> resourceReference(R.string.common_choose_account)
|
||||
false -> resourceReference(R.string.common_choose_wallet)
|
||||
|
|
@ -66,24 +67,25 @@ internal class PortfolioSelectorModel @Inject constructor(
|
|||
isAccountsMode: Boolean,
|
||||
portfolioData: PortfolioFetcher.Data,
|
||||
artworks: Map<UserWalletId, UserWalletItemUM.ImageState>,
|
||||
isEnabled: (UserWallet, AccountStatus) -> Boolean,
|
||||
): List<PortfolioSelectorItemUM> = when (isAccountsMode) {
|
||||
true -> buildAccountsList(portfolioData, artworks)
|
||||
false -> buildWalletList(portfolioData, artworks)
|
||||
true -> buildAccountsList(portfolioData, artworks, isEnabled)
|
||||
false -> buildWalletList(portfolioData, artworks, isEnabled)
|
||||
}
|
||||
|
||||
private fun buildWalletList(
|
||||
portfolioData: PortfolioFetcher.Data,
|
||||
artworks: Map<UserWalletId, UserWalletItemUM.ImageState>,
|
||||
isEnabled: (UserWallet, AccountStatus) -> Boolean,
|
||||
): 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 balance = portfolio.walletBalance
|
||||
val walletItemUM = UserWalletItemUMConverter(
|
||||
onClick = {
|
||||
// todo account
|
||||
// selectorController.selectAccount(portfolio.accountsBalance.mainAccount)
|
||||
selectorController.selectAccount(portfolio.accountsBalance.mainAccount.account.accountId)
|
||||
},
|
||||
appCurrency = appCurrency,
|
||||
balance = balance,
|
||||
|
|
@ -92,7 +94,10 @@ internal class PortfolioSelectorModel @Inject constructor(
|
|||
isAuthMode = false,
|
||||
).convert(wallet)
|
||||
if (walletItemUM.isEnabled) {
|
||||
add(PortfolioSelectorItemUM.Portfolio(walletItemUM))
|
||||
val isEnabledByFeature = isEnabled(wallet, portfolio.accountsBalance.mainAccount)
|
||||
val finalWalletItemUM =
|
||||
if (isEnabledByFeature) walletItemUM else walletItemUM.copy(isEnabled = false)
|
||||
add(PortfolioSelectorItemUM.Portfolio(finalWalletItemUM))
|
||||
} else {
|
||||
lockedWallets.add(PortfolioSelectorItemUM.Portfolio(walletItemUM))
|
||||
}
|
||||
|
|
@ -110,16 +115,16 @@ internal class PortfolioSelectorModel @Inject constructor(
|
|||
private fun buildAccountsList(
|
||||
portfolioData: PortfolioFetcher.Data,
|
||||
artworks: Map<UserWalletId, UserWalletItemUM.ImageState>,
|
||||
isEnabled: (UserWallet, AccountStatus) -> Boolean,
|
||||
): 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 balance = portfolio.walletBalance
|
||||
val walletItemUM = UserWalletItemUMConverter(
|
||||
onClick = {
|
||||
// todo account
|
||||
// selectorController.selectAccount(portfolio.accountsBalance.mainAccount)
|
||||
selectorController.selectAccount(portfolio.accountsBalance.mainAccount.account.accountId)
|
||||
},
|
||||
appCurrency = appCurrency,
|
||||
balance = balance,
|
||||
|
|
@ -138,8 +143,8 @@ internal class PortfolioSelectorModel @Inject constructor(
|
|||
)
|
||||
add(walletTitle)
|
||||
|
||||
add(PortfolioSelectorItemUM.Portfolio(walletItemUM))
|
||||
portfolio.accountsBalance.accountStatuses.forEach { accountStatus ->
|
||||
val isEnabledByFeature = isEnabled(wallet, accountStatus)
|
||||
val account = accountStatus.account
|
||||
val accountBalance = when (accountStatus) {
|
||||
is AccountStatus.CryptoPortfolio -> accountStatus.tokenList.totalFiatBalance
|
||||
|
|
@ -148,6 +153,7 @@ internal class PortfolioSelectorModel @Inject constructor(
|
|||
onClick = { selectorController.selectAccount(account.accountId) },
|
||||
appCurrency = appCurrency,
|
||||
accountBalance = accountBalance,
|
||||
isEnabled = isEnabledByFeature,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
).convert(account)
|
||||
add(PortfolioSelectorItemUM.Portfolio(accountItemUM))
|
||||
|
|
@ -163,22 +169,6 @@ internal class PortfolioSelectorModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -19,21 +19,26 @@ import com.tangem.features.account.impl.R
|
|||
import com.tangem.features.account.selector.entity.PortfolioSelectorUM
|
||||
|
||||
@Composable
|
||||
internal fun PortfolioSelectorBS(state: PortfolioSelectorUM, onDismiss: () -> Unit, modifier: Modifier = Modifier) {
|
||||
internal fun PortfolioSelectorBS(
|
||||
state: PortfolioSelectorUM,
|
||||
onDismiss: () -> Unit,
|
||||
onBack: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
TangemModalBottomSheet<TangemBottomSheetConfigContent.Empty>(
|
||||
config = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = onDismiss,
|
||||
content = TangemBottomSheetConfigContent.Empty,
|
||||
),
|
||||
onBack = onDismiss,
|
||||
onBack = onBack,
|
||||
scrollableContent = false,
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
containerColor = TangemTheme.colors.background.tertiary,
|
||||
title = {
|
||||
TangemModalBottomSheetTitle(
|
||||
title = state.title,
|
||||
startIconRes = R.drawable.ic_back_24,
|
||||
onStartClick = onDismiss,
|
||||
onStartClick = onBack,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
|
|
@ -54,7 +59,8 @@ private fun Preview(@PreviewParameter(PortfolioSelectorPreviewStateProvider::cla
|
|||
PortfolioSelectorBS(
|
||||
state = params,
|
||||
onDismiss = {},
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.secondary),
|
||||
modifier = Modifier,
|
||||
onBack = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,10 @@ package com.tangem.features.account.selector.ui
|
|||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
|
|
@ -18,9 +20,10 @@ 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.UserWalletItemRow
|
||||
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
|
||||
import com.tangem.common.ui.userwallet.state.UserWalletItemUM.ImageState
|
||||
import com.tangem.core.ui.extensions.conditional
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
|
|
@ -66,12 +69,16 @@ internal fun PortfolioSelectorContent(
|
|||
}
|
||||
|
||||
when (item) {
|
||||
is PortfolioSelectorItemUM.Portfolio -> UserWalletItem(
|
||||
is PortfolioSelectorItemUM.Portfolio -> UserWalletItemRow(
|
||||
state = item.item,
|
||||
modifier = offsetModifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = TangemTheme.dimens.size68)
|
||||
.clip(RoundedCornerShape(TangemTheme.dimens.radius14))
|
||||
.background(color = TangemTheme.colors.background.primary)
|
||||
.let { if (!item.item.isEnabled) it.alpha(DISABLED_WALLET_ALPHA) else it },
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.clickable(enabled = item.item.isEnabled, onClick = item.item.onClick)
|
||||
.padding(all = TangemTheme.dimens.spacing12)
|
||||
.conditional(!item.item.isEnabled) { alpha(DISABLED_WALLET_ALPHA) },
|
||||
)
|
||||
is PortfolioSelectorItemUM.GroupTitle -> WalletNameRow(
|
||||
model = item,
|
||||
|
|
@ -103,7 +110,7 @@ private fun Preview(@PreviewParameter(PortfolioSelectorPreviewStateProvider::cla
|
|||
TangemThemePreview {
|
||||
PortfolioSelectorContent(
|
||||
state = params,
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.secondary),
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.tertiary),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -127,6 +134,9 @@ internal object PortfolioSelectorPreviewData {
|
|||
),
|
||||
)
|
||||
|
||||
private val lockedAccountItem: UserWalletItemUM
|
||||
get() = accountItem.copy(isEnabled = false)
|
||||
|
||||
private val walletItem: UserWalletItemUM
|
||||
get() = UserWalletItemUM(
|
||||
id = UserWalletId(UUID.randomUUID().toString().encodeToByteArray()),
|
||||
|
|
@ -150,7 +160,7 @@ internal object PortfolioSelectorPreviewData {
|
|||
accountItem
|
||||
.let { PortfolioSelectorItemUM.Portfolio(it) }
|
||||
.let(::add)
|
||||
accountItem
|
||||
lockedAccountItem
|
||||
.let { PortfolioSelectorItemUM.Portfolio(it) }
|
||||
.let(::add)
|
||||
PortfolioSelectorItemUM.GroupTitle(
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import androidx.compose.ui.Modifier
|
|||
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.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
|
|
@ -46,7 +45,7 @@ class DefaultReferralComponent @AssistedInject constructor(
|
|||
params = PortfolioSelectorComponent.Params(
|
||||
portfolioFetcher = model.portfolioFetcher,
|
||||
controller = model.portfolioSelectorController,
|
||||
onDismiss = model.bottomSheetNavigation::dismiss,
|
||||
bsCallback = model.portfolioSelectorCallback,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import androidx.compose.runtime.setValue
|
|||
import arrow.core.getOrElse
|
||||
import com.arkivanov.decompose.router.slot.SlotNavigation
|
||||
import com.arkivanov.decompose.router.slot.activate
|
||||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.common.ui.userwallet.ext.walletInterationIcon
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
|
|
@ -41,6 +42,7 @@ import com.tangem.feature.referral.models.DemoModeException
|
|||
import com.tangem.feature.referral.models.ReferralStateHolder
|
||||
import com.tangem.feature.referral.models.ReferralStateHolder.*
|
||||
import com.tangem.features.account.PortfolioFetcher
|
||||
import com.tangem.features.account.PortfolioSelectorComponent
|
||||
import com.tangem.features.account.PortfolioSelectorController
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
|
@ -84,6 +86,11 @@ internal class ReferralModel @Inject constructor(
|
|||
var uiState: ReferralStateHolder by mutableStateOf(createInitiallyUiState())
|
||||
private set
|
||||
|
||||
val portfolioSelectorCallback = object : PortfolioSelectorComponent.BottomSheetCallback {
|
||||
override val onDismiss: () -> Unit = { bottomSheetNavigation::dismiss }
|
||||
override val onBack: () -> Unit = { bottomSheetNavigation::dismiss }
|
||||
}
|
||||
|
||||
init {
|
||||
analyticsEventHandler.send(ReferralEvents.ReferralScreenOpened)
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
|
|
@ -172,7 +179,7 @@ internal class ReferralModel @Inject constructor(
|
|||
modelScope.launch {
|
||||
val lastInfoState = uiState.referralInfoState
|
||||
val portfolioId = when (accountsFeatureToggles.isFeatureEnabled) {
|
||||
true -> PortfolioId(requireNotNull(portfolioSelectorController.selectedAccount.value))
|
||||
true -> PortfolioId(requireNotNull(portfolioSelectorController.selectedAccountSync))
|
||||
false -> PortfolioId(params.userWalletId)
|
||||
}
|
||||
runCatching { referralInteractor.startReferral(portfolioId) }
|
||||
|
|
@ -259,7 +266,7 @@ internal class ReferralModel @Inject constructor(
|
|||
|
||||
private suspend fun selectAccount(referralData: ReferralData) {
|
||||
when (referralData) {
|
||||
is ReferralData.NonParticipantData -> if (portfolioSelectorController.selectedAccount.value == null) {
|
||||
is ReferralData.NonParticipantData -> if (portfolioSelectorController.selectedAccountSync == null) {
|
||||
portfolioSelectorController.selectAccount(
|
||||
accountId = walletAccounts(params.userWalletId).first().mainAccount.account.accountId,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import kotlinx.coroutines.flow.Flow
|
|||
|
||||
interface UserWalletImageFetcher {
|
||||
|
||||
fun allWallets(size: ArtworkSize): Flow<Map<UserWalletId, UserWalletItemUM.ImageState>>
|
||||
fun walletImage(walletId: UserWalletId, size: ArtworkSize): Flow<UserWalletItemUM.ImageState>
|
||||
fun walletImage(cardDTO: CardDTO, size: ArtworkSize): Flow<UserWalletItemUM.ImageState>
|
||||
fun walletImage(wallet: UserWallet, size: ArtworkSize): Flow<UserWalletItemUM.ImageState>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.tangem.domain.models.wallet.UserWallet
|
|||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.GetCardImageUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.features.wallet.utils.UserWalletImageFetcher
|
||||
import com.tangem.operations.attestation.ArtworkSize
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
|
@ -17,12 +18,21 @@ import javax.inject.Inject
|
|||
class DefaultUserWalletImageFetcher @Inject constructor(
|
||||
private val getCardImageUseCase: GetCardImageUseCase,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val getWalletsUseCase: GetWalletsUseCase,
|
||||
private val artworkUMConverter: ArtworkUMConverter,
|
||||
) : UserWalletImageFetcher {
|
||||
|
||||
private val smallCache = MutableStateFlow(mapOf<String, ArtworkUM>())
|
||||
private val largeCache = MutableStateFlow(mapOf<String, ArtworkUM>())
|
||||
|
||||
override fun allWallets(size: ArtworkSize): Flow<Map<UserWalletId, UserWalletItemUM.ImageState>> =
|
||||
getWalletsUseCase.invoke()
|
||||
.map { list -> list.map { it.walletId } }
|
||||
.distinctUntilChanged()
|
||||
.map { list -> list.map { walletId -> walletImage(walletId, size).map { image -> walletId to image } } }
|
||||
.flatMapLatest { flows -> combine(flows) { it.toMap() } }
|
||||
.distinctUntilChanged()
|
||||
|
||||
override fun walletImage(wallet: UserWallet, size: ArtworkSize): Flow<UserWalletItemUM.ImageState> = when (wallet) {
|
||||
is UserWallet.Cold -> walletImage(wallet.scanResponse.card, size)
|
||||
is UserWallet.Hot -> flowOf(UserWalletItemUM.ImageState.MobileWallet)
|
||||
|
|
@ -31,11 +41,8 @@ class DefaultUserWalletImageFetcher @Inject constructor(
|
|||
override fun walletsImage(
|
||||
wallets: Collection<UserWallet>,
|
||||
size: ArtworkSize,
|
||||
): Flow<Map<UserWalletId, UserWalletItemUM.ImageState>> = wallets
|
||||
.map { userWallet -> walletImage(userWallet, size).map { imageState -> userWallet.walletId to imageState } }
|
||||
.merge()
|
||||
.runningFold(mapOf<UserWalletId, UserWalletItemUM.ImageState>()) { map, newState -> map.plus(newState) }
|
||||
.filter { it.size >= wallets.size } // prevent spam, waiting full map
|
||||
): Flow<Map<UserWalletId, UserWalletItemUM.ImageState>> = allWallets(size)
|
||||
.map { allWallets -> allWallets.filter { (walletId, _) -> wallets.any { it.walletId == walletId } } }
|
||||
.distinctUntilChanged()
|
||||
|
||||
override fun walletImage(walletId: UserWalletId, size: ArtworkSize): Flow<UserWalletItemUM.ImageState> = flow {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue