From 7e992b986b168877a0d63753c2ed76a2b68cca84 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 9 Aug 2023 17:49:23 +0800 Subject: [PATCH] Updated on 2026-08-14 --- .../wallet/state/WalletLoading.kt | 37 ------ .../wallet/state/WalletMultiCurrencyState.kt | 2 +- .../wallet/state/WalletSingleCurrencyState.kt | 2 +- .../presentation/wallet/state/WalletState.kt | 41 ++++++ .../wallet/state/WalletStateHolder.kt | 103 --------------- .../WalletLoadedTokensListConverter.kt | 16 ++- .../factory/WalletSkeletonStateConverter.kt | 117 ++++++++--------- .../state/factory/WalletStateFactory.kt | 111 ++++++++++------ .../WalletLoadedTxHistoryConverter.kt | 51 ++------ .../WalletLoadingTxHistoryConverter.kt | 69 ++-------- .../presentation/wallet/ui/WalletScreen.kt | 24 ++-- .../ui/components/common/WalletContent.kt | 7 +- .../components/common/WalletNotifications.kt | 8 +- .../wallet/utils/TokenListErrorConverter.kt | 21 +++ .../TokenListErrorToWalletStateConverter.kt | 28 ---- .../utils/TokenListToWalletStateConverter.kt | 46 +++---- .../wallet/viewmodels/WalletClickIntents.kt | 2 + .../WalletNotificationsListFactory.kt | 4 +- .../wallet/viewmodels/WalletViewModel.kt | 120 +++++++++++++----- 19 files changed, 349 insertions(+), 460 deletions(-) delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLoading.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletState.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorToWalletStateConverter.kt diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLoading.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLoading.kt deleted file mode 100644 index 55e211840f..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLoading.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state - -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.wallet.state.components.* -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf - -/** - * Loading wallet state - * - * @property onBackClick Lambda be invoked when back button is clicked - * -[REDACTED_AUTHOR] - */ -internal data class WalletLoading(override val onBackClick: () -> Unit) : WalletStateHolder() { - - override val topBarConfig = WalletTopBarConfig(onScanCardClick = {}, onMoreClick = {}) - - override val walletsListConfig = WalletsListConfig( - selectedWalletIndex = 0, - wallets = persistentListOf( - WalletCardState.Loading( - id = UserWalletId(stringValue = ""), - title = "", - additionalInfo = "", - imageResId = null, - ), - ), - onWalletChange = {}, - ) - - override val pullToRefreshConfig = WalletPullToRefreshConfig(isRefreshing = false, onRefresh = {}) - - override val notifications: ImmutableList = persistentListOf() - - override val bottomSheetConfig = null -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt index 0b93c6033d..87d675cd95 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt @@ -9,7 +9,7 @@ import kotlinx.collections.immutable.persistentListOf * [REDACTED_AUTHOR] */ -internal sealed class WalletMultiCurrencyState : WalletStateHolder() { +internal sealed class WalletMultiCurrencyState : WalletState.ContentState() { /** Tokens list state */ abstract val tokensListState: WalletTokensListState 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 f29b229afd..616d8d5f0b 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 @@ -10,7 +10,7 @@ import kotlinx.collections.immutable.persistentListOf * [REDACTED_AUTHOR] */ -internal sealed class WalletSingleCurrencyState : WalletStateHolder() { +internal sealed class WalletSingleCurrencyState : WalletState.ContentState() { /** Manage buttons */ abstract val buttons: ImmutableList diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletState.kt new file mode 100644 index 0000000000..570accc073 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletState.kt @@ -0,0 +1,41 @@ +package com.tangem.feature.wallet.presentation.wallet.state + +import com.tangem.feature.wallet.presentation.wallet.state.components.* +import kotlinx.collections.immutable.ImmutableList + +/** + * Wallet screen state + * +[REDACTED_AUTHOR] + */ +internal sealed class WalletState { + + /** Lambda be invoked when back button is clicked */ + abstract val onBackClick: () -> Unit + + /** Wallet screen content state */ + sealed class ContentState : WalletState() { + + /** Top bar config */ + abstract val topBarConfig: WalletTopBarConfig + + /** Wallets list config */ + abstract val walletsListConfig: WalletsListConfig + + /** Pull to refresh config */ + abstract val pullToRefreshConfig: WalletPullToRefreshConfig + + /** Notifications */ + abstract val notifications: ImmutableList + + /** Bottom sheet config */ + abstract val bottomSheetConfig: WalletBottomSheetConfig? + } + + /** + * Initial state + * + * @property onBackClick lambda be invoked when back button is clicked + */ + data class Initial(override val onBackClick: () -> Unit) : WalletState() +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt deleted file mode 100644 index 25338db85c..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt +++ /dev/null @@ -1,103 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state - -import com.tangem.feature.wallet.presentation.wallet.state.components.* -import kotlinx.collections.immutable.ImmutableList - -/** - * Wallet screen state holder - * -[REDACTED_AUTHOR] - */ -internal sealed class WalletStateHolder { - - /** Lambda be invoked when back button is clicked */ - abstract val onBackClick: () -> Unit - - /** Top bar config */ - abstract val topBarConfig: WalletTopBarConfig - - /** Wallets list config */ - abstract val walletsListConfig: WalletsListConfig - - /** Pull to refresh config */ - abstract val pullToRefreshConfig: WalletPullToRefreshConfig - - /** Notifications */ - abstract val notifications: ImmutableList - - /** Bottom sheet config */ - abstract val bottomSheetConfig: WalletBottomSheetConfig? - - fun copySealed( - onBackClick: () -> Unit = this.onBackClick, - topBarConfig: WalletTopBarConfig = this.topBarConfig, - walletsListConfig: WalletsListConfig = this.walletsListConfig, - pullToRefreshConfig: WalletPullToRefreshConfig = this.pullToRefreshConfig, - notifications: ImmutableList = this.notifications, - bottomSheet: WalletBottomSheetConfig? = this.bottomSheetConfig, - ): WalletStateHolder { - return when (this) { - is WalletLoading -> { - copy(onBackClick = onBackClick) - } - is WalletMultiCurrencyState.Content -> { - copy( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - notifications = notifications, - bottomSheetConfig = bottomSheet, - ) - } - is WalletMultiCurrencyState.Locked -> { - if (bottomSheet != null) { - copy( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - isBottomSheetShow = bottomSheet.isShow, - onBottomSheetDismiss = bottomSheet.onDismissRequest, - ) - } else { - copy( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - ) - } - } - is WalletSingleCurrencyState.Content -> { - copy( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - notifications = notifications, - bottomSheetConfig = bottomSheet, - ) - } - is WalletSingleCurrencyState.Locked -> { - if (bottomSheet != null) { - copy( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - isBottomSheetShow = bottomSheet.isShow, - onBottomSheetDismiss = bottomSheet.onDismissRequest, - ) - } else { - copy( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - ) - } - } - } - } -} \ 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 9ffc95c838..6b4f617a1b 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 @@ -5,28 +5,30 @@ import com.tangem.common.Provider import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.model.TokenList -import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder +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.TokenListErrorToWalletStateConverter +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 import com.tangem.utils.converter.Converter /** - * Converter from loaded [TokenListError] or [TokenList] to [WalletStateHolder] + * Converter from loaded [TokenListError] or [TokenList] to [WalletMultiCurrencyState] * * @property currentStateProvider current ui state provider * @param cardTypeResolverProvider card type resolver + * @param isLockedWalletProvider current wallet is locked or not provider * @param clickIntents screen click intents * [REDACTED_AUTHOR] */ internal class WalletLoadedTokensListConverter( - private val currentStateProvider: Provider, + private val currentStateProvider: Provider, cardTypeResolverProvider: Provider, isLockedWalletProvider: Provider, clickIntents: WalletClickIntents, -) : Converter { +) : Converter { private val tokenListStateConverter = TokenListToWalletStateConverter( currentStateProvider = currentStateProvider, @@ -38,11 +40,11 @@ internal class WalletLoadedTokensListConverter( clickIntents = clickIntents, ) - private val tokenListErrorStateConverter = TokenListErrorToWalletStateConverter( + private val tokenListErrorStateConverter = TokenListErrorConverter( currentStateProvider = currentStateProvider, ) - override fun convert(value: LoadedTokensListModel): WalletStateHolder { + override fun convert(value: LoadedTokensListModel): WalletMultiCurrencyState.Content { return value.tokenListEither.fold( ifLeft = tokenListErrorStateConverter::convert, ifRight = { 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 9289845a10..6c86a81215 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 @@ -1,15 +1,14 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory -import androidx.paging.PagingData +import com.tangem.common.Provider import com.tangem.core.ui.components.marketprice.MarketPriceBlockState -import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver 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.WalletStateHolder +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.factory.WalletSkeletonStateConverter.SkeletonModel import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents @@ -17,62 +16,43 @@ import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList -import kotlinx.coroutines.flow.flow /** - * Converter from loaded list of [UserWallet] to skeleton state of screen [WalletStateHolder] + * Converter from loaded list of [UserWallet] to skeleton state of screen [WalletState.ContentState] * - * @property clickIntents screen click intents + * @property currentStateProvider current ui state provider + * @property clickIntents screen click intents * [REDACTED_AUTHOR] */ internal class WalletSkeletonStateConverter( + private val currentStateProvider: Provider, private val clickIntents: WalletClickIntents, -) : Converter { +) : Converter { - override fun convert(value: SkeletonModel): WalletStateHolder { - val wallet = requireNotNull(value.wallets.getOrNull(value.selectedWalletIndex)) { "Empty wallet list" } - val cardTypeResolver = wallet.scanResponse.cardTypesResolver + override fun convert(value: SkeletonModel): WalletState.ContentState { + val cardTypeResolver = value.wallets[value.selectedWalletIndex].scanResponse.cardTypesResolver - return when { - cardTypeResolver.isMultiwalletAllowed() -> createMultiCurrencyState(value) - !cardTypeResolver.isMultiwalletAllowed() -> createSingleCurrencyState(value, cardTypeResolver) - else -> error("Illegal wallet state: $wallet") + return if (cardTypeResolver.isMultiwalletAllowed()) { + createMultiCurrencyState(value = value) + } else { + createSingleCurrencyState(value = value, currencyName = cardTypeResolver.getBlockchain().currency) } } - /** - * Create [WalletMultiCurrencyState.Content]. - * Tokens and notifications are updated asynchronously. - * - * @param value converted value - */ - private fun createMultiCurrencyState(value: SkeletonModel): WalletMultiCurrencyState.Content { + private fun createMultiCurrencyState(value: SkeletonModel): WalletMultiCurrencyState { return WalletMultiCurrencyState.Content( onBackClick = clickIntents::onBackClick, topBarConfig = createTopBarConfig(), walletsListConfig = createWalletsListConfig(value), pullToRefreshConfig = createPullToRefreshConfig(), - tokensListState = WalletTokensListState.Content( - items = persistentListOf(), - onOrganizeTokensClick = null, - ), + tokensListState = WalletTokensListState.Loading, notifications = persistentListOf(), bottomSheetConfig = null, ) } - /** - * Create [WalletSingleCurrencyState.Content]. - * Transactions, notifications and market price are updated asynchronously. - * - * @param value converted value - * @param cardTypeResolver card type resolver - */ - private fun createSingleCurrencyState( - value: SkeletonModel, - cardTypeResolver: CardTypesResolver, - ): WalletSingleCurrencyState.Content { + private fun createSingleCurrencyState(value: SkeletonModel, currencyName: String): WalletSingleCurrencyState { return WalletSingleCurrencyState.Content( onBackClick = clickIntents::onBackClick, topBarConfig = createTopBarConfig(), @@ -81,12 +61,8 @@ internal class WalletSkeletonStateConverter( notifications = persistentListOf(), bottomSheetConfig = null, buttons = getButtons(), - marketPriceBlockState = MarketPriceBlockState.Loading( - currencyName = cardTypeResolver.getBlockchain().currency, - ), - txHistoryState = WalletTxHistoryState.Content( - items = flow { PagingData.empty() }, - ), + marketPriceBlockState = MarketPriceBlockState.Loading(currencyName = currencyName), + txHistoryState = WalletTxHistoryState.Loading(onExploreClick = clickIntents::onExploreClick), ) } @@ -100,22 +76,43 @@ internal class WalletSkeletonStateConverter( private fun createWalletsListConfig(value: SkeletonModel): WalletsListConfig { return WalletsListConfig( selectedWalletIndex = value.selectedWalletIndex, - wallets = value.wallets.map { wallet -> - val cardTypeResolver = wallet.scanResponse.cardTypesResolver - WalletCardState.Loading( - id = wallet.walletId, - title = wallet.name, - additionalInfo = WalletAdditionalInfoFactory.resolve( - cardTypesResolver = cardTypeResolver, - isLocked = wallet.isLocked, - ), - imageResId = WalletImageResolver.resolve(cardTypesResolver = cardTypeResolver), - ) - }.toImmutableList(), + wallets = value.wallets.map(::createWalletState).toImmutableList(), onWalletChange = clickIntents::onWalletChange, ) } + private fun createWalletState(wallet: UserWallet): WalletCardState { + val state = currentStateProvider() + + // If it isn't first initialization (example, when user unlocks wallet) + return if (state is WalletState.ContentState) { + val initializedWallet = state.walletsListConfig.wallets.first { it.id == wallet.walletId } + + // If wallet is initialized, return it, otherwise return loading state + if (initializedWallet !is WalletCardState.Loading) { + initializedWallet + } else { + createWalletLoadingState(wallet) + } + } else { + createWalletLoadingState(wallet) + } + } + + private fun createWalletLoadingState(wallet: UserWallet): WalletCardState { + val cardTypeResolver = wallet.scanResponse.cardTypesResolver + + return WalletCardState.Loading( + id = wallet.walletId, + title = wallet.name, + additionalInfo = WalletAdditionalInfoFactory.resolve( + cardTypesResolver = cardTypeResolver, + isLocked = wallet.isLocked, + ), + imageResId = WalletImageResolver.resolve(cardTypesResolver = cardTypeResolver), + ) + } + private fun createPullToRefreshConfig(): WalletPullToRefreshConfig { return WalletPullToRefreshConfig(isRefreshing = false, onRefresh = clickIntents::onRefreshSwipe) } @@ -123,16 +120,14 @@ internal class WalletSkeletonStateConverter( // TODO: [REDACTED_JIRA] private fun getButtons(): ImmutableList { return persistentListOf( - WalletManageButton.Buy(onClick = {}), - WalletManageButton.Send(onClick = {}), + WalletManageButton.Buy(), + WalletManageButton.Send(), WalletManageButton.Receive(onClick = {}), - WalletManageButton.Exchange(onClick = {}), + WalletManageButton.Exchange(), + WalletManageButton.Sell(), WalletManageButton.CopyAddress(onClick = {}), ) } - data class SkeletonModel( - val wallets: List, - val selectedWalletIndex: Int, - ) + data class SkeletonModel(val wallets: List, val selectedWalletIndex: Int) } \ No newline at end of file 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 4d31745689..0f4d3bd755 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 @@ -10,14 +10,12 @@ import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.txhistory.models.TxHistoryListError import com.tangem.domain.txhistory.models.TxHistoryStateError import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.state.WalletLoading 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.WalletStateHolder +import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletBottomSheetConfig import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification -import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletLoadedTokensListConverter.LoadedTokensListModel 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.viewmodels.WalletClickIntents @@ -26,7 +24,7 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.Flow /** - * Main factory for creating [WalletStateHolder] + * Main factory for creating [WalletState] * * @property currentStateProvider current ui state provider * @property currentCardTypeResolverProvider current card type resolver @@ -34,13 +32,13 @@ import kotlinx.coroutines.flow.Flow * @property clickIntents screen click intents */ internal class WalletStateFactory( - private val currentStateProvider: Provider, + private val currentStateProvider: Provider, private val currentCardTypeResolverProvider: Provider, private val isLockedWalletProvider: Provider, private val clickIntents: WalletClickIntents, ) { - private val skeletonConverter by lazy { WalletSkeletonStateConverter(clickIntents = clickIntents) } + private val skeletonConverter by lazy { WalletSkeletonStateConverter(currentStateProvider, clickIntents) } private val loadedTokensListConverter by lazy { WalletLoadedTokensListConverter( @@ -54,7 +52,6 @@ internal class WalletStateFactory( private val loadingTransactionsStateConverter by lazy { WalletLoadingTxHistoryConverter( currentStateProvider = currentStateProvider, - currentCardTypeResolverProvider = currentCardTypeResolverProvider, clickIntents = clickIntents, ) } @@ -67,70 +64,97 @@ internal class WalletStateFactory( ) } - fun getInitialState(): WalletStateHolder = WalletLoading(onBackClick = clickIntents::onBackClick) + fun getInitialState(): WalletState = WalletState.Initial(onBackClick = clickIntents::onBackClick) - fun getSkeletonState(wallets: List, index: Int): WalletStateHolder { + fun getSkeletonState(wallets: List, selectedWalletIndex: Int): WalletState { return skeletonConverter.convert( - value = WalletSkeletonStateConverter.SkeletonModel(wallets = wallets, selectedWalletIndex = index), + value = WalletSkeletonStateConverter.SkeletonModel( + wallets = wallets, + selectedWalletIndex = selectedWalletIndex, + ), ) } - fun getStateByTokensList( - tokenListEither: Either, - isRefreshing: Boolean, - ): WalletStateHolder { + fun getStateByTokensList(tokenListEither: Either, isRefreshing: Boolean): WalletState { return loadedTokensListConverter.convert( - value = LoadedTokensListModel(tokenListEither = tokenListEither, isRefreshing = isRefreshing), + value = WalletLoadedTokensListConverter.LoadedTokensListModel( + tokenListEither = tokenListEither, + isRefreshing = isRefreshing, + ), ) } - fun getStateByNotifications(notifications: ImmutableList): WalletStateHolder { - return currentStateProvider().copySealed(notifications = notifications) - } - - fun getStateAfterWalletChanging(index: Int): WalletStateHolder { - return currentStateProvider().let { stateHolder -> - stateHolder.copySealed(walletsListConfig = stateHolder.walletsListConfig.copy(selectedWalletIndex = index)) + fun getStateByNotifications(notifications: ImmutableList): WalletState { + return when (val state = currentStateProvider()) { + is WalletMultiCurrencyState.Content -> state.copy(notifications = notifications) + is WalletSingleCurrencyState.Content -> state.copy(notifications = notifications) + else -> state } } - fun getStateAfterContentRefreshing(): WalletStateHolder { - return currentStateProvider().let { state -> - state.copySealed(pullToRefreshConfig = state.pullToRefreshConfig.copy(isRefreshing = true)) - } + fun getStateAfterContentRefreshing(): WalletState { + return currentStateProvider() } - fun getStateWithOpenBottomSheet(content: WalletBottomSheetConfig.BottomSheetContentConfig): WalletStateHolder { - return currentStateProvider().let { state -> - state.copySealed( - bottomSheet = WalletBottomSheetConfig( + fun getStateWithOpenBottomSheet(content: WalletBottomSheetConfig.BottomSheetContentConfig): WalletState { + return when (val state = currentStateProvider() as WalletState.ContentState) { + is WalletMultiCurrencyState.Content -> state.copy( + bottomSheetConfig = WalletBottomSheetConfig( isShow = true, - onDismissRequest = { - state.copySealed(bottomSheet = state.bottomSheetConfig?.copy(isShow = false)) - }, + onDismissRequest = clickIntents::onBottomSheetDismiss, content = content, ), ) + is WalletMultiCurrencyState.Locked -> state.copy( + isBottomSheetShow = true, + onBottomSheetDismiss = clickIntents::onBottomSheetDismiss, + ) + is WalletSingleCurrencyState.Content -> state.copy( + bottomSheetConfig = WalletBottomSheetConfig( + isShow = true, + onDismissRequest = clickIntents::onBottomSheetDismiss, + content = content, + ), + ) + is WalletSingleCurrencyState.Locked -> state.copy( + isBottomSheetShow = true, + onBottomSheetDismiss = clickIntents::onBottomSheetDismiss, + ) } } - fun getLoadingTxHistoryState(itemsCountEither: Either): WalletStateHolder { + fun getStateWithClosedBottomSheet(): WalletState { + return when (val state = currentStateProvider() as WalletState.ContentState) { + is WalletMultiCurrencyState.Content -> state.copy( + bottomSheetConfig = state.bottomSheetConfig?.copy(isShow = false), + ) + is WalletMultiCurrencyState.Locked -> state.copy(isBottomSheetShow = false) + is WalletSingleCurrencyState.Content -> state.copy( + bottomSheetConfig = state.bottomSheetConfig?.copy(isShow = false), + ) + is WalletSingleCurrencyState.Locked -> state.copy(isBottomSheetShow = false) + } + } + + fun getLoadingTxHistoryState(itemsCountEither: Either): WalletState { return loadingTransactionsStateConverter.convert(value = itemsCountEither) } fun getLoadedTxHistoryState( txHistoryEither: Either>>, - ): WalletStateHolder { + ): WalletState { return loadedTxHistoryConverter.convert(txHistoryEither) } - fun getLockedState(): WalletStateHolder { + fun getLockedState(): WalletState { val cardTypeResolver = currentCardTypeResolverProvider() - val state = currentStateProvider() + val state = requireNotNull(currentStateProvider() as? WalletState.ContentState) return if (cardTypeResolver.isMultiwalletAllowed()) { WalletMultiCurrencyState.Locked( onBackClick = state.onBackClick, - topBarConfig = state.topBarConfig, + topBarConfig = state.topBarConfig.copy( + onMoreClick = clickIntents::onUnlockWalletNotificationClick, + ), walletsListConfig = state.walletsListConfig, pullToRefreshConfig = state.pullToRefreshConfig, onUnlockWalletsNotificationClick = clickIntents::onUnlockWalletNotificationClick, @@ -140,7 +164,9 @@ internal class WalletStateFactory( } else { WalletSingleCurrencyState.Locked( onBackClick = state.onBackClick, - topBarConfig = state.topBarConfig, + topBarConfig = state.topBarConfig.copy( + onMoreClick = clickIntents::onUnlockWalletNotificationClick, + ), walletsListConfig = state.walletsListConfig, pullToRefreshConfig = state.pullToRefreshConfig, buttons = getButtons(), @@ -155,10 +181,11 @@ internal class WalletStateFactory( // TODO: [REDACTED_JIRA] private fun getButtons(): ImmutableList { return persistentListOf( - WalletManageButton.Buy(onClick = {}), - WalletManageButton.Send(onClick = {}), + WalletManageButton.Buy(), + WalletManageButton.Send(), WalletManageButton.Receive(onClick = {}), - WalletManageButton.Exchange(onClick = {}), + WalletManageButton.Exchange(), + WalletManageButton.Sell(), WalletManageButton.CopyAddress(onClick = {}), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt index 801c6556a2..d0c95681cb 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt @@ -3,18 +3,14 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory import androidx.paging.PagingData import arrow.core.Either import com.tangem.common.Provider -import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.txhistory.models.TxHistoryListError import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton +import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import com.tangem.utils.converter.Converter -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.Flow /** @@ -27,10 +23,10 @@ import kotlinx.coroutines.flow.Flow [REDACTED_AUTHOR] */ internal class WalletLoadedTxHistoryConverter( - private val currentStateProvider: Provider, + private val currentStateProvider: Provider, private val currentCardTypeResolverProvider: Provider, private val clickIntents: WalletClickIntents, -) : Converter>>, WalletStateHolder> { +) : Converter>>, WalletState> { private val walletTxHistoryItemFlowConverter by lazy { WalletTxHistoryItemFlowConverter( @@ -39,12 +35,12 @@ internal class WalletLoadedTxHistoryConverter( ) } - override fun convert(value: Either>>): WalletStateHolder { + override fun convert(value: Either>>): WalletState { return value.fold(ifLeft = ::convertError, ifRight = ::convert) } - private fun convertError(error: TxHistoryListError): WalletStateHolder { - return currentStateProvider().copySingleCurrencyContent( + private fun convertError(error: TxHistoryListError): WalletState { + return requireNotNull(currentStateProvider() as? WalletSingleCurrencyState.Content).copy( txHistoryState = when (error) { is TxHistoryListError.DataError -> { WalletTxHistoryState.Error(onReloadClick = clickIntents::onReloadClick) @@ -53,40 +49,9 @@ internal class WalletLoadedTxHistoryConverter( ) } - private fun convert(items: Flow>): WalletStateHolder { - return currentStateProvider().copySingleCurrencyContent( + private fun convert(items: Flow>): WalletState { + return requireNotNull(currentStateProvider() as? WalletSingleCurrencyState.Content).copy( txHistoryState = walletTxHistoryItemFlowConverter.convert(value = items), ) } - - private fun WalletStateHolder.copySingleCurrencyContent( - txHistoryState: WalletTxHistoryState, - ): WalletSingleCurrencyState { - return WalletSingleCurrencyState.Content( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - notifications = notifications, - bottomSheetConfig = bottomSheetConfig, - buttons = getButtons(), - marketPriceBlockState = getLoadingMarketPriceBlockState(), - txHistoryState = txHistoryState, - ) - } - - // TODO: [REDACTED_JIRA] - private fun getButtons(): ImmutableList { - return persistentListOf( - WalletManageButton.Buy(onClick = {}), - WalletManageButton.Send(onClick = {}), - WalletManageButton.Receive(onClick = {}), - WalletManageButton.Exchange(onClick = {}), - WalletManageButton.CopyAddress(onClick = {}), - ) - } - - private fun getLoadingMarketPriceBlockState(): MarketPriceBlockState { - return MarketPriceBlockState.Loading(currencyName = currentCardTypeResolverProvider().getBlockchain().currency) - } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt index a43a3f8355..c689acf2df 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt @@ -1,57 +1,33 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory -import androidx.paging.PagingData import arrow.core.Either import com.tangem.common.Provider -import com.tangem.core.ui.components.marketprice.MarketPriceBlockState -import com.tangem.core.ui.components.transactions.TransactionState -import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.txhistory.models.TxHistoryStateError import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton +import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import com.tangem.utils.converter.Converter -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf -import kotlinx.coroutines.flow.flowOf /** - * Converter from loading tx history to [WalletTxHistoryState] + * Converter from loading tx history state to [WalletSingleCurrencyState.Content] * * @property currentStateProvider current state provider - * @property currentCardTypeResolverProvider current card type resolver provider * @property clickIntents screen click intents * [REDACTED_AUTHOR] */ internal class WalletLoadingTxHistoryConverter( - private val currentStateProvider: Provider, - private val currentCardTypeResolverProvider: Provider, + private val currentStateProvider: Provider, private val clickIntents: WalletClickIntents, -) : Converter, WalletStateHolder> { +) : Converter, WalletSingleCurrencyState.Content> { - override fun convert(value: Either): WalletStateHolder { + override fun convert(value: Either): WalletSingleCurrencyState.Content { return value.fold(ifLeft = ::convertError, ifRight = ::convert) } - private fun convert(value: Int): WalletStateHolder { - return currentStateProvider().copySingleCurrencyContent( - txHistoryState = WalletTxHistoryState.Content( - items = flowOf( - value = PagingData.from( - data = buildList(capacity = value) { - add(WalletTxHistoryState.TxHistoryItemState.Transaction(state = TransactionState.Loading)) - }, - ), - ), - ), - ) - } - - private fun convertError(error: TxHistoryStateError): WalletStateHolder { - return currentStateProvider().copySingleCurrencyContent( + private fun convertError(error: TxHistoryStateError): WalletSingleCurrencyState.Content { + return requireNotNull(currentStateProvider() as? WalletSingleCurrencyState.Content).copy( txHistoryState = when (error) { is TxHistoryStateError.EmptyTxHistories -> { WalletTxHistoryState.Empty(onBuyClick = clickIntents::onBuyClick) @@ -66,34 +42,9 @@ internal class WalletLoadingTxHistoryConverter( ) } - private fun WalletStateHolder.copySingleCurrencyContent( - txHistoryState: WalletTxHistoryState, - ): WalletSingleCurrencyState { - return WalletSingleCurrencyState.Content( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - notifications = notifications, - bottomSheetConfig = bottomSheetConfig, - buttons = getButtons(), - marketPriceBlockState = getLoadingMarketPriceBlockState(), - txHistoryState = txHistoryState, + private fun convert(value: Int): WalletSingleCurrencyState.Content { + return requireNotNull(currentStateProvider() as? WalletSingleCurrencyState.Content).copy( + txHistoryState = WalletTxHistoryState.ContentWithLoadingItems(itemsCount = value), ) } - - // TODO: [REDACTED_JIRA] - private fun getButtons(): ImmutableList { - return persistentListOf( - WalletManageButton.Buy(onClick = {}), - WalletManageButton.Send(onClick = {}), - WalletManageButton.Receive(onClick = {}), - WalletManageButton.Exchange(onClick = {}), - WalletManageButton.CopyAddress(onClick = {}), - ) - } - - private fun getLoadingMarketPriceBlockState(): MarketPriceBlockState { - return MarketPriceBlockState.Loading(currencyName = currentCardTypeResolverProvider().getBlockchain().currency) - } } \ 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 cc15ab700c..0c8e6d0aa7 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 @@ -20,7 +20,7 @@ import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.presentation.common.WalletPreviewData 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.WalletStateHolder +import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletsList import com.tangem.feature.wallet.presentation.wallet.ui.components.common.* @@ -36,11 +36,19 @@ import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimat * [REDACTED_AUTHOR] */ -@OptIn(ExperimentalMaterialApi::class) @Composable -internal fun WalletScreen(state: WalletStateHolder) { +internal fun WalletScreen(state: WalletState) { BackHandler(onBack = state.onBackClick) + when (state) { + is WalletState.ContentState -> WalletContent(state = state) + is WalletState.Initial -> Unit + } +} + +@OptIn(ExperimentalMaterialApi::class) +@Composable +private fun WalletContent(state: WalletState.ContentState) { val walletsListState = rememberLazyListState() Scaffold( @@ -121,9 +129,7 @@ internal fun WalletScreen(state: WalletStateHolder) { // region Preview @Preview @Composable -private fun WalletScreenPreview_Light( - @PreviewParameter(WalletScreenParameterProvider::class) state: WalletStateHolder, -) { +private fun WalletScreenPreview_Light(@PreviewParameter(WalletScreenParameterProvider::class) state: WalletState) { TangemTheme { WalletScreen(state) } @@ -131,15 +137,13 @@ private fun WalletScreenPreview_Light( @Preview @Composable -private fun WalletScreenPreview_Dark( - @PreviewParameter(WalletScreenParameterProvider::class) state: WalletStateHolder, -) { +private fun WalletScreenPreview_Dark(@PreviewParameter(WalletScreenParameterProvider::class) state: WalletState) { TangemTheme(isDark = true) { WalletScreen(state) } } -private class WalletScreenParameterProvider : CollectionPreviewParameterProvider( +private class WalletScreenParameterProvider : CollectionPreviewParameterProvider( collection = listOf( WalletPreviewData.multicurrencyWalletScreenState, WalletPreviewData.singleWalletScreenState, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt index f95107b7e9..cbd8a73c90 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt @@ -3,10 +3,9 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.common import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.ui.Modifier import androidx.paging.compose.LazyPagingItems -import com.tangem.feature.wallet.presentation.wallet.state.WalletLoading 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.WalletStateHolder +import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.tokensListItems import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.txHistoryItems @@ -21,14 +20,12 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrenc [REDACTED_AUTHOR] */ internal fun LazyListScope.contentItems( - state: WalletStateHolder, + state: WalletState.ContentState, txHistoryItems: LazyPagingItems?, modifier: Modifier = Modifier, ) { when (state) { is WalletMultiCurrencyState -> tokensListItems(state.tokensListState, modifier) is WalletSingleCurrencyState -> txHistoryItems(state.txHistoryState, txHistoryItems, modifier) - is WalletLoading, - -> Unit } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt index 4b2f3cffb7..baa0144e1b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt @@ -1,5 +1,6 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.common +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.items import androidx.compose.ui.Modifier @@ -15,6 +16,11 @@ import kotlinx.collections.immutable.ImmutableList * [REDACTED_AUTHOR] */ +@OptIn(ExperimentalFoundationApi::class) internal fun LazyListScope.notifications(configs: ImmutableList, modifier: Modifier = Modifier) { - items(items = configs, itemContent = { Notification(state = it.state, modifier = modifier) }) + items( + items = configs, + key = { it.state.title.hashCode() }, + itemContent = { Notification(state = it.state, modifier = modifier.animateItemPlacement()) }, + ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt new file mode 100644 index 0000000000..4d8b4cc1f4 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt @@ -0,0 +1,21 @@ +package com.tangem.feature.wallet.presentation.wallet.utils + +import com.tangem.common.Provider +import com.tangem.domain.tokens.error.TokenListError +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.WalletTokensListState +import com.tangem.utils.converter.Converter +import kotlinx.collections.immutable.persistentListOf + +internal class TokenListErrorConverter( + private val currentStateProvider: Provider, +) : Converter { + + // TODO: [REDACTED_JIRA] + override fun convert(value: TokenListError): WalletMultiCurrencyState.Content { + return requireNotNull(currentStateProvider() as? WalletMultiCurrencyState.Content).copy( + tokensListState = WalletTokensListState.Content(items = persistentListOf(), onOrganizeTokensClick = null), + ) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorToWalletStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorToWalletStateConverter.kt deleted file mode 100644 index 2697e71bd3..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorToWalletStateConverter.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.utils - -import com.tangem.common.Provider -import com.tangem.domain.tokens.error.TokenListError -import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState -import com.tangem.utils.converter.Converter -import kotlinx.collections.immutable.persistentListOf - -internal class TokenListErrorToWalletStateConverter( - private val currentStateProvider: Provider, -) : Converter { - - // TODO: [REDACTED_JIRA] - override fun convert(value: TokenListError): WalletStateHolder { - val state = currentStateProvider() - return WalletMultiCurrencyState.Content( - onBackClick = state.onBackClick, - topBarConfig = state.topBarConfig, - walletsListConfig = state.walletsListConfig, - pullToRefreshConfig = state.pullToRefreshConfig, - notifications = state.notifications, - bottomSheetConfig = state.bottomSheetConfig, - tokensListState = WalletTokensListState.Content(items = persistentListOf(), onOrganizeTokensClick = null), - ) - } -} \ 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 f94b216186..885c12dd52 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 @@ -5,7 +5,7 @@ import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.tokens.model.TokenList 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.WalletStateHolder +import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig import com.tangem.feature.wallet.presentation.wallet.utils.TokenListToWalletStateConverter.TokensListModel @@ -15,14 +15,14 @@ import kotlinx.collections.immutable.toPersistentList @Suppress("LongParameterList") internal class TokenListToWalletStateConverter( - private val currentStateProvider: Provider, + private val currentStateProvider: Provider, private val cardTypeResolverProvider: Provider, private val isLockedWalletProvider: Provider, private val isWalletContentHidden: Boolean, private val fiatCurrencyCode: String, private val fiatCurrencySymbol: String, clickIntents: WalletClickIntents, -) : Converter { +) : Converter { private val tokenListToContentConverter = TokenListToContentItemsConverter( isWalletContentHidden = isWalletContentHidden, @@ -31,33 +31,20 @@ internal class TokenListToWalletStateConverter( clickIntents = clickIntents, ) - override fun convert(value: TokensListModel): WalletStateHolder { - val state = currentStateProvider() - return state - .updateWithTokenList(tokenList = value.tokenList) - .copySealed( - walletsListConfig = state.updateSelectedWallet(value.tokenList.totalFiatBalance), - pullToRefreshConfig = if (value.isRefreshing) { - state.pullToRefreshConfig.copy(isRefreshing = state.getRefreshingStatus()) - } else { - state.pullToRefreshConfig - }, - ) - } - - private fun WalletStateHolder.updateWithTokenList(tokenList: TokenList): WalletMultiCurrencyState.Content { - return WalletMultiCurrencyState.Content( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - notifications = notifications, - bottomSheetConfig = bottomSheetConfig, - tokensListState = tokenListToContentConverter.convert(value = tokenList), + override fun convert(value: TokensListModel): 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 = state.getRefreshingStatus()) + } else { + state.pullToRefreshConfig + }, + tokensListState = tokenListToContentConverter.convert(value = value.tokenList), ) } - private fun WalletStateHolder.updateSelectedWallet(fiatBalance: TokenList.FiatBalance): WalletsListConfig { + private fun WalletMultiCurrencyState.updateSelectedWallet(fiatBalance: TokenList.FiatBalance): WalletsListConfig { val selectedWalletIndex = walletsListConfig.selectedWalletIndex val selectedWalletCard = walletsListConfig.wallets[selectedWalletIndex] val converter = FiatBalanceToWalletCardConverter( @@ -70,13 +57,12 @@ internal class TokenListToWalletStateConverter( ) return walletsListConfig.copy( - wallets = walletsListConfig.wallets - .toPersistentList() + wallets = walletsListConfig.wallets.toPersistentList() .set(index = selectedWalletIndex, element = converter.convert(fiatBalance)), ) } - private fun WalletStateHolder.getRefreshingStatus(): Boolean { + private fun WalletState.getRefreshingStatus(): Boolean { return if (this is WalletMultiCurrencyState.Content) { tokensListState.items.any { tokensListItemState -> tokensListItemState is WalletTokensListState.TokensListItemState.Token && diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt index 89d57acf5b..e38b9bd0f3 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt @@ -35,4 +35,6 @@ internal interface WalletClickIntents { fun onUnlockWalletClick() fun onUnlockWalletNotificationClick() + + fun onBottomSheetDismiss() } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt index a7fa8f46f0..18bfbbbe34 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt @@ -7,7 +7,7 @@ import com.tangem.domain.tokens.model.NetworkGroup import com.tangem.domain.tokens.model.TokenList 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.WalletStateHolder +import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState import kotlinx.collections.immutable.ImmutableList @@ -26,7 +26,7 @@ import kotlinx.coroutines.flow.flow [REDACTED_AUTHOR] */ internal class WalletNotificationsListFactory( - private val currentStateProvider: Provider, + private val currentStateProvider: Provider, private val wasCardScannedCallback: suspend (String) -> Boolean, private val isUserAlreadyRateAppCallback: suspend () -> Boolean, private val isDemoCardCallback: (String) -> Boolean, 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 6083b4973d..bc50b0d051 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,17 +21,20 @@ import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase import com.tangem.domain.userwallets.UserWalletBuilder import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase -import com.tangem.domain.wallets.usecase.GetWalletsUseCase -import com.tangem.domain.wallets.usecase.SaveWalletUseCase -import com.tangem.domain.wallets.usecase.UnlockWalletsUseCase +import com.tangem.domain.wallets.usecase.* import com.tangem.feature.wallet.presentation.router.InnerWalletRouter -import com.tangem.feature.wallet.presentation.wallet.state.* +import com.tangem.feature.wallet.presentation.wallet.state.WalletLockedState +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.WalletBottomSheetConfig import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletStateFactory import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import javax.inject.Inject import kotlin.properties.Delegates @@ -41,11 +44,13 @@ import kotlin.properties.Delegates * [REDACTED_AUTHOR] */ -@Suppress("LongParameterList") +@Suppress("LongParameterList", "TooManyFunctions") @HiltViewModel internal class WalletViewModel @Inject constructor( private val getWalletsUseCase: GetWalletsUseCase, private val saveWalletUseCase: SaveWalletUseCase, + private val getSelectedWalletUseCase: GetSelectedWalletUseCase, + private val selectWalletUseCase: SelectWalletUseCase, private val getBiometricsStatusUseCase: GetBiometricsStatusUseCase, private val setAccessCodeRequestPolicyUseCase: SetAccessCodeRequestPolicyUseCase, private val getAccessCodeSavingStatusUseCase: GetAccessCodeSavingStatusUseCase, @@ -75,14 +80,18 @@ internal class WalletViewModel @Inject constructor( private val stateFactory = WalletStateFactory( currentStateProvider = Provider { uiState }, currentCardTypeResolverProvider = Provider { - getCardTypeResolver(index = uiState.walletsListConfig.selectedWalletIndex) + getCardTypeResolver( + index = requireNotNull(uiState as? WalletState.ContentState).walletsListConfig.selectedWalletIndex, + ) + }, + isLockedWalletProvider = Provider { + wallets[requireNotNull(uiState as? WalletState.ContentState).walletsListConfig.selectedWalletIndex].isLocked }, - isLockedWalletProvider = Provider { wallets[uiState.walletsListConfig.selectedWalletIndex].isLocked }, clickIntents = this, ) /** Screen state */ - var uiState: WalletStateHolder by mutableStateOf(stateFactory.getInitialState()) + var uiState: WalletState by mutableStateOf(stateFactory.getInitialState()) private set private var wallets: List by Delegates.notNull() @@ -100,17 +109,26 @@ internal class WalletViewModel @Inject constructor( } private fun updateWallets(sourceList: List) { - if (sourceList.isEmpty() || sourceList.all(UserWallet::isLocked)) return + if (sourceList.isEmpty()) return wallets = sourceList - val unlockedWalletIndex = sourceList.indexOfFirst { !it.isLocked } - uiState = stateFactory.getSkeletonState( - wallets = wallets, - index = if (unlockedWalletIndex == -1) 0 else unlockedWalletIndex, - ) + val currentState = uiState + val selectedWalletIndex = if (currentState is WalletLockedState) { + when (currentState) { + is WalletMultiCurrencyState.Locked -> currentState.walletsListConfig.selectedWalletIndex + is WalletSingleCurrencyState.Locked -> currentState.walletsListConfig.selectedWalletIndex + } + } else { + val selectedWallet = getSelectedWalletUseCase().fold( + ifLeft = { error("Selected wallet is null") }, + ifRight = { it }, + ) + sourceList.indexOfFirst { it.walletId == selectedWallet.walletId } + } - updateContentItems(index = unlockedWalletIndex) + uiState = stateFactory.getSkeletonState(wallets = sourceList, selectedWalletIndex = selectedWalletIndex) + updateContentItems(index = selectedWalletIndex) } private fun updateContentItems(index: Int, isRefreshing: Boolean = false) { @@ -122,8 +140,12 @@ internal class WalletViewModel @Inject constructor( } } - private fun updateByTokensList(index: Int, isRefreshing: Boolean) { - getTokenListUseCase(userWalletId = uiState.walletsListConfig.wallets[index].id) + private fun updateByTokensList(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( @@ -144,7 +166,7 @@ internal class WalletViewModel @Inject constructor( private fun updateByTxHistory(index: Int) { viewModelScope.launch(dispatchers.io) { val wallet = getWallet(index) - val blockchain = wallet.scanResponse.cardTypesResolver.getBlockchain() + val blockchain = getCardTypeResolver(index).getBlockchain() val derivationPath = blockchain.derivationPath(style = wallet.scanResponse.card.derivationStyle)?.rawPath val txHistoryItemsCountEither = txHistoryItemsCountUseCase( @@ -160,8 +182,9 @@ internal class WalletViewModel @Inject constructor( derivationPath = derivationPath, ) } - updateNotifications(index) } + + updateNotifications(index) } private fun updateTxHistory(networkId: Network.ID, derivationPath: String?) { @@ -179,7 +202,20 @@ internal class WalletViewModel @Inject constructor( .onEach { uiState = stateFactory.getStateByNotifications(notifications = it) } .flowOn(dispatchers.io) .launchIn(viewModelScope) - .saveIn(jobHolder = notificationsJobHolder) + .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 { @@ -258,21 +294,30 @@ internal class WalletViewModel @Inject constructor( } override fun onWalletChange(index: Int) { - if (uiState.walletsListConfig.selectedWalletIndex == index) return + val state = requireNotNull(uiState as? WalletState.ContentState) { + "Impossible to change wallet if state isn't WalletState.ContentState" + } - uiState = stateFactory.getStateAfterWalletChanging(index = index) + if (state.walletsListConfig.selectedWalletIndex == index) return + + uiState = stateFactory.getSkeletonState(wallets = wallets, selectedWalletIndex = index) updateContentItems(index = index) } override fun onRefreshSwipe() { uiState = stateFactory.getStateAfterContentRefreshing() - updateContentItems(index = uiState.walletsListConfig.selectedWalletIndex, isRefreshing = true) + + updateContentItems( + index = requireNotNull(uiState as? WalletState.ContentState).walletsListConfig.selectedWalletIndex, + isRefreshing = true, + ) } override fun onOrganizeTokensClick() { - val index = uiState.walletsListConfig.selectedWalletIndex - val walletId = uiState.walletsListConfig.wallets[index].id + val state = requireNotNull(uiState as? WalletState.ContentState) + val index = state.walletsListConfig.selectedWalletIndex + val walletId = state.walletsListConfig.wallets[index].id router.openOrganizeTokensScreen(walletId) } @@ -283,12 +328,16 @@ internal class WalletViewModel @Inject constructor( override fun onReloadClick() { uiState = stateFactory.getStateAfterContentRefreshing() - updateByTxHistory(index = uiState.walletsListConfig.selectedWalletIndex) + updateByTxHistory( + index = requireNotNull(uiState as? WalletState.ContentState).walletsListConfig.selectedWalletIndex, + ) } override fun onExploreClick() { viewModelScope.launch(dispatchers.io) { - val wallet = getWallet(uiState.walletsListConfig.selectedWalletIndex) + val wallet = getWallet( + index = requireNotNull(uiState as? WalletState.ContentState).walletsListConfig.selectedWalletIndex, + ) router.openTxHistoryWebsite( url = getExploreUrlUseCase( userWalletId = wallet.walletId, @@ -307,8 +356,19 @@ internal class WalletViewModel @Inject constructor( } override fun onUnlockWalletNotificationClick() { + val state = requireNotNull(uiState as? WalletLockedState) { + "Impossible to unlock wallet if state isn't WalletLockedState" + } + uiState = stateFactory.getStateWithOpenBottomSheet( - content = requireNotNull(uiState.bottomSheetConfig?.content), + content = when (state) { + is WalletMultiCurrencyState.Locked -> state.bottomSheetConfig.content + is WalletSingleCurrencyState.Locked -> state.bottomSheetConfig.content + }, ) } + + override fun onBottomSheetDismiss() { + uiState = stateFactory.getStateWithClosedBottomSheet() + } } \ No newline at end of file