Updated on 2026-08-14
This commit is contained in:
parent
a730af0f1f
commit
98915e2507
21 changed files with 448 additions and 45 deletions
|
|
@ -27,6 +27,7 @@ internal sealed class WalletMultiCurrencyState : WalletState.ContentState() {
|
|||
override val tokensListState: WalletTokensListState,
|
||||
override val event: StateEvent<WalletEvent> = consumedEvent(),
|
||||
override val isBalanceHidden: Boolean,
|
||||
val isManageTokensAvailable: Boolean = true,
|
||||
val tokenActionsBottomSheet: ActionsBottomSheetConfig?,
|
||||
val onManageTokensClick: () -> Unit,
|
||||
) : WalletMultiCurrencyState()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.factory
|
||||
|
||||
import com.tangem.domain.tokens.model.TokenList
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
|
||||
data class TokenListWithWallet(
|
||||
val tokenList: TokenList,
|
||||
val wallet: UserWallet,
|
||||
)
|
||||
|
|
@ -31,7 +31,7 @@ internal class WalletLoadedTokensListConverter(
|
|||
appCurrencyProvider: Provider<AppCurrency>,
|
||||
currentWalletProvider: Provider<UserWallet>,
|
||||
clickIntents: WalletClickIntents,
|
||||
) : Converter<Either<TokenListError, TokenList>, WalletState> {
|
||||
) : Converter<Either<TokenListError, TokenListWithWallet>, WalletState> {
|
||||
|
||||
private val tokenListStateConverter = TokenListToWalletStateConverter(
|
||||
currentStateProvider = currentStateProvider,
|
||||
|
|
@ -40,7 +40,7 @@ internal class WalletLoadedTokensListConverter(
|
|||
clickIntents = clickIntents,
|
||||
)
|
||||
|
||||
override fun convert(value: Either<TokenListError, TokenList>): WalletState {
|
||||
override fun convert(value: Either<TokenListError, TokenListWithWallet>): WalletState {
|
||||
return value.fold(
|
||||
ifLeft = tokenListErrorConverter::convert,
|
||||
ifRight = tokenListStateConverter::convert,
|
||||
|
|
|
|||
|
|
@ -38,7 +38,9 @@ internal class WalletSkeletonStateConverter(
|
|||
override fun convert(value: SkeletonModel): WalletState.ContentState {
|
||||
val selectedWallet = value.wallets[value.selectedWalletIndex]
|
||||
|
||||
return if (selectedWallet.isMultiCurrency) {
|
||||
val isSingleWalletWithToken = !selectedWallet.isMultiCurrency &&
|
||||
selectedWallet.scanResponse.walletData?.token != null
|
||||
return if (selectedWallet.isMultiCurrency || isSingleWalletWithToken) {
|
||||
createMultiCurrencyState(value = value)
|
||||
} else {
|
||||
createSingleCurrencyState(value = value, currencyName = selectedWallet.getPrimaryCurrencyName())
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import com.tangem.domain.tokens.error.CurrencyStatusError
|
|||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.TokenActionsState
|
||||
import com.tangem.domain.tokens.model.TokenList
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.domain.txhistory.models.TxHistoryListError
|
||||
import com.tangem.domain.txhistory.models.TxHistoryStateError
|
||||
|
|
@ -160,8 +159,8 @@ internal class WalletStateFactory(
|
|||
)
|
||||
}
|
||||
|
||||
fun getStateByTokensList(maybeTokenList: Either<TokenListError, TokenList>): WalletState {
|
||||
return loadedTokensListConverter.convert(maybeTokenList)
|
||||
fun getStateByTokensList(maybeTokenListWithWallet: Either<TokenListError, TokenListWithWallet>): WalletState {
|
||||
return loadedTokensListConverter.convert(maybeTokenListWithWallet)
|
||||
}
|
||||
|
||||
fun getStateByTokenListError(error: TokenListError): WalletState {
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ private fun BaseScaffold(
|
|||
topBar = { WalletTopBar(config = state.topBarConfig) },
|
||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
||||
floatingActionButton = {
|
||||
if (state is WalletMultiCurrencyState.Content) {
|
||||
if (state is WalletMultiCurrencyState.Content && state.isManageTokensAvailable) {
|
||||
ManageTokensButton(onManageTokensClick = state.onManageTokensClick)
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.tangem.domain.tokens.model.TokenList
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState.OrganizeTokensButtonState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState.TokensListItemState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.factory.TokenListWithWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
|
|
@ -18,23 +19,25 @@ import kotlinx.collections.immutable.persistentListOf
|
|||
internal class TokenListToContentItemsConverter(
|
||||
appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
) : Converter<TokenList, WalletTokensListState> {
|
||||
) : Converter<TokenListWithWallet, WalletTokensListState> {
|
||||
|
||||
private val tokenStatusConverter = CryptoCurrencyStatusToTokenItemConverter(
|
||||
appCurrencyProvider = appCurrencyProvider,
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
|
||||
override fun convert(value: TokenList): WalletTokensListState {
|
||||
return when (value) {
|
||||
override fun convert(value: TokenListWithWallet): WalletTokensListState {
|
||||
val isSingleCurrencyWalletWithToken = !value.wallet.isMultiCurrency &&
|
||||
value.wallet.scanResponse.walletData?.token != null
|
||||
return when (val tokenList = value.tokenList) {
|
||||
is TokenList.Empty -> WalletTokensListState.Empty
|
||||
is TokenList.GroupedByNetwork -> WalletTokensListState.Content(
|
||||
items = value.mapToMultiCurrencyItems(),
|
||||
organizeTokensButton = value.mapToOrganizeTokensButtonState(),
|
||||
items = tokenList.mapToMultiCurrencyItems(),
|
||||
organizeTokensButton = tokenList.mapToOrganizeTokensButtonState(isSingleCurrencyWalletWithToken),
|
||||
)
|
||||
is TokenList.Ungrouped -> WalletTokensListState.Content(
|
||||
items = value.mapToMultiCurrencyItems(),
|
||||
organizeTokensButton = value.mapToOrganizeTokensButtonState(),
|
||||
items = tokenList.mapToMultiCurrencyItems(),
|
||||
organizeTokensButton = tokenList.mapToOrganizeTokensButtonState(isSingleCurrencyWalletWithToken),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -51,17 +54,23 @@ internal class TokenListToContentItemsConverter(
|
|||
}
|
||||
}
|
||||
|
||||
private fun TokenList.GroupedByNetwork.mapToOrganizeTokensButtonState(): OrganizeTokensButtonState {
|
||||
private fun TokenList.GroupedByNetwork.mapToOrganizeTokensButtonState(
|
||||
isSingleCurrencyWithTokenWallet: Boolean,
|
||||
): OrganizeTokensButtonState {
|
||||
return getOrganizeTokensButtonState(
|
||||
isLoading = totalFiatBalance is TokenList.FiatBalance.Loading,
|
||||
currenciesSize = groups.flatMap(NetworkGroup::currencies).size,
|
||||
isSingleCurrencyWithTokenWallet = isSingleCurrencyWithTokenWallet,
|
||||
)
|
||||
}
|
||||
|
||||
private fun TokenList.Ungrouped.mapToOrganizeTokensButtonState(): OrganizeTokensButtonState {
|
||||
private fun TokenList.Ungrouped.mapToOrganizeTokensButtonState(
|
||||
isSingleCurrencyWithTokenWallet: Boolean,
|
||||
): OrganizeTokensButtonState {
|
||||
return getOrganizeTokensButtonState(
|
||||
isLoading = totalFiatBalance is TokenList.FiatBalance.Loading,
|
||||
currenciesSize = currencies.size,
|
||||
isSingleCurrencyWithTokenWallet = isSingleCurrencyWithTokenWallet,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -88,8 +97,12 @@ internal class TokenListToContentItemsConverter(
|
|||
return this
|
||||
}
|
||||
|
||||
private fun getOrganizeTokensButtonState(isLoading: Boolean, currenciesSize: Int): OrganizeTokensButtonState {
|
||||
return if (currenciesSize > 1) {
|
||||
private fun getOrganizeTokensButtonState(
|
||||
isLoading: Boolean,
|
||||
currenciesSize: Int,
|
||||
isSingleCurrencyWithTokenWallet: Boolean,
|
||||
): OrganizeTokensButtonState {
|
||||
return if (currenciesSize > 1 && !isSingleCurrencyWithTokenWallet) {
|
||||
OrganizeTokensButtonState.Visible(
|
||||
isEnabled = !isLoading,
|
||||
onClick = clickIntents::onOrganizeTokensClick,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencySt
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.factory.TokenListWithWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
|
@ -18,19 +19,23 @@ internal class TokenListToWalletStateConverter(
|
|||
private val currentWalletProvider: Provider<UserWallet>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
clickIntents: WalletClickIntents,
|
||||
) : Converter<TokenList, WalletState> {
|
||||
) : Converter<TokenListWithWallet, WalletState> {
|
||||
|
||||
private val tokenListToContentConverter = TokenListToContentItemsConverter(
|
||||
appCurrencyProvider = appCurrencyProvider,
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
|
||||
override fun convert(value: TokenList): WalletState {
|
||||
override fun convert(value: TokenListWithWallet): WalletState {
|
||||
val tokenList = value.tokenList
|
||||
val isSingleCurrencyWalletWithToken = !value.wallet.isMultiCurrency &&
|
||||
value.wallet.scanResponse.walletData?.token != null
|
||||
return when (val state = currentStateProvider()) {
|
||||
is WalletMultiCurrencyState.Content -> {
|
||||
state.copy(
|
||||
walletsListConfig = state.updateSelectedWallet(fiatBalance = value.totalFiatBalance),
|
||||
walletsListConfig = state.updateSelectedWallet(fiatBalance = tokenList.totalFiatBalance),
|
||||
tokensListState = tokenListToContentConverter.convert(value = value),
|
||||
isManageTokensAvailable = !isSingleCurrencyWalletWithToken,
|
||||
)
|
||||
}
|
||||
is WalletMultiCurrencyState.Locked,
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ import com.tangem.feature.wallet.presentation.wallet.analytics.PortfolioEvent
|
|||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.factory.TokenListWithWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletStateFactory
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -92,6 +93,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
private val updateWalletUseCase: UpdateWalletUseCase,
|
||||
private val deleteWalletUseCase: DeleteWalletUseCase,
|
||||
private val getTokenListUseCase: GetTokenListUseCase,
|
||||
private val getCardTokensListUseCase: GetCardTokensListUseCase,
|
||||
private val fetchTokenListUseCase: FetchTokenListUseCase,
|
||||
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
|
||||
private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase,
|
||||
|
|
@ -592,10 +594,14 @@ internal class WalletViewModel @Inject constructor(
|
|||
viewModelScope.launch(dispatchers.io) {
|
||||
val userWallet = getWallet(index = state.walletsListConfig.selectedWalletIndex)
|
||||
|
||||
val isSingleWalletWithTokens = !userWallet.isMultiCurrency &&
|
||||
userWallet.scanResponse.walletData?.token != null
|
||||
|
||||
getNetworkCoinStatusUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
networkId = cryptoCurrencyStatus.currency.network.id,
|
||||
derivationPath = cryptoCurrencyStatus.currency.network.derivationPath,
|
||||
isSingleWalletWithTokens = isSingleWalletWithTokens,
|
||||
)
|
||||
.take(count = 1)
|
||||
.collectLatest {
|
||||
|
|
@ -916,6 +922,9 @@ internal class WalletViewModel @Inject constructor(
|
|||
uiState = stateFactory.getLockedState()
|
||||
}
|
||||
wallet.isMultiCurrency -> getMultiCurrencyContent(wallet, index)
|
||||
isSingleWalletWithTokens(wallet) -> {
|
||||
getSingleCurrencyWithTokenContent(index)
|
||||
}
|
||||
!wallet.isMultiCurrency -> getSingleCurrencyContent(index)
|
||||
}
|
||||
}
|
||||
|
|
@ -933,7 +942,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
tokenListFlow
|
||||
.distinctUntilChanged()
|
||||
.onEach { maybeTokenList ->
|
||||
uiState = stateFactory.getStateByTokensList(maybeTokenList)
|
||||
uiState = stateFactory.getStateByTokensList(maybeTokenList.getTokenListWithWallet(wallet))
|
||||
|
||||
maybeTokenList.onRight { checkMultiWalletWithFunds(it) }
|
||||
|
||||
|
|
@ -978,6 +987,10 @@ internal class WalletViewModel @Inject constructor(
|
|||
.saveIn(updateWcJobHolder)
|
||||
}
|
||||
|
||||
private fun isSingleWalletWithTokens(userWallet: UserWallet): Boolean {
|
||||
return userWallet.scanResponse.walletData?.token != null && !userWallet.isMultiCurrency
|
||||
}
|
||||
|
||||
private fun List<CryptoCurrencyStatus>.isAllCurrenciesLoaded(): Boolean {
|
||||
return !this.any { it.value is CryptoCurrencyStatus.Loading }
|
||||
}
|
||||
|
|
@ -1008,6 +1021,14 @@ internal class WalletViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun Either<TokenListError, TokenList>.getTokenListWithWallet(
|
||||
userWallet: UserWallet,
|
||||
): Either<TokenListError, TokenListWithWallet> {
|
||||
return this.map {
|
||||
TokenListWithWallet(it, userWallet)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSingleCurrencyContent(index: Int) {
|
||||
val wallet = getWallet(index)
|
||||
getPrimaryCurrencyStatusUpdatesUseCase(wallet.walletId)
|
||||
|
|
@ -1032,6 +1053,30 @@ internal class WalletViewModel @Inject constructor(
|
|||
.saveIn(marketPriceJobHolder)
|
||||
}
|
||||
|
||||
private fun getSingleCurrencyWithTokenContent(walletIndex: Int) {
|
||||
val state = requireNotNull(uiState as? WalletMultiCurrencyState) {
|
||||
"Impossible to get a token list updates if state isn't WalletMultiCurrencyState"
|
||||
}
|
||||
|
||||
val wallet = getWallet(walletIndex)
|
||||
|
||||
getCardTokensListUseCase(userWalletId = state.walletsListConfig.wallets[walletIndex].id)
|
||||
.distinctUntilChanged()
|
||||
.onEach { maybeTokenList ->
|
||||
uiState = stateFactory.getStateByTokensList(maybeTokenList.getTokenListWithWallet(wallet))
|
||||
|
||||
maybeTokenList.onRight { checkMultiWalletWithFunds(it) }
|
||||
|
||||
updateNotifications(
|
||||
index = walletIndex,
|
||||
tokenList = maybeTokenList.fold(ifLeft = { null }, ifRight = { it }),
|
||||
)
|
||||
}
|
||||
.flowOn(dispatchers.io)
|
||||
.launchIn(viewModelScope)
|
||||
.saveIn(tokensJobHolder)
|
||||
}
|
||||
|
||||
private fun updateTxHistory(userWalletId: UserWalletId, currency: CryptoCurrency, refresh: Boolean) {
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
val txHistoryItemsCountEither = txHistoryItemsCountUseCase(
|
||||
|
|
@ -1095,10 +1140,15 @@ internal class WalletViewModel @Inject constructor(
|
|||
val wallet = getWallet(walletIndex)
|
||||
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
val result = fetchTokenListUseCase(wallet.walletId, refresh = true)
|
||||
if (isSingleWalletWithTokens(wallet)) {
|
||||
// TODO add refresh for nodl cards ([REDACTED_JIRA])
|
||||
delay(timeMillis = 1000)
|
||||
} else {
|
||||
val result = fetchTokenListUseCase(wallet.walletId, refresh = true)
|
||||
uiState = result.fold(stateFactory::getStateByTokenListError) { uiState }
|
||||
}
|
||||
|
||||
uiState = stateFactory.getRefreshedState()
|
||||
uiState = result.fold(stateFactory::getStateByTokenListError) { uiState }
|
||||
}.saveIn(refreshContentJobHolder)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue