Updated on 2026-08-14

This commit is contained in:
Tangem 2023-08-09 17:49:23 +08:00
parent 9ce639e080
commit 7e992b986b
19 changed files with 349 additions and 460 deletions

View file

@ -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<WalletNotification> = persistentListOf()
override val bottomSheetConfig = null
}

View file

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

View file

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

View file

@ -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<WalletNotification>
/** 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()
}

View file

@ -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<WalletNotification>
/** 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<WalletNotification> = 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,
)
}
}
}
}
}

View file

@ -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<WalletStateHolder>,
private val currentStateProvider: Provider<WalletState>,
cardTypeResolverProvider: Provider<CardTypesResolver>,
isLockedWalletProvider: Provider<Boolean>,
clickIntents: WalletClickIntents,
) : Converter<LoadedTokensListModel, WalletStateHolder> {
) : Converter<LoadedTokensListModel, WalletMultiCurrencyState.Content> {
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 = {

View file

@ -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<WalletState>,
private val clickIntents: WalletClickIntents,
) : Converter<SkeletonModel, WalletStateHolder> {
) : Converter<SkeletonModel, WalletState.ContentState> {
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<WalletTxHistoryState.TxHistoryItemState>() },
),
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<WalletManageButton> {
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<UserWallet>,
val selectedWalletIndex: Int,
)
data class SkeletonModel(val wallets: List<UserWallet>, val selectedWalletIndex: Int)
}

View file

@ -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<WalletStateHolder>,
private val currentStateProvider: Provider<WalletState>,
private val currentCardTypeResolverProvider: Provider<CardTypesResolver>,
private val isLockedWalletProvider: Provider<Boolean>,
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<UserWallet>, index: Int): WalletStateHolder {
fun getSkeletonState(wallets: List<UserWallet>, selectedWalletIndex: Int): WalletState {
return skeletonConverter.convert(
value = WalletSkeletonStateConverter.SkeletonModel(wallets = wallets, selectedWalletIndex = index),
value = WalletSkeletonStateConverter.SkeletonModel(
wallets = wallets,
selectedWalletIndex = selectedWalletIndex,
),
)
}
fun getStateByTokensList(
tokenListEither: Either<TokenListError, TokenList>,
isRefreshing: Boolean,
): WalletStateHolder {
fun getStateByTokensList(tokenListEither: Either<TokenListError, TokenList>, isRefreshing: Boolean): WalletState {
return loadedTokensListConverter.convert(
value = LoadedTokensListModel(tokenListEither = tokenListEither, isRefreshing = isRefreshing),
value = WalletLoadedTokensListConverter.LoadedTokensListModel(
tokenListEither = tokenListEither,
isRefreshing = isRefreshing,
),
)
}
fun getStateByNotifications(notifications: ImmutableList<WalletNotification>): 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<WalletNotification>): 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<TxHistoryStateError, Int>): 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<TxHistoryStateError, Int>): WalletState {
return loadingTransactionsStateConverter.convert(value = itemsCountEither)
}
fun getLoadedTxHistoryState(
txHistoryEither: Either<TxHistoryListError, Flow<PagingData<TxHistoryItem>>>,
): 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<WalletManageButton> {
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 = {}),
)
}

View file

@ -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<WalletStateHolder>,
private val currentStateProvider: Provider<WalletState>,
private val currentCardTypeResolverProvider: Provider<CardTypesResolver>,
private val clickIntents: WalletClickIntents,
) : Converter<Either<TxHistoryListError, Flow<PagingData<TxHistoryItem>>>, WalletStateHolder> {
) : Converter<Either<TxHistoryListError, Flow<PagingData<TxHistoryItem>>>, WalletState> {
private val walletTxHistoryItemFlowConverter by lazy {
WalletTxHistoryItemFlowConverter(
@ -39,12 +35,12 @@ internal class WalletLoadedTxHistoryConverter(
)
}
override fun convert(value: Either<TxHistoryListError, Flow<PagingData<TxHistoryItem>>>): WalletStateHolder {
override fun convert(value: Either<TxHistoryListError, Flow<PagingData<TxHistoryItem>>>): 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<PagingData<TxHistoryItem>>): WalletStateHolder {
return currentStateProvider().copySingleCurrencyContent(
private fun convert(items: Flow<PagingData<TxHistoryItem>>): 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<WalletManageButton> {
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)
}
}

View file

@ -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<WalletStateHolder>,
private val currentCardTypeResolverProvider: Provider<CardTypesResolver>,
private val currentStateProvider: Provider<WalletState>,
private val clickIntents: WalletClickIntents,
) : Converter<Either<TxHistoryStateError, Int>, WalletStateHolder> {
) : Converter<Either<TxHistoryStateError, Int>, WalletSingleCurrencyState.Content> {
override fun convert(value: Either<TxHistoryStateError, Int>): WalletStateHolder {
override fun convert(value: Either<TxHistoryStateError, Int>): 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<WalletManageButton> {
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)
}
}

View file

@ -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<WalletStateHolder>(
private class WalletScreenParameterProvider : CollectionPreviewParameterProvider<WalletState>(
collection = listOf(
WalletPreviewData.multicurrencyWalletScreenState,
WalletPreviewData.singleWalletScreenState,

View file

@ -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<WalletTxHistoryState.TxHistoryItemState>?,
modifier: Modifier = Modifier,
) {
when (state) {
is WalletMultiCurrencyState -> tokensListItems(state.tokensListState, modifier)
is WalletSingleCurrencyState -> txHistoryItems(state.txHistoryState, txHistoryItems, modifier)
is WalletLoading,
-> Unit
}
}

View file

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

View file

@ -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<WalletState>,
) : Converter<TokenListError, WalletMultiCurrencyState.Content> {
// TODO: [REDACTED_JIRA]
override fun convert(value: TokenListError): WalletMultiCurrencyState.Content {
return requireNotNull(currentStateProvider() as? WalletMultiCurrencyState.Content).copy(
tokensListState = WalletTokensListState.Content(items = persistentListOf(), onOrganizeTokensClick = null),
)
}
}

View file

@ -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<WalletStateHolder>,
) : Converter<TokenListError, WalletStateHolder> {
// 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),
)
}
}

View file

@ -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<WalletStateHolder>,
private val currentStateProvider: Provider<WalletState>,
private val cardTypeResolverProvider: Provider<CardTypesResolver>,
private val isLockedWalletProvider: Provider<Boolean>,
private val isWalletContentHidden: Boolean,
private val fiatCurrencyCode: String,
private val fiatCurrencySymbol: String,
clickIntents: WalletClickIntents,
) : Converter<TokensListModel, WalletStateHolder> {
) : Converter<TokensListModel, WalletMultiCurrencyState.Content> {
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 &&

View file

@ -35,4 +35,6 @@ internal interface WalletClickIntents {
fun onUnlockWalletClick()
fun onUnlockWalletNotificationClick()
fun onBottomSheetDismiss()
}

View file

@ -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<WalletStateHolder>,
private val currentStateProvider: Provider<WalletState>,
private val wasCardScannedCallback: suspend (String) -> Boolean,
private val isUserAlreadyRateAppCallback: suspend () -> Boolean,
private val isDemoCardCallback: (String) -> Boolean,

View file

@ -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<UserWallet> by Delegates.notNull()
@ -100,17 +109,26 @@ internal class WalletViewModel @Inject constructor(
}
private fun updateWallets(sourceList: List<UserWallet>) {
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()
}
}