Updated on 2026-08-14
This commit is contained in:
parent
c444f76c76
commit
4ad8c5bfc1
17 changed files with 318 additions and 124 deletions
|
|
@ -1,76 +1,67 @@
|
|||
package com.tangem.core.ui.components.transactions.state
|
||||
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.TerminalSeparatorType
|
||||
import androidx.paging.insertHeaderItem
|
||||
import com.tangem.core.ui.components.wallet.WalletLockedContentState
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
/**
|
||||
* Wallet transaction history state
|
||||
*/
|
||||
/** Wallet transaction history state */
|
||||
sealed interface TxHistoryState {
|
||||
|
||||
/**
|
||||
* Wallet transaction history state with content
|
||||
* Wallet transaction history state with content. Items contains a required [TxHistoryItemState.Title].
|
||||
*
|
||||
* @property items content items
|
||||
* @property contentItems content items
|
||||
*/
|
||||
sealed class ContentState(open val items: Flow<PagingData<TxHistoryItemState>>) : TxHistoryState
|
||||
sealed class ContentState(private val contentItems: Flow<PagingData<TxHistoryItemState>>) : TxHistoryState {
|
||||
|
||||
/** Lambda be invoke when explore button was clicked */
|
||||
abstract val onExploreClick: () -> Unit
|
||||
|
||||
/** Content items with [TxHistoryItemState.Title] */
|
||||
val items: Flow<PagingData<TxHistoryItemState>>
|
||||
get() {
|
||||
return contentItems.map {
|
||||
it.insertHeaderItem(
|
||||
terminalSeparatorType = TerminalSeparatorType.SOURCE_COMPLETE,
|
||||
item = TxHistoryItemState.Title(onExploreClick = onExploreClick),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loading state
|
||||
*
|
||||
* @property onExploreClick lambda be invoke when explore button was clicked
|
||||
* @property transactions loading transactions
|
||||
*/
|
||||
data class Loading(val onExploreClick: () -> Unit) : ContentState(
|
||||
items = flowOf(
|
||||
PagingData.from(
|
||||
listOf(
|
||||
TxHistoryItemState.Title(onExploreClick = onExploreClick),
|
||||
TxHistoryItemState.Transaction(state = TransactionState.Loading(txHash = LOADING_TX_HASH)),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
/**
|
||||
* Wallet transaction history state with loading transactions
|
||||
*
|
||||
* @property itemsCount count of loading transactions
|
||||
*/
|
||||
data class ContentWithLoadingItems(val itemsCount: Int) : ContentState(
|
||||
items = flowOf(
|
||||
value = PagingData.from(
|
||||
data = buildList(capacity = itemsCount) {
|
||||
add(TxHistoryItemState.Transaction(state = TransactionState.Loading(txHash = LOADING_TX_HASH)))
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
data class Loading(
|
||||
override val onExploreClick: () -> Unit,
|
||||
val transactions: Flow<PagingData<TxHistoryItemState>> = getDefaultLoadingTransactions(),
|
||||
) : ContentState(transactions)
|
||||
|
||||
/**
|
||||
* Wallet transaction history state with content
|
||||
*
|
||||
* @property items content items
|
||||
* @property onExploreClick lambda be invoke when explore button was clicked
|
||||
* @property contentItems content items
|
||||
*/
|
||||
data class Content(override val items: Flow<PagingData<TxHistoryItemState>>) : ContentState(items)
|
||||
data class Content(
|
||||
override val onExploreClick: () -> Unit,
|
||||
val contentItems: Flow<PagingData<TxHistoryItemState>>,
|
||||
) : ContentState(contentItems)
|
||||
|
||||
/**
|
||||
* Locked state
|
||||
*
|
||||
* @property onExploreClick lambda be invoke when explore button was clicked
|
||||
*/
|
||||
data class Locked(val onExploreClick: () -> Unit) :
|
||||
ContentState(
|
||||
items = flowOf(
|
||||
PagingData.from(
|
||||
listOf(
|
||||
TxHistoryItemState.Title(onExploreClick = onExploreClick),
|
||||
TxHistoryItemState.Transaction(state = TransactionState.Loading(txHash = LOADING_TX_HASH)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
data class Locked(override val onExploreClick: () -> Unit) :
|
||||
ContentState(contentItems = getDefaultLoadingTransactions()),
|
||||
WalletLockedContentState
|
||||
|
||||
/**
|
||||
|
|
@ -121,5 +112,17 @@ sealed interface TxHistoryState {
|
|||
|
||||
private companion object {
|
||||
const val LOADING_TX_HASH = "LOADING_TX_HASH"
|
||||
|
||||
private fun getDefaultLoadingTransactions(): Flow<PagingData<TxHistoryItemState>> {
|
||||
return flowOf(
|
||||
value = PagingData.from(
|
||||
data = listOf(
|
||||
element = TxHistoryItemState.Transaction(
|
||||
state = TransactionState.Loading(txHash = LOADING_TX_HASH),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -351,10 +351,10 @@ internal object WalletPreviewData {
|
|||
),
|
||||
),
|
||||
txHistoryState = TxHistoryState.Content(
|
||||
flowOf(
|
||||
onExploreClick = {},
|
||||
contentItems = flowOf(
|
||||
PagingData.from(
|
||||
listOf(
|
||||
TxHistoryState.TxHistoryItemState.Title(onExploreClick = {}),
|
||||
TxHistoryState.TxHistoryItemState.GroupTitle("Today"),
|
||||
TxHistoryState.TxHistoryItemState.Transaction(
|
||||
TransactionState.Sending(
|
||||
|
|
|
|||
|
|
@ -75,12 +75,13 @@ internal sealed interface TokenItemState {
|
|||
) : TokenItemState
|
||||
|
||||
/** Token options state */
|
||||
@Immutable
|
||||
sealed interface TokenOptionsState {
|
||||
|
||||
/**
|
||||
* Visible token options state
|
||||
*
|
||||
* @property fiatAmount fiat amount of token
|
||||
* @property fiatAmount fiat amount of token
|
||||
* @property priceChange value of price changing
|
||||
*/
|
||||
data class Visible(val fiatAmount: String, val priceChange: PriceChangeConfig) : TokenOptionsState
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
|
||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.*
|
||||
|
|
@ -11,14 +12,12 @@ import kotlinx.collections.immutable.persistentListOf
|
|||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Immutable
|
||||
internal sealed class WalletSingleCurrencyState : WalletState.ContentState() {
|
||||
|
||||
/** Manage buttons */
|
||||
abstract val buttons: ImmutableList<WalletManageButton>
|
||||
|
||||
/** Market price block state */
|
||||
abstract val marketPriceBlockState: MarketPriceBlockState?
|
||||
|
||||
/** Transactions history state */
|
||||
abstract val txHistoryState: TxHistoryState
|
||||
|
||||
|
|
@ -30,8 +29,8 @@ internal sealed class WalletSingleCurrencyState : WalletState.ContentState() {
|
|||
override val notifications: ImmutableList<WalletNotification>,
|
||||
override val bottomSheetConfig: WalletBottomSheetConfig?,
|
||||
override val buttons: ImmutableList<WalletManageButton>,
|
||||
override val marketPriceBlockState: MarketPriceBlockState,
|
||||
override val txHistoryState: TxHistoryState,
|
||||
val marketPriceBlockState: MarketPriceBlockState,
|
||||
) : WalletSingleCurrencyState()
|
||||
|
||||
data class Locked(
|
||||
|
|
@ -61,8 +60,6 @@ internal sealed class WalletSingleCurrencyState : WalletState.ContentState() {
|
|||
),
|
||||
)
|
||||
|
||||
override val marketPriceBlockState = null
|
||||
|
||||
override val txHistoryState: TxHistoryState = TxHistoryState.Locked(onExploreClick)
|
||||
}
|
||||
}
|
||||
|
|
@ -34,14 +34,26 @@ internal sealed class WalletState {
|
|||
/**
|
||||
* Util function that allow to make a copy
|
||||
*
|
||||
* @param walletsListConfig wallets list config
|
||||
* @param walletsListConfig wallets list config
|
||||
* @param pullToRefreshConfig pull to refresh config
|
||||
*/
|
||||
fun copySealed(walletsListConfig: WalletsListConfig = this.walletsListConfig): ContentState {
|
||||
fun copySealed(
|
||||
walletsListConfig: WalletsListConfig = this.walletsListConfig,
|
||||
pullToRefreshConfig: WalletPullToRefreshConfig = this.pullToRefreshConfig,
|
||||
): ContentState {
|
||||
return when (this) {
|
||||
is WalletMultiCurrencyState.Content -> copy(walletsListConfig = walletsListConfig)
|
||||
is WalletMultiCurrencyState.Locked -> copy(walletsListConfig = walletsListConfig)
|
||||
is WalletSingleCurrencyState.Content -> copy(walletsListConfig = walletsListConfig)
|
||||
is WalletSingleCurrencyState.Locked -> copy(walletsListConfig = walletsListConfig)
|
||||
is WalletMultiCurrencyState.Content -> {
|
||||
copy(walletsListConfig = walletsListConfig, pullToRefreshConfig = pullToRefreshConfig)
|
||||
}
|
||||
is WalletMultiCurrencyState.Locked -> {
|
||||
copy(walletsListConfig = walletsListConfig, pullToRefreshConfig = pullToRefreshConfig)
|
||||
}
|
||||
is WalletSingleCurrencyState.Content -> {
|
||||
copy(walletsListConfig = walletsListConfig, pullToRefreshConfig = pullToRefreshConfig)
|
||||
}
|
||||
is WalletSingleCurrencyState.Locked -> {
|
||||
copy(walletsListConfig = walletsListConfig, pullToRefreshConfig = pullToRefreshConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.components
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
|
|
@ -11,6 +12,7 @@ import com.tangem.feature.wallet.impl.R
|
|||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Immutable
|
||||
sealed class WalletManageButton(val config: ActionButtonConfig) {
|
||||
|
||||
/** Lambda be invoked when manage button is clicked */
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.components
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.notifications.NotificationState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.WrappedList
|
||||
|
|
@ -14,6 +15,7 @@ import com.tangem.feature.wallet.impl.R
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
// TODO: Finalize notification strings [REDACTED_JIRA]
|
||||
@Immutable
|
||||
sealed class WalletNotification(open val state: NotificationState) {
|
||||
|
||||
/** Clickable notification */
|
||||
|
|
|
|||
|
|
@ -28,14 +28,17 @@ internal sealed class WalletTokensListState {
|
|||
open val onOrganizeTokensClick: (() -> Unit)?,
|
||||
) : WalletTokensListState()
|
||||
|
||||
/** Loading content state */
|
||||
object Loading : ContentState(
|
||||
items = persistentListOf(
|
||||
/**
|
||||
* Loading content state
|
||||
*
|
||||
* @property items content items
|
||||
*/
|
||||
data class Loading(
|
||||
override val items: ImmutableList<TokensListItemState.Token> = persistentListOf(
|
||||
TokensListItemState.Token(state = TokenItemState.Loading(id = FIRST_LOADING_TOKEN_ID)),
|
||||
TokensListItemState.Token(state = TokenItemState.Loading(id = SECOND_LOADING_TOKEN_ID)),
|
||||
),
|
||||
onOrganizeTokensClick = null,
|
||||
)
|
||||
) : ContentState(items = items, onOrganizeTokensClick = null)
|
||||
|
||||
/**
|
||||
* Content state
|
||||
|
|
|
|||
|
|
@ -0,0 +1,128 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.factory
|
||||
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.map
|
||||
import com.tangem.common.Provider
|
||||
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
|
||||
import com.tangem.core.ui.components.transactions.state.TransactionState
|
||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState.TxHistoryItemState
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletPullToRefreshConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState.TokensListItemState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
internal class WalletRefreshStateConverter(
|
||||
private val currentStateProvider: Provider<WalletState>,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
) : Converter<Unit, WalletState> {
|
||||
|
||||
override fun convert(value: Unit): WalletState {
|
||||
return when (val state = currentStateProvider()) {
|
||||
is WalletMultiCurrencyState.Content -> state.getRefreshState()
|
||||
is WalletSingleCurrencyState.Content -> state.getRefreshState()
|
||||
else -> state
|
||||
}
|
||||
}
|
||||
|
||||
private fun WalletMultiCurrencyState.Content.getRefreshState(): WalletMultiCurrencyState.Content {
|
||||
return copy(
|
||||
walletsListConfig = getWalletsListConfig(),
|
||||
pullToRefreshConfig = getPullToRefreshConfig(),
|
||||
tokensListState = getTokenListState(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun WalletSingleCurrencyState.Content.getRefreshState(): WalletSingleCurrencyState.Content {
|
||||
return copy(
|
||||
// TODO: [REDACTED_JIRA]
|
||||
walletsListConfig = getWalletsListConfig(additionalInfo = ""),
|
||||
pullToRefreshConfig = getPullToRefreshConfig(),
|
||||
txHistoryState = getTxHistoryState(),
|
||||
marketPriceBlockState = MarketPriceBlockState.Loading(currencyName = marketPriceBlockState.currencyName),
|
||||
)
|
||||
}
|
||||
|
||||
private fun WalletState.ContentState.getWalletsListConfig(additionalInfo: String? = null): WalletsListConfig {
|
||||
val selectedWallet = walletsListConfig.wallets[walletsListConfig.selectedWalletIndex]
|
||||
|
||||
return walletsListConfig.copy(
|
||||
wallets = walletsListConfig.wallets
|
||||
.toPersistentList()
|
||||
.set(
|
||||
index = walletsListConfig.selectedWalletIndex,
|
||||
element = WalletCardState.Loading(
|
||||
id = selectedWallet.id,
|
||||
title = selectedWallet.title,
|
||||
additionalInfo = additionalInfo ?: selectedWallet.additionalInfo,
|
||||
imageResId = selectedWallet.imageResId,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun WalletState.ContentState.getPullToRefreshConfig(): WalletPullToRefreshConfig {
|
||||
return pullToRefreshConfig.copy(isRefreshing = true)
|
||||
}
|
||||
|
||||
private fun WalletMultiCurrencyState.Content.getTokenListState(): WalletTokensListState {
|
||||
return when (tokensListState) {
|
||||
is WalletTokensListState.Content -> {
|
||||
WalletTokensListState.Loading(
|
||||
items = tokensListState.items
|
||||
.filterIsInstance<TokensListItemState.Token>()
|
||||
.map {
|
||||
TokensListItemState.Token(state = TokenItemState.Loading(id = it.state.id))
|
||||
}
|
||||
.toImmutableList(),
|
||||
)
|
||||
}
|
||||
is WalletTokensListState.Empty -> WalletTokensListState.Loading()
|
||||
is WalletTokensListState.Loading,
|
||||
is WalletTokensListState.Locked,
|
||||
-> tokensListState
|
||||
}
|
||||
}
|
||||
|
||||
private fun WalletSingleCurrencyState.Content.getTxHistoryState(): TxHistoryState {
|
||||
return when (txHistoryState) {
|
||||
is TxHistoryState.Content -> {
|
||||
TxHistoryState.Loading(
|
||||
onExploreClick = clickIntents::onExploreClick,
|
||||
transactions = txHistoryState.contentItems
|
||||
.filterIsInstance<PagingData<TxHistoryItemState.Transaction>>()
|
||||
.mapPagingData { transaction ->
|
||||
transaction.copy(
|
||||
state = TransactionState.Loading(txHash = transaction.state.txHash),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
is TxHistoryState.Empty,
|
||||
is TxHistoryState.Error,
|
||||
is TxHistoryState.NotSupported,
|
||||
-> TxHistoryState.Loading(onExploreClick = clickIntents::onExploreClick)
|
||||
is TxHistoryState.Locked,
|
||||
is TxHistoryState.Loading,
|
||||
-> txHistoryState
|
||||
}
|
||||
}
|
||||
|
||||
private fun Flow<PagingData<TxHistoryItemState.Transaction>>.mapPagingData(
|
||||
transform: (TxHistoryItemState.Transaction) -> TxHistoryItemState,
|
||||
): Flow<PagingData<TxHistoryItemState>> {
|
||||
return map { it.map(transform) }
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyS
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletSingleCurrencyLoadedBalanceConverter.SingleCurrencyLoadedBalanceModel
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -22,21 +23,32 @@ internal class WalletSingleCurrencyLoadedBalanceConverter(
|
|||
private val currentStateProvider: Provider<WalletState>,
|
||||
private val cardTypeResolverProvider: Provider<CardTypesResolver>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
) : Converter<Either<CurrencyError, CryptoCurrencyStatus>, WalletSingleCurrencyState.Content> {
|
||||
) : Converter<SingleCurrencyLoadedBalanceModel, WalletSingleCurrencyState.Content> {
|
||||
|
||||
override fun convert(value: Either<CurrencyError, CryptoCurrencyStatus>): WalletSingleCurrencyState.Content {
|
||||
return value.fold(ifLeft = { convertError() }, ifRight = ::convert)
|
||||
override fun convert(value: SingleCurrencyLoadedBalanceModel): WalletSingleCurrencyState.Content {
|
||||
return value.cryptoCurrencyEither.fold(
|
||||
ifLeft = { convertError() },
|
||||
ifRight = { convertContent(it, value.isRefreshing) },
|
||||
)
|
||||
}
|
||||
|
||||
private fun convertError(): WalletSingleCurrencyState.Content {
|
||||
return requireNotNull(currentStateProvider() as? WalletSingleCurrencyState.Content)
|
||||
}
|
||||
|
||||
private fun convert(status: CryptoCurrencyStatus): WalletSingleCurrencyState.Content {
|
||||
private fun convertContent(
|
||||
status: CryptoCurrencyStatus,
|
||||
isRefreshing: Boolean,
|
||||
): WalletSingleCurrencyState.Content {
|
||||
val state = requireNotNull(currentStateProvider() as? WalletSingleCurrencyState.Content)
|
||||
val currencyName = state.marketPriceBlockState.currencyName
|
||||
return state.copy(
|
||||
walletsListConfig = getUpdatedSelectedWallet(status = status.value, state = state),
|
||||
pullToRefreshConfig = if (isRefreshing) {
|
||||
state.pullToRefreshConfig.copy(isRefreshing = status.value is CryptoCurrencyStatus.Loading)
|
||||
} else {
|
||||
state.pullToRefreshConfig
|
||||
},
|
||||
marketPriceBlockState = getMarketPriceState(status = status.value, currencyName = currencyName),
|
||||
)
|
||||
}
|
||||
|
|
@ -153,4 +165,9 @@ internal class WalletSingleCurrencyLoadedBalanceConverter(
|
|||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
)
|
||||
}
|
||||
|
||||
data class SingleCurrencyLoadedBalanceModel(
|
||||
val cryptoCurrencyEither: Either<CurrencyError, CryptoCurrencyStatus>,
|
||||
val isRefreshing: Boolean,
|
||||
)
|
||||
}
|
||||
|
|
@ -47,7 +47,7 @@ internal class WalletSkeletonStateConverter(
|
|||
topBarConfig = createTopBarConfig(),
|
||||
walletsListConfig = createWalletsListConfig(value),
|
||||
pullToRefreshConfig = createPullToRefreshConfig(),
|
||||
tokensListState = WalletTokensListState.Loading,
|
||||
tokensListState = WalletTokensListState.Loading(),
|
||||
notifications = persistentListOf(),
|
||||
bottomSheetConfig = null,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -77,6 +77,10 @@ internal class WalletStateFactory(
|
|||
)
|
||||
}
|
||||
|
||||
private val refreshStateConverter by lazy {
|
||||
WalletRefreshStateConverter(currentStateProvider = currentStateProvider, clickIntents = clickIntents)
|
||||
}
|
||||
|
||||
fun getInitialState(): WalletState = WalletState.Initial(onBackClick = clickIntents::onBackClick)
|
||||
|
||||
fun getSkeletonState(wallets: List<UserWallet>, selectedWalletIndex: Int): WalletState {
|
||||
|
|
@ -105,9 +109,7 @@ internal class WalletStateFactory(
|
|||
}
|
||||
}
|
||||
|
||||
fun getStateAfterContentRefreshing(): WalletState {
|
||||
return currentStateProvider()
|
||||
}
|
||||
fun getStateAfterContentRefreshing(): WalletState = refreshStateConverter.convert(Unit)
|
||||
|
||||
fun getStateWithOpenBottomSheet(content: WalletBottomSheetConfig.BottomSheetContentConfig): WalletState {
|
||||
return when (val state = currentStateProvider() as WalletState.ContentState) {
|
||||
|
|
@ -205,7 +207,13 @@ internal class WalletStateFactory(
|
|||
|
||||
fun getSingleCurrencyLoadedBalanceState(
|
||||
cryptoCurrencyEither: Either<CurrencyError, CryptoCurrencyStatus>,
|
||||
isRefreshing: Boolean,
|
||||
): WalletState {
|
||||
return singleCurrencyLoadedBalanceConverter.convert(cryptoCurrencyEither)
|
||||
return singleCurrencyLoadedBalanceConverter.convert(
|
||||
value = WalletSingleCurrencyLoadedBalanceConverter.SingleCurrencyLoadedBalanceModel(
|
||||
cryptoCurrencyEither = cryptoCurrencyEither,
|
||||
isRefreshing = isRefreshing,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,16 @@
|
|||
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.transactions.state.TransactionState
|
||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||
import com.tangem.domain.txhistory.models.TxHistoryStateError
|
||||
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.viewmodels.WalletClickIntents
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.coroutines.flow.flow
|
||||
|
||||
/**
|
||||
* Converter from loading tx history state to [WalletSingleCurrencyState.Content]
|
||||
|
|
@ -44,7 +47,17 @@ internal class WalletLoadingTxHistoryConverter(
|
|||
|
||||
private fun convert(value: Int): WalletSingleCurrencyState.Content {
|
||||
return requireNotNull(currentStateProvider() as? WalletSingleCurrencyState.Content).copy(
|
||||
txHistoryState = TxHistoryState.ContentWithLoadingItems(itemsCount = value),
|
||||
txHistoryState = TxHistoryState.Loading(
|
||||
onExploreClick = clickIntents::onExploreClick,
|
||||
transactions = flow {
|
||||
PagingData.from(
|
||||
data = MutableList(
|
||||
size = value,
|
||||
init = { TransactionState.Loading(it.toString()) },
|
||||
),
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory
|
||||
|
||||
import android.text.format.DateUtils
|
||||
import androidx.paging.*
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.TerminalSeparatorType
|
||||
import androidx.paging.insertSeparators
|
||||
import androidx.paging.map
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.core.ui.components.transactions.state.TransactionState
|
||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||
|
|
@ -58,17 +61,14 @@ internal class WalletTxHistoryItemFlowConverter(
|
|||
|
||||
override fun convert(value: Flow<PagingData<TxHistoryItem>>): TxHistoryState {
|
||||
return TxHistoryState.Content(
|
||||
items = value
|
||||
onExploreClick = clickIntents::onExploreClick,
|
||||
contentItems = value
|
||||
.map { pagingData ->
|
||||
pagingData
|
||||
.map<TxHistoryItem, TxHistoryItemState> { item ->
|
||||
// [createTransactionState] returns timestamp without formatting
|
||||
TxHistoryItemState.Transaction(state = createTransactionState(item))
|
||||
}
|
||||
.insertHeaderItem(
|
||||
terminalSeparatorType = TerminalSeparatorType.SOURCE_COMPLETE,
|
||||
item = TxHistoryItemState.Title(onExploreClick = clickIntents::onExploreClick),
|
||||
)
|
||||
.insertGroupTitle() // method uses the raw timestamp
|
||||
.formatTransactionsTimestamp() // method formats the timestamp
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,10 +4,8 @@ import com.tangem.common.Provider
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
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.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
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents
|
||||
|
|
@ -35,7 +33,7 @@ internal class TokenListToWalletStateConverter(
|
|||
return state.copy(
|
||||
walletsListConfig = state.updateSelectedWallet(fiatBalance = value.tokenList.totalFiatBalance),
|
||||
pullToRefreshConfig = if (value.isRefreshing) {
|
||||
state.pullToRefreshConfig.copy(isRefreshing = state.getRefreshingStatus())
|
||||
state.pullToRefreshConfig.copy(isRefreshing = getRefreshingStatus(tokenList = value.tokenList))
|
||||
} else {
|
||||
state.pullToRefreshConfig
|
||||
},
|
||||
|
|
@ -60,17 +58,8 @@ internal class TokenListToWalletStateConverter(
|
|||
)
|
||||
}
|
||||
|
||||
private fun WalletState.getRefreshingStatus(): Boolean {
|
||||
return if (this is WalletMultiCurrencyState.Content &&
|
||||
this.tokensListState is WalletTokensListState.ContentState
|
||||
) {
|
||||
tokensListState.items.any { tokensListItemState ->
|
||||
tokensListItemState is WalletTokensListState.TokensListItemState.Token &&
|
||||
tokensListItemState.state is TokenItemState.Loading
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
private fun getRefreshingStatus(tokenList: TokenList): Boolean {
|
||||
return tokenList.totalFiatBalance is TokenList.FiatBalance.Loading
|
||||
}
|
||||
|
||||
data class TokensListModel(val tokenList: TokenList, val isRefreshing: Boolean)
|
||||
|
|
|
|||
|
|
@ -1,15 +1,10 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.viewmodels
|
||||
|
||||
import com.tangem.common.Provider
|
||||
import com.tangem.domain.common.CardTypesResolver
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
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.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
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -26,7 +21,6 @@ import kotlinx.coroutines.flow.flow
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class WalletNotificationsListFactory(
|
||||
private val currentStateProvider: Provider<WalletState>,
|
||||
private val wasCardScannedCallback: suspend (String) -> Boolean,
|
||||
private val isUserAlreadyRateAppCallback: suspend () -> Boolean,
|
||||
private val isDemoCardCallback: (String) -> Boolean,
|
||||
|
|
@ -56,7 +50,7 @@ internal class WalletNotificationsListFactory(
|
|||
add(element = WalletNotification.DemoCard)
|
||||
}
|
||||
|
||||
if (hasUnreachableNetworks()) {
|
||||
if (hasUnreachableNetworks(tokenList)) {
|
||||
add(element = WalletNotification.UnreachableNetworks)
|
||||
}
|
||||
|
||||
|
|
@ -112,15 +106,22 @@ internal class WalletNotificationsListFactory(
|
|||
}
|
||||
}
|
||||
|
||||
private fun hasUnreachableNetworks(): Boolean {
|
||||
val isUnreachableState = { item: WalletTokensListState.TokensListItemState ->
|
||||
(item as? WalletTokensListState.TokensListItemState.Token)?.state is TokenItemState.Unreachable
|
||||
}
|
||||
|
||||
return currentStateProvider().let { state ->
|
||||
state is WalletMultiCurrencyState.Content &&
|
||||
state.tokensListState is WalletTokensListState.ContentState &&
|
||||
state.tokensListState.items.any(isUnreachableState)
|
||||
private fun hasUnreachableNetworks(tokenList: TokenList?): Boolean {
|
||||
return when (tokenList) {
|
||||
is TokenList.GroupedByNetwork -> {
|
||||
tokenList.groups
|
||||
.flatMap(NetworkGroup::currencies)
|
||||
.map(CryptoCurrencyStatus::value)
|
||||
.any { it is CryptoCurrencyStatus.Unreachable }
|
||||
}
|
||||
is TokenList.Ungrouped -> {
|
||||
tokenList.currencies
|
||||
.map(CryptoCurrencyStatus::value)
|
||||
.any { it is CryptoCurrencyStatus.Unreachable }
|
||||
}
|
||||
is TokenList.NotInitialized,
|
||||
null,
|
||||
-> false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
|||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
|
@ -83,7 +84,6 @@ internal class WalletViewModel @Inject constructor(
|
|||
private val selectedAppCurrencyFlow: StateFlow<AppCurrency> = createSelectedAppCurrencyFlow()
|
||||
|
||||
private val notificationsListFactory = WalletNotificationsListFactory(
|
||||
currentStateProvider = Provider { uiState },
|
||||
wasCardScannedCallback = getCardWasScannedUseCase::invoke,
|
||||
isUserAlreadyRateAppCallback = isUserAlreadyRateAppUseCase::invoke,
|
||||
isDemoCardCallback = isDemoCardUseCase::invoke,
|
||||
|
|
@ -148,7 +148,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
when {
|
||||
getWallet(index).isLocked -> uiState = stateFactory.getLockedState()
|
||||
cardTypeResolver.isMultiwalletAllowed() -> updateMultiCurrencyContent(index, isRefreshing)
|
||||
!cardTypeResolver.isMultiwalletAllowed() -> updateSingleCurrencyContent(index)
|
||||
!cardTypeResolver.isMultiwalletAllowed() -> updateSingleCurrencyContent(index, isRefreshing)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -157,7 +157,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
"Impossible to update tokens list if state isn't WalletMultiCurrencyState"
|
||||
}
|
||||
|
||||
getTokenListUseCase(userWalletId = state.walletsListConfig.wallets[index].id)
|
||||
getTokenListUseCase(userWalletId = state.walletsListConfig.wallets[index].id, refresh = isRefreshing)
|
||||
.distinctUntilChanged()
|
||||
.onEach { tokenListEither ->
|
||||
uiState = stateFactory.getStateByTokensList(
|
||||
|
|
@ -175,13 +175,13 @@ internal class WalletViewModel @Inject constructor(
|
|||
.saveIn(tokensJobHolder)
|
||||
}
|
||||
|
||||
private fun updateSingleCurrencyContent(index: Int) {
|
||||
private fun updateSingleCurrencyContent(index: Int, isRefreshing: Boolean) {
|
||||
val wallet = getWallet(index)
|
||||
updateTxHistory(
|
||||
blockchain = getCardTypeResolver(index).getBlockchain(),
|
||||
derivationStyle = wallet.scanResponse.derivationStyleProvider.getDerivationStyle(),
|
||||
)
|
||||
updateMarketPrice(userWalletId = wallet.walletId)
|
||||
updateMarketPrice(userWalletId = wallet.walletId, isRefreshing = isRefreshing)
|
||||
updateNotifications(index)
|
||||
}
|
||||
|
||||
|
|
@ -209,10 +209,16 @@ internal class WalletViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateMarketPrice(userWalletId: UserWalletId) {
|
||||
// It also update wallet balance
|
||||
private fun updateMarketPrice(userWalletId: UserWalletId, isRefreshing: Boolean) {
|
||||
getPrimaryCurrencyUseCase(userWalletId = userWalletId)
|
||||
.distinctUntilChanged()
|
||||
.onEach { uiState = stateFactory.getSingleCurrencyLoadedBalanceState(cryptoCurrencyEither = it) }
|
||||
.onEach {
|
||||
uiState = stateFactory.getSingleCurrencyLoadedBalanceState(
|
||||
cryptoCurrencyEither = it,
|
||||
isRefreshing = isRefreshing,
|
||||
)
|
||||
}
|
||||
.flowOn(dispatchers.io)
|
||||
.launchIn(viewModelScope)
|
||||
.saveIn(marketPriceJobHolder)
|
||||
|
|
@ -336,7 +342,10 @@ internal class WalletViewModel @Inject constructor(
|
|||
val cacheState = WalletStateCache.getState(userWalletId = state.walletsListConfig.wallets[index].id)
|
||||
if (cacheState != null) {
|
||||
uiState = if (cacheState is WalletState.ContentState) {
|
||||
cacheState.copySealed(walletsListConfig = state.walletsListConfig.copy(selectedWalletIndex = index))
|
||||
cacheState.copySealed(
|
||||
walletsListConfig = state.walletsListConfig.copy(selectedWalletIndex = index),
|
||||
pullToRefreshConfig = state.pullToRefreshConfig.copy(isRefreshing = false),
|
||||
)
|
||||
} else {
|
||||
cacheState
|
||||
}
|
||||
|
|
@ -366,19 +375,27 @@ internal class WalletViewModel @Inject constructor(
|
|||
tokensListState is WalletTokensListState.Loading || hasLoadingTokens
|
||||
}
|
||||
is WalletSingleCurrencyState -> {
|
||||
txHistoryState is TxHistoryState.Loading || marketPriceBlockState is MarketPriceBlockState.Loading
|
||||
this is WalletSingleCurrencyState.Content && marketPriceBlockState is MarketPriceBlockState.Loading ||
|
||||
txHistoryState is TxHistoryState.Loading
|
||||
}
|
||||
is WalletState.Initial -> false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRefreshSwipe() {
|
||||
uiState = stateFactory.getStateAfterContentRefreshing()
|
||||
if (uiState is WalletState.Initial || uiState is WalletLockedState) return
|
||||
|
||||
updateContentItems(
|
||||
index = requireNotNull(uiState as? WalletState.ContentState).walletsListConfig.selectedWalletIndex,
|
||||
isRefreshing = true,
|
||||
)
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
uiState = stateFactory.getStateAfterContentRefreshing()
|
||||
|
||||
// TODO: [REDACTED_JIRA]
|
||||
delay(timeMillis = 500)
|
||||
|
||||
updateContentItems(
|
||||
index = requireNotNull(uiState as? WalletState.ContentState).walletsListConfig.selectedWalletIndex,
|
||||
isRefreshing = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOrganizeTokensClick() {
|
||||
|
|
@ -397,6 +414,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
uiState = stateFactory.getStateAfterContentRefreshing()
|
||||
updateSingleCurrencyContent(
|
||||
index = requireNotNull(uiState as? WalletState.ContentState).walletsListConfig.selectedWalletIndex,
|
||||
isRefreshing = true,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue