Updated on 2026-08-14

This commit is contained in:
Tangem 2025-10-15 15:59:43 +07:00
parent b92bb00c7f
commit 5e6bf6aaae
14 changed files with 172 additions and 109 deletions

View file

@ -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),

View file

@ -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,
)
}
}
}

View file

@ -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
}

View file

@ -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>?>

View file

@ -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))

View file

@ -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

View file

@ -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>?> =

View file

@ -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,

View file

@ -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 = {},
)
}
}

View file

@ -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(

View file

@ -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,
),
)

View file

@ -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,
)

View file

@ -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>

View file

@ -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 {