diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt index 6991db467a..5b09fe934e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt @@ -7,6 +7,7 @@ import com.tangem.core.ui.components.transactions.state.TransactionState import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.feature.wallet.presentation.wallet.state.components.* import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.MutableStateFlow @@ -19,7 +20,7 @@ import kotlinx.coroutines.flow.MutableStateFlow internal sealed class WalletSingleCurrencyState : WalletState.ContentState() { /** Manage buttons */ - abstract val buttons: ImmutableList + abstract val buttons: PersistentList /** Transactions history state */ abstract val txHistoryState: TxHistoryState @@ -31,7 +32,7 @@ internal sealed class WalletSingleCurrencyState : WalletState.ContentState() { override val pullToRefreshConfig: WalletPullToRefreshConfig, override val notifications: ImmutableList, override val bottomSheetConfig: WalletBottomSheetConfig?, - override val buttons: ImmutableList, + override val buttons: PersistentList, override val txHistoryState: TxHistoryState, val marketPriceBlockState: MarketPriceBlockState, ) : WalletSingleCurrencyState() @@ -41,7 +42,7 @@ internal sealed class WalletSingleCurrencyState : WalletState.ContentState() { override val topBarConfig: WalletTopBarConfig, override val walletsListConfig: WalletsListConfig, override val pullToRefreshConfig: WalletPullToRefreshConfig, - override val buttons: ImmutableList, + override val buttons: PersistentList, override val onUnlockWalletsNotificationClick: () -> Unit, override val onUnlockClick: () -> Unit, override val onScanClick: () -> Unit, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletCryptoCurrencyActionsConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletCryptoCurrencyActionsConverter.kt index 597680d5eb..38e18cdf52 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletCryptoCurrencyActionsConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletCryptoCurrencyActionsConverter.kt @@ -8,8 +8,8 @@ import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import com.tangem.utils.converter.Converter -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toImmutableList +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.toPersistentList internal class WalletCryptoCurrencyActionsConverter( private val currentStateProvider: Provider, @@ -26,7 +26,7 @@ internal class WalletCryptoCurrencyActionsConverter( } } - private fun List.mapToManageButtons(): ImmutableList { + private fun List.mapToManageButtons(): PersistentList { return this .mapNotNull { action -> when (action) { @@ -45,6 +45,6 @@ internal class WalletCryptoCurrencyActionsConverter( is TokenActionsState.ActionState.Swap -> null } } - .toImmutableList() + .toPersistentList() } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt index a98b75e4f8..7960bab25a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt @@ -9,7 +9,6 @@ import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletLoadedTokensListConverter.LoadedTokensListModel import com.tangem.feature.wallet.presentation.wallet.utils.TokenListErrorConverter import com.tangem.feature.wallet.presentation.wallet.utils.TokenListToWalletStateConverter import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents @@ -27,11 +26,12 @@ import com.tangem.utils.converter.Converter */ internal class WalletLoadedTokensListConverter( private val currentStateProvider: Provider, + private val tokenListErrorConverter: TokenListErrorConverter, appCurrencyProvider: Provider, cardTypeResolverProvider: Provider, currentWalletProvider: Provider, clickIntents: WalletClickIntents, -) : Converter { +) : Converter, WalletState> { private val tokenListStateConverter = TokenListToWalletStateConverter( currentStateProvider = currentStateProvider, @@ -42,26 +42,10 @@ internal class WalletLoadedTokensListConverter( clickIntents = clickIntents, ) - private val tokenListErrorStateConverter = TokenListErrorConverter( - currentStateProvider = currentStateProvider, - ) - - override fun convert(value: LoadedTokensListModel): WalletState { - return value.tokenListEither.fold( - ifLeft = tokenListErrorStateConverter::convert, - ifRight = { - tokenListStateConverter.convert( - value = TokenListToWalletStateConverter.TokensListModel( - tokenList = it, - isRefreshing = value.isRefreshing, - ), - ) - }, + override fun convert(value: Either): WalletState { + return value.fold( + ifLeft = tokenListErrorConverter::convert, + ifRight = tokenListStateConverter::convert, ) } - - data class LoadedTokensListModel( - val tokenListEither: Either, - val isRefreshing: Boolean, - ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLockedConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLockedConverter.kt index 5ec6613bb8..3e89437610 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLockedConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLockedConverter.kt @@ -13,7 +13,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTopB import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import com.tangem.utils.converter.Converter -import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList @@ -99,7 +99,7 @@ internal class WalletLockedConverter( ) } - private fun createButtons(): ImmutableList { + private fun createButtons(): PersistentList { return persistentListOf( WalletManageButton.Buy(enabled = false, onClick = {}), WalletManageButton.Send(enabled = false, onClick = {}), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletRefreshStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletRefreshStateConverter.kt index f820ff8444..a7d44c70d8 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletRefreshStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletRefreshStateConverter.kt @@ -1,124 +1,112 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory import com.tangem.common.Provider -import com.tangem.core.ui.components.marketprice.MarketPriceBlockState -import com.tangem.core.ui.components.transactions.state.TxHistoryState -import com.tangem.domain.common.CardTypesResolver -import com.tangem.feature.wallet.presentation.common.state.TokenItemState import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState 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.* -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState.TokensListItemState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletPullToRefreshConfig +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import com.tangem.utils.converter.Converter -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toImmutableList -import kotlinx.collections.immutable.toPersistentList -import kotlinx.coroutines.flow.update +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.mutate internal class WalletRefreshStateConverter( private val currentStateProvider: Provider, - private val currentCardTypeResolverProvider: Provider, - private val clickIntents: WalletClickIntents, -) : Converter { + private val intents: WalletClickIntents, +) : Converter { - override fun convert(value: Unit): WalletState { - return when (val state = currentStateProvider()) { - is WalletMultiCurrencyState.Content -> state.getRefreshState() - is WalletSingleCurrencyState.Content -> state.getRefreshState() - else -> state - } - } + override fun convert(value: Boolean): WalletState { + val state = currentStateProvider() + val contentState = state as? WalletState.ContentState ?: return state - private fun WalletMultiCurrencyState.Content.getRefreshState(): WalletMultiCurrencyState.Content { - return copy( - walletsListConfig = createWalletsListConfig(), - pullToRefreshConfig = createPullToRefreshConfig(), - tokensListState = createTokenListState(), - ) - } - - private fun WalletSingleCurrencyState.Content.getRefreshState(): WalletSingleCurrencyState.Content { - return copy( - walletsListConfig = createWalletsListConfig(), - pullToRefreshConfig = createPullToRefreshConfig(), - buttons = buttons.mapToDisabledButton(), - txHistoryState = createTxHistoryState(), - marketPriceBlockState = MarketPriceBlockState.Loading(currencyName = marketPriceBlockState.currencyName), - ) - } - - private fun WalletState.ContentState.createWalletsListConfig(): WalletsListConfig { - val selectedWallet = walletsListConfig.wallets[walletsListConfig.selectedWalletIndex] - val additionalInfo = if (currentCardTypeResolverProvider().isMultiwalletAllowed()) { - selectedWallet.additionalInfo + return if (value) { + contentState.getRefreshingState() } else { - null + contentState.getRefreshedState() } + } - return walletsListConfig.copy( - wallets = walletsListConfig.wallets.toPersistentList().set( - index = walletsListConfig.selectedWalletIndex, - element = WalletCardState.Loading( - id = selectedWallet.id, - title = selectedWallet.title, - additionalInfo = additionalInfo, - imageResId = selectedWallet.imageResId, - onRenameClick = selectedWallet.onRenameClick, - onDeleteClick = selectedWallet.onDeleteClick, - ), - ), + private fun WalletState.ContentState.getRefreshingState(): WalletState { + return when (this) { + is WalletMultiCurrencyState.Content -> getRefreshingState() + is WalletSingleCurrencyState.Content -> getRefreshingState() + is WalletMultiCurrencyState.Locked, + is WalletSingleCurrencyState.Locked, + -> this + } + } + + private fun WalletState.ContentState.getRefreshedState(): WalletState { + return when (this) { + is WalletMultiCurrencyState.Content -> getRefreshedState() + is WalletSingleCurrencyState.Content -> getRefreshedState() + is WalletMultiCurrencyState.Locked, + is WalletSingleCurrencyState.Locked, + -> this + } + } + + private fun WalletMultiCurrencyState.Content.getRefreshingState(): WalletMultiCurrencyState { + return copy( + pullToRefreshConfig = updatePullToRefreshConfig(isRefreshing = true), + tokensListState = updateTokenListState(isRefreshing = true), ) } - private fun WalletState.ContentState.createPullToRefreshConfig(): WalletPullToRefreshConfig { - return pullToRefreshConfig.copy(isRefreshing = true) + private fun WalletSingleCurrencyState.Content.getRefreshingState(): WalletSingleCurrencyState { + return copy( + pullToRefreshConfig = updatePullToRefreshConfig(isRefreshing = true), + buttons = updateButtons(isRefreshing = true), + ) } - private fun WalletMultiCurrencyState.Content.createTokenListState(): WalletTokensListState { - return when (tokensListState) { + private fun WalletMultiCurrencyState.Content.getRefreshedState(): WalletMultiCurrencyState { + return copy( + pullToRefreshConfig = updatePullToRefreshConfig(isRefreshing = false), + tokensListState = updateTokenListState(isRefreshing = false), + ) + } + + private fun WalletSingleCurrencyState.Content.getRefreshedState(): WalletSingleCurrencyState { + return copy( + pullToRefreshConfig = updatePullToRefreshConfig(isRefreshing = false), + buttons = updateButtons(isRefreshing = false), + ) + } + + private fun WalletMultiCurrencyState.updateTokenListState(isRefreshing: Boolean): WalletTokensListState { + return when (val state = tokensListState) { is WalletTokensListState.Content -> { - WalletTokensListState.Loading( - items = tokensListState.items - .filterIsInstance() - .mapToLoadingTokenState(), - ) + val onOrganizeTokensClick = if (isRefreshing) null else intents::onOrganizeTokensClick + + state.copy(onOrganizeTokensClick = onOrganizeTokensClick) } - is WalletTokensListState.Empty -> WalletTokensListState.Loading() - is WalletTokensListState.Loading, is WalletTokensListState.Locked, - -> tokensListState + is WalletTokensListState.Loading, + is WalletTokensListState.Empty, + -> state } } - private fun List.mapToLoadingTokenState(): ImmutableList { - return this - .map { TokensListItemState.Token(state = TokenItemState.Loading(id = it.state.id)) } - .toImmutableList() - } + private fun WalletSingleCurrencyState.updateButtons(isRefreshing: Boolean): PersistentList { + val isButtonsEnabled = !isRefreshing - private fun ImmutableList.mapToDisabledButton(): ImmutableList { - return this - .mapNotNull { button -> + return buttons.mutate { + it.mapNotNull { button -> when (button) { - is WalletManageButton.Buy -> button.copy(enabled = false) - is WalletManageButton.Send -> button.copy(enabled = false) + is WalletManageButton.Buy -> button.copy(enabled = isButtonsEnabled) + is WalletManageButton.Send -> button.copy(enabled = isButtonsEnabled) + is WalletManageButton.Sell -> button.copy(enabled = isButtonsEnabled) is WalletManageButton.Receive -> button - is WalletManageButton.Sell -> button.copy(enabled = false) is WalletManageButton.Swap -> null } } - .toImmutableList() + } } - private fun WalletSingleCurrencyState.Content.createTxHistoryState(): TxHistoryState { - if (txHistoryState is TxHistoryState.Content) { - txHistoryState.contentItems.update { - TxHistoryState.getDefaultLoadingTransactions(onExploreClick = clickIntents::onExploreClick) - } - } - - return txHistoryState + private fun WalletState.ContentState.updatePullToRefreshConfig(isRefreshing: Boolean): WalletPullToRefreshConfig { + return pullToRefreshConfig.copy(isRefreshing = isRefreshing) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt index 6acdd08c82..18892dce3f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt @@ -15,7 +15,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyS import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig -import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletSingleCurrencyLoadedBalanceConverter.SingleCurrencyLoadedBalanceModel +import com.tangem.feature.wallet.presentation.wallet.utils.CurrencyStatusErrorConverter import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.toPersistentList import java.math.BigDecimal @@ -25,32 +25,21 @@ internal class WalletSingleCurrencyLoadedBalanceConverter( private val cardTypeResolverProvider: Provider, private val appCurrencyProvider: Provider, private val currentWalletProvider: Provider, -) : Converter { + private val currencyStatusErrorConverter: CurrencyStatusErrorConverter, +) : Converter, WalletSingleCurrencyState.Content> { - override fun convert(value: SingleCurrencyLoadedBalanceModel): WalletSingleCurrencyState.Content { - return value.cryptoCurrencyEither.fold( - ifLeft = { convertError() }, - ifRight = { convertContent(it, value.isRefreshing) }, + override fun convert(value: Either): WalletSingleCurrencyState.Content { + return value.fold( + ifLeft = currencyStatusErrorConverter::convert, + ifRight = ::convertContent, ) } - private fun convertError(): WalletSingleCurrencyState.Content { - return requireNotNull(currentStateProvider() as? WalletSingleCurrencyState.Content) - } - - private fun convertContent( - status: CryptoCurrencyStatus, - isRefreshing: Boolean, - ): WalletSingleCurrencyState.Content { + private fun convertContent(status: CryptoCurrencyStatus): WalletSingleCurrencyState.Content { val state = requireNotNull(currentStateProvider() as? WalletSingleCurrencyState.Content) val currencyName = state.marketPriceBlockState.currencyName return state.copy( walletsListConfig = getUpdatedSelectedWallet(status = status.value, state = state), - pullToRefreshConfig = if (isRefreshing) { - state.pullToRefreshConfig.copy(isRefreshing = status.value is CryptoCurrencyStatus.Loading) - } else { - state.pullToRefreshConfig - }, marketPriceBlockState = getMarketPriceState(status = status.value, currencyName = currencyName), ) } @@ -168,9 +157,4 @@ internal class WalletSingleCurrencyLoadedBalanceConverter( fiatCurrencySymbol = appCurrency.symbol, ) } - - data class SingleCurrencyLoadedBalanceModel( - val cryptoCurrencyEither: Either, - val isRefreshing: Boolean, - ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt index 15e42f3283..05b16b6424 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt @@ -14,7 +14,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.components.* import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletSkeletonStateConverter.SkeletonModel import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import com.tangem.utils.converter.Converter -import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.MutableStateFlow @@ -128,7 +128,7 @@ internal class WalletSkeletonStateConverter( return WalletPullToRefreshConfig(isRefreshing = false, onRefresh = clickIntents::onRefreshSwipe) } - private fun createButtons(): ImmutableList { + private fun createButtons(): PersistentList { return persistentListOf( WalletManageButton.Buy(enabled = false, onClick = {}), WalletManageButton.Send(enabled = false, onClick = {}), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt index 512d0d0cc0..4fe28bb627 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt @@ -22,6 +22,8 @@ import com.tangem.feature.wallet.presentation.wallet.state.components.WalletBott import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification import com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory.WalletLoadedTxHistoryConverter import com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory.WalletLoadingTxHistoryConverter +import com.tangem.feature.wallet.presentation.wallet.utils.CurrencyStatusErrorConverter +import com.tangem.feature.wallet.presentation.wallet.utils.TokenListErrorConverter import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.flow.Flow @@ -45,9 +47,16 @@ internal class WalletStateFactory( private val tokenActionsProvider by lazy { TokenActionsProvider(currentStateProvider = currentStateProvider) } private val skeletonConverter by lazy { WalletSkeletonStateConverter(currentStateProvider, clickIntents) } + private val tokenListErrorConverter by lazy { + TokenListErrorConverter(currentStateProvider) + } + private val currencyStatusErrorConverter by lazy { + CurrencyStatusErrorConverter(currentStateProvider) + } private val loadedTokensListConverter by lazy { WalletLoadedTokensListConverter( currentStateProvider = currentStateProvider, + tokenListErrorConverter = tokenListErrorConverter, cardTypeResolverProvider = currentCardTypeResolverProvider, currentWalletProvider = currentWalletProvider, appCurrencyProvider = appCurrencyProvider, @@ -76,6 +85,7 @@ internal class WalletStateFactory( cardTypeResolverProvider = currentCardTypeResolverProvider, appCurrencyProvider = appCurrencyProvider, currentWalletProvider = currentWalletProvider, + currencyStatusErrorConverter = currencyStatusErrorConverter, ) } @@ -91,8 +101,7 @@ internal class WalletStateFactory( private val refreshStateConverter by lazy { WalletRefreshStateConverter( currentStateProvider = currentStateProvider, - currentCardTypeResolverProvider = currentCardTypeResolverProvider, - clickIntents = clickIntents, + intents = clickIntents, ) } @@ -114,13 +123,12 @@ internal class WalletStateFactory( ) } - fun getStateByTokensList(tokenListEither: Either, isRefreshing: Boolean): WalletState { - return loadedTokensListConverter.convert( - value = WalletLoadedTokensListConverter.LoadedTokensListModel( - tokenListEither = tokenListEither, - isRefreshing = isRefreshing, - ), - ) + fun getStateByTokensList(maybeTokenList: Either): WalletState { + return loadedTokensListConverter.convert(maybeTokenList) + } + + fun getStateByTokenListError(error: TokenListError): WalletState { + return tokenListErrorConverter.convert(error) } fun getStateByNotifications(notifications: ImmutableList): WalletState { @@ -131,7 +139,9 @@ internal class WalletStateFactory( } } - fun getStateAfterContentRefreshing(): WalletState = refreshStateConverter.convert(Unit) + fun getRefreshingState(): WalletState = refreshStateConverter.convert(value = true) + + fun getRefreshedState(): WalletState = refreshStateConverter.convert(value = false) fun getStateWithOpenWalletBottomSheet(content: WalletBottomSheetConfig.BottomSheetContentConfig): WalletState { return when (val state = currentStateProvider() as WalletState.ContentState) { @@ -157,6 +167,7 @@ internal class WalletStateFactory( isBottomSheetShow = true, onBottomSheetDismiss = clickIntents::onDismissBottomSheet, ) + else -> state } } @@ -170,6 +181,7 @@ internal class WalletStateFactory( bottomSheetConfig = state.bottomSheetConfig?.copy(isShow = false), ) is WalletSingleCurrencyState.Locked -> state.copy(isBottomSheetShow = false) + else -> state } } @@ -199,18 +211,16 @@ internal class WalletStateFactory( fun getLockedState(): WalletState = lockedConverter.convert(Unit) fun getSingleCurrencyLoadedBalanceState( - cryptoCurrencyEither: Either, - isRefreshing: Boolean, + maybeCryptoCurrencyStatus: Either, ): WalletState { - return singleCurrencyLoadedBalanceConverter.convert( - value = WalletSingleCurrencyLoadedBalanceConverter.SingleCurrencyLoadedBalanceModel( - cryptoCurrencyEither = cryptoCurrencyEither, - isRefreshing = isRefreshing, - ), - ) + return singleCurrencyLoadedBalanceConverter.convert(maybeCryptoCurrencyStatus) } fun getSingleCurrencyManageButtonsState(actions: List): WalletState { return cryptoCurrencyActionsConverter.convert(value = actions) } + + fun getStateByCurrencyStatusError(error: CurrencyStatusError): WalletState { + return currencyStatusErrorConverter.convert(error) + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index fca76604c4..10f4dbd9e4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -1,15 +1,15 @@ package com.tangem.feature.wallet.presentation.wallet.ui import androidx.activity.compose.BackHandler -import androidx.compose.animation.* -import androidx.compose.foundation.* import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material.pullrefresh.rememberPullRefreshState -import androidx.compose.material3.* -import androidx.compose.runtime.* +import androidx.compose.material3.FabPosition +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CurrencyStatusErrorConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CurrencyStatusErrorConverter.kt new file mode 100644 index 0000000000..352f9f4739 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CurrencyStatusErrorConverter.kt @@ -0,0 +1,17 @@ +package com.tangem.feature.wallet.presentation.wallet.utils + +import com.tangem.common.Converter +import com.tangem.common.Provider +import com.tangem.domain.tokens.error.CurrencyStatusError +import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState +import com.tangem.feature.wallet.presentation.wallet.state.WalletState + +// TODO: Implement this +internal class CurrencyStatusErrorConverter( + private val currentStateProvider: Provider, +) : Converter { + + override fun convert(value: CurrencyStatusError): WalletSingleCurrencyState.Content { + return requireNotNull(currentStateProvider() as? WalletSingleCurrencyState.Content) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt index 35aa99598f..9019ff5b9a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt @@ -8,7 +8,6 @@ import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState 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.utils.TokenListToWalletStateConverter.TokensListModel import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.toPersistentList @@ -21,7 +20,7 @@ internal class TokenListToWalletStateConverter( private val appCurrencyProvider: Provider, private val isWalletContentHidden: Boolean, clickIntents: WalletClickIntents, -) : Converter { +) : Converter { private val tokenListToContentConverter = TokenListToContentItemsConverter( isWalletContentHidden = isWalletContentHidden, @@ -29,16 +28,11 @@ internal class TokenListToWalletStateConverter( clickIntents = clickIntents, ) - override fun convert(value: TokensListModel): WalletMultiCurrencyState.Content { + override fun convert(value: TokenList): WalletMultiCurrencyState.Content { val state = requireNotNull(currentStateProvider() as? WalletMultiCurrencyState.Content) return state.copy( - walletsListConfig = state.updateSelectedWallet(fiatBalance = value.tokenList.totalFiatBalance), - pullToRefreshConfig = if (value.isRefreshing) { - state.pullToRefreshConfig.copy(isRefreshing = getRefreshingStatus(tokenList = value.tokenList)) - } else { - state.pullToRefreshConfig - }, - tokensListState = tokenListToContentConverter.convert(value = value.tokenList), + walletsListConfig = state.updateSelectedWallet(fiatBalance = value.totalFiatBalance), + tokensListState = tokenListToContentConverter.convert(value = value), ) } @@ -58,10 +52,4 @@ internal class TokenListToWalletStateConverter( .set(index = selectedWalletIndex, element = converter.convert(fiatBalance)), ) } - - private fun getRefreshingStatus(tokenList: TokenList): Boolean { - return tokenList.totalFiatBalance is TokenList.FiatBalance.Loading - } - - data class TokensListModel(val tokenList: TokenList, val isRefreshing: Boolean) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index c02df958f3..1f2483b904 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -21,9 +21,7 @@ import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.settings.CanUseBiometryUseCase import com.tangem.domain.settings.IsUserAlreadyRateAppUseCase import com.tangem.domain.settings.ShouldShowSaveWalletScreenUseCase -import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase -import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase -import com.tangem.domain.tokens.GetTokenListUseCase +import com.tangem.domain.tokens.* import com.tangem.domain.tokens.legacy.TradeCryptoAction import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenList @@ -73,7 +71,9 @@ internal class WalletViewModel @Inject constructor( private val setAccessCodeRequestPolicyUseCase: SetAccessCodeRequestPolicyUseCase, private val getAccessCodeSavingStatusUseCase: GetAccessCodeSavingStatusUseCase, private val getTokenListUseCase: GetTokenListUseCase, - private val getPrimaryCurrencyUseCase: GetPrimaryCurrencyStatusUpdatesUseCase, + private val fetchTokenListUseCase: FetchTokenListUseCase, + private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase, + private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase, private val getCardWasScannedUseCase: GetCardWasScannedUseCase, private val isUserAlreadyRateAppUseCase: IsUserAlreadyRateAppUseCase, private val isDemoCardUseCase: IsDemoCardUseCase, @@ -127,6 +127,7 @@ internal class WalletViewModel @Inject constructor( private val marketPriceJobHolder = JobHolder() private val buttonsJobHolder = JobHolder() private val notificationsJobHolder = JobHolder() + private val refreshContentJobHolder = JobHolder() override fun onCreate(owner: LifecycleOwner) { viewModelScope.launch(dispatchers.main) { @@ -151,6 +152,10 @@ internal class WalletViewModel @Inject constructor( wallets = sourceList val currentState = uiState + val previousSelectedWalletIndex = (currentState as? WalletState.ContentState) + ?.walletsListConfig + ?.selectedWalletIndex + val selectedWalletIndex = if (currentState is WalletLockedState) { currentState.getSelectedWalletIndex() } else { @@ -161,141 +166,13 @@ internal class WalletViewModel @Inject constructor( sourceList.indexOfFirst { it.walletId == selectedWallet.walletId } } - uiState = stateFactory.getSkeletonState(wallets = sourceList, selectedWalletIndex = selectedWalletIndex) + if (previousSelectedWalletIndex != selectedWalletIndex) { + uiState = stateFactory.getSkeletonState(wallets = sourceList, selectedWalletIndex = selectedWalletIndex) - updateContentItems(index = selectedWalletIndex) - } - - private fun updateContentItems(index: Int, isRefreshing: Boolean = false) { - val cardTypeResolver = getCardTypeResolver(index) - when { - getWallet(index).isLocked -> uiState = stateFactory.getLockedState() - cardTypeResolver.isMultiwalletAllowed() -> updateMultiCurrencyContent(index, isRefreshing) - !cardTypeResolver.isMultiwalletAllowed() -> updateSingleCurrencyContent(index, isRefreshing) + getContentItemsUpdates(index = selectedWalletIndex) } } - private fun updateMultiCurrencyContent(index: Int, isRefreshing: Boolean = false) { - val state = requireNotNull(uiState as? WalletMultiCurrencyState) { - "Impossible to update tokens list if state isn't WalletMultiCurrencyState" - } - - getTokenListUseCase(userWalletId = state.walletsListConfig.wallets[index].id) - .distinctUntilChanged() - .onEach { tokenListEither -> - uiState = stateFactory.getStateByTokensList( - tokenListEither = tokenListEither, - isRefreshing = isRefreshing, - ) - - updateNotifications( - index = index, - tokenList = tokenListEither.fold(ifLeft = { null }, ifRight = { it }), - ) - } - .flowOn(dispatchers.io) - .launchIn(viewModelScope) - .saveIn(tokensJobHolder) - } - - private fun updateSingleCurrencyContent(index: Int, isRefreshing: Boolean) { - val wallet = getWallet(index) - val blockchain = getCardTypeResolver(index).getBlockchain() - updateTxHistory( - blockchain = blockchain, - derivationStyle = wallet.scanResponse.derivationStyleProvider.getDerivationStyle(), - ) - updateMarketPrice(userWalletId = wallet.walletId, isRefreshing = isRefreshing) - updateNotifications(index) - } - - private fun updateTxHistory(blockchain: Blockchain, derivationStyle: DerivationStyle?) { - viewModelScope.launch(dispatchers.io) { - val derivationPath = blockchain.derivationPath(style = derivationStyle)?.rawPath - - val txHistoryItemsCountEither = txHistoryItemsCountUseCase( - networkId = Network.ID(blockchain.id), - derivationPath = derivationPath, - ) - - uiState = stateFactory.getLoadingTxHistoryState(itemsCountEither = txHistoryItemsCountEither) - - txHistoryItemsCountEither.onRight { - uiState = stateFactory.getLoadedTxHistoryState( - txHistoryEither = txHistoryItemsUseCase( - networkId = Network.ID(blockchain.id), - derivationPath = derivationPath, - ).map { - it.cachedIn(viewModelScope) - }, - ) - } - } - } - - // It also update wallet balance - private fun updateMarketPrice(userWalletId: UserWalletId, isRefreshing: Boolean) { - getPrimaryCurrencyUseCase(userWalletId = userWalletId) - .distinctUntilChanged() - .onEach { either -> - uiState = stateFactory.getSingleCurrencyLoadedBalanceState( - cryptoCurrencyEither = either, - isRefreshing = isRefreshing, - ) - - either.onRight { status -> - cryptoCurrencyStatus = status - updateButtons(userWalletId = userWalletId, currency = status.currency) - } - } - .flowOn(dispatchers.io) - .launchIn(viewModelScope) - .saveIn(marketPriceJobHolder) - } - - private fun updateButtons(userWalletId: UserWalletId, currency: CryptoCurrency) { - getCryptoCurrencyActionsUseCase(userWalletId = userWalletId, cryptoCurrency = currency) - .distinctUntilChanged() - .onEach { uiState = stateFactory.getSingleCurrencyManageButtonsState(actions = it.states) } - .flowOn(dispatchers.io) - .launchIn(viewModelScope) - .saveIn(buttonsJobHolder) - } - - private fun updateNotifications(index: Int, tokenList: TokenList? = null) { - notificationsListFactory.create( - cardTypesResolver = getCardTypeResolver(index = index), - tokenList = tokenList, - ) - .distinctUntilChanged() - .onEach { uiState = stateFactory.getStateByNotifications(notifications = it) } - .flowOn(dispatchers.io) - .launchIn(viewModelScope) - .saveIn(notificationsJobHolder) - } - - override fun onStop(owner: LifecycleOwner) { - viewModelScope.launch(dispatchers.io) { - saveSelectedWallet() - } - } - - private suspend fun saveSelectedWallet() { - val state = uiState - if (state is WalletState.ContentState) { - selectWalletUseCase(getWallet(index = state.walletsListConfig.selectedWalletIndex).walletId) - } - } - - private fun getWallet(index: Int): UserWallet { - return requireNotNull( - value = wallets.getOrNull(index), - lazyMessage = { "WalletsList doesn't contain element with index = $index" }, - ) - } - - private fun getCardTypeResolver(index: Int): CardTypesResolver = getWallet(index).scanResponse.cardTypesResolver - override fun onBackClick() { viewModelScope.launch(dispatchers.main) { router.popBackStack(screen = if (shouldSaveUserWalletsUseCase()) AppScreen.Welcome else AppScreen.Home) @@ -373,14 +250,9 @@ internal class WalletViewModel @Inject constructor( if (state.walletsListConfig.selectedWalletIndex == index) return - /* - * When wallet is changed it's necessary to stop the last jobs. - * If jobs aren't stopped and wallet is changed then it will update state for the prev wallet. - */ - tokensJobHolder.update(job = null) - marketPriceJobHolder.update(job = null) - buttonsJobHolder.update(job = null) - notificationsJobHolder.update(job = null) + viewModelScope.launch(dispatchers.io) { + selectWalletUseCase(getWallet(index = index).walletId) + } val cacheState = WalletStateCache.getState(userWalletId = state.walletsListConfig.wallets[index].id) if (cacheState != null) { @@ -393,50 +265,28 @@ internal class WalletViewModel @Inject constructor( cacheState } - if (cacheState.isLoadingState()) updateContentItems(index) + if (cacheState.isLoadingState()) { + getContentItemsUpdates(index) + } } else { uiState = stateFactory.getSkeletonState(wallets = wallets, selectedWalletIndex = index) - updateContentItems(index = index) - } - } - - private fun WalletState.isLoadingState(): Boolean { - // Check the base components - if (this is WalletState.ContentState) { - walletsListConfig.wallets[walletsListConfig.selectedWalletIndex] is WalletCardState.Loading || - notifications.isEmpty() - } - - // Check the special components - return when (this) { - is WalletMultiCurrencyState -> { - val hasLoadingTokens = tokensListState is WalletTokensListState.ContentState && - (tokensListState as WalletTokensListState.ContentState).items - .filterIsInstance() - .any { it.state is TokenItemState.Loading } - - tokensListState is WalletTokensListState.Loading || hasLoadingTokens - } - is WalletSingleCurrencyState -> { - this is WalletSingleCurrencyState.Content && marketPriceBlockState is MarketPriceBlockState.Loading - } - is WalletState.Initial -> false + getContentItemsUpdates(index = index) } } override fun onRefreshSwipe() { - if (uiState is WalletState.Initial || uiState is WalletLockedState) return + val selectedWalletIndex = (uiState as? WalletState.ContentState) + ?.walletsListConfig + ?.selectedWalletIndex + ?: return - viewModelScope.launch(dispatchers.io) { - uiState = stateFactory.getStateAfterContentRefreshing() - - // TODO: [REDACTED_JIRA] - delay(timeMillis = 500) - - updateContentItems( - index = requireNotNull(uiState as? WalletState.ContentState).walletsListConfig.selectedWalletIndex, - isRefreshing = true, - ) + when (uiState) { + is WalletMultiCurrencyState.Content -> refreshMultiCurrencyContent(selectedWalletIndex) + is WalletSingleCurrencyState.Content -> refreshSingleCurrencyContent(selectedWalletIndex) + is WalletState.Initial, + is WalletMultiCurrencyState.Locked, + is WalletSingleCurrencyState.Locked, + -> Unit } } @@ -486,11 +336,12 @@ internal class WalletViewModel @Inject constructor( } override fun onReloadClick() { - uiState = stateFactory.getStateAfterContentRefreshing() - updateSingleCurrencyContent( - index = requireNotNull(uiState as? WalletState.ContentState).walletsListConfig.selectedWalletIndex, - isRefreshing = true, - ) + val selectedWalletIndex = (uiState as? WalletSingleCurrencyState) + ?.walletsListConfig + ?.selectedWalletIndex + ?: return + + refreshSingleCurrencyContent(selectedWalletIndex) } override fun onExploreClick() { @@ -553,18 +404,6 @@ internal class WalletViewModel @Inject constructor( } } - private fun createSelectedAppCurrencyFlow(): StateFlow { - return getSelectedAppCurrencyUseCase() - .map { maybeAppCurrency -> - maybeAppCurrency.getOrElse { AppCurrency.Default } - } - .stateIn( - scope = viewModelScope, - started = SharingStarted.Eagerly, - initialValue = AppCurrency.Default, - ) - } - override fun onDismissBottomSheet() { uiState = stateFactory.getStateWithClosedBottomSheet() } @@ -578,4 +417,188 @@ internal class WalletViewModel @Inject constructor( ) } } + + private fun getContentItemsUpdates(index: Int) { + /* + * When wallet is changed it's necessary to stop the last jobs. + * If jobs aren't stopped and wallet is changed then it will update state for the prev wallet. + */ + tokensJobHolder.update(job = null) + marketPriceJobHolder.update(job = null) + buttonsJobHolder.update(job = null) + notificationsJobHolder.update(job = null) + refreshContentJobHolder.update(job = null) + + val wallet = getWallet(index) + + when { + wallet.isLocked -> { + uiState = stateFactory.getLockedState() + } + wallet.isMultiCurrency -> getMultiCurrencyContent(index) + !wallet.isMultiCurrency -> getSingleCurrencyContent(index) + } + } + + private fun getMultiCurrencyContent(walletIndex: Int) { + val state = requireNotNull(uiState as? WalletMultiCurrencyState) { + "Impossible to get a token list updates if state isn't WalletMultiCurrencyState" + } + + getTokenListUseCase(userWalletId = state.walletsListConfig.wallets[walletIndex].id) + .distinctUntilChanged() + .onEach { maybeTokenList -> + uiState = stateFactory.getStateByTokensList(maybeTokenList) + + updateNotifications( + index = walletIndex, + tokenList = maybeTokenList.fold(ifLeft = { null }, ifRight = { it }), + ) + } + .flowOn(dispatchers.io) + .launchIn(viewModelScope) + .saveIn(tokensJobHolder) + } + + private fun getSingleCurrencyContent(index: Int) { + val wallet = getWallet(index) + val blockchain = getCardTypeResolver(index).getBlockchain() + updateTxHistory( + blockchain = blockchain, + derivationStyle = wallet.scanResponse.derivationStyleProvider.getDerivationStyle(), + ) + updateMarketPrice(userWalletId = wallet.walletId) + updateNotifications(index) + } + + private fun updateTxHistory(blockchain: Blockchain, derivationStyle: DerivationStyle?) { + viewModelScope.launch(dispatchers.io) { + val derivationPath = blockchain.derivationPath(style = derivationStyle)?.rawPath + + val txHistoryItemsCountEither = txHistoryItemsCountUseCase( + networkId = Network.ID(blockchain.id), + derivationPath = derivationPath, + ) + + uiState = stateFactory.getLoadingTxHistoryState(itemsCountEither = txHistoryItemsCountEither) + + txHistoryItemsCountEither.onRight { + uiState = stateFactory.getLoadedTxHistoryState( + txHistoryEither = txHistoryItemsUseCase( + networkId = Network.ID(blockchain.id), + derivationPath = derivationPath, + ).map { + it.cachedIn(viewModelScope) + }, + ) + } + } + } + + // It also update wallet balance + private fun updateMarketPrice(userWalletId: UserWalletId) { + getPrimaryCurrencyStatusUpdatesUseCase(userWalletId = userWalletId) + .distinctUntilChanged() + .onEach { maybeCryptoCurrencyStatus -> + uiState = stateFactory.getSingleCurrencyLoadedBalanceState(maybeCryptoCurrencyStatus) + + maybeCryptoCurrencyStatus.onRight { status -> + cryptoCurrencyStatus = status + updateButtons(userWalletId = userWalletId, currency = status.currency) + } + } + .flowOn(dispatchers.io) + .launchIn(viewModelScope) + .saveIn(marketPriceJobHolder) + } + + private fun updateButtons(userWalletId: UserWalletId, currency: CryptoCurrency) { + getCryptoCurrencyActionsUseCase(userWalletId = userWalletId, cryptoCurrency = currency) + .distinctUntilChanged() + .onEach { uiState = stateFactory.getSingleCurrencyManageButtonsState(actions = it.states) } + .flowOn(dispatchers.io) + .launchIn(viewModelScope) + .saveIn(buttonsJobHolder) + } + + private fun updateNotifications(index: Int, tokenList: TokenList? = null) { + notificationsListFactory.create( + cardTypesResolver = getCardTypeResolver(index = index), + tokenList = tokenList, + ) + .distinctUntilChanged() + .onEach { uiState = stateFactory.getStateByNotifications(notifications = it) } + .flowOn(dispatchers.io) + .launchIn(viewModelScope) + .saveIn(notificationsJobHolder) + } + + private fun refreshMultiCurrencyContent(walletIndex: Int) { + uiState = stateFactory.getRefreshingState() + val wallet = getWallet(walletIndex) + + viewModelScope.launch(dispatchers.io) { + val result = fetchTokenListUseCase(wallet.walletId, refresh = true) + + uiState = stateFactory.getRefreshedState() + uiState = result.fold(stateFactory::getStateByTokenListError) { uiState } + }.saveIn(refreshContentJobHolder) + } + + private fun refreshSingleCurrencyContent(walletIndex: Int) { + uiState = stateFactory.getRefreshingState() + val wallet = getWallet(walletIndex) + + viewModelScope.launch(dispatchers.io) { + val result = fetchCurrencyStatusUseCase(wallet.walletId, refresh = true) + + uiState = stateFactory.getRefreshedState() + uiState = result.fold(stateFactory::getStateByCurrencyStatusError) { uiState } + }.saveIn(refreshContentJobHolder) + } + + private fun createSelectedAppCurrencyFlow(): StateFlow { + return getSelectedAppCurrencyUseCase() + .map { maybeAppCurrency -> + maybeAppCurrency.getOrElse { AppCurrency.Default } + } + .stateIn( + scope = viewModelScope, + started = SharingStarted.Eagerly, + initialValue = AppCurrency.Default, + ) + } + + private fun WalletState.isLoadingState(): Boolean { + // Check the base components + if (this is WalletState.ContentState) { + walletsListConfig.wallets[walletsListConfig.selectedWalletIndex] is WalletCardState.Loading || + notifications.isEmpty() + } + + // Check the special components + return when (this) { + is WalletMultiCurrencyState -> { + val hasLoadingTokens = tokensListState is WalletTokensListState.ContentState && + (tokensListState as WalletTokensListState.ContentState).items + .filterIsInstance() + .any { it.state is TokenItemState.Loading } + + tokensListState is WalletTokensListState.Loading || hasLoadingTokens + } + is WalletSingleCurrencyState -> { + this is WalletSingleCurrencyState.Content && marketPriceBlockState is MarketPriceBlockState.Loading + } + is WalletState.Initial -> false + } + } + + private fun getWallet(index: Int): UserWallet { + return requireNotNull( + value = wallets.getOrNull(index), + lazyMessage = { "WalletsList doesn't contain element with index = $index" }, + ) + } + + private fun getCardTypeResolver(index: Int): CardTypesResolver = getWallet(index).scanResponse.cardTypesResolver } \ No newline at end of file