Updated on 2026-08-14
This commit is contained in:
parent
74e4b644f7
commit
0680fb9e7e
9 changed files with 64 additions and 569 deletions
|
|
@ -1,220 +0,0 @@
|
||||||
package com.tangem.features.txhistory.converter
|
|
||||||
|
|
||||||
import com.tangem.core.ui.components.transactions.state.TransactionState
|
|
||||||
import com.tangem.core.ui.extensions.TextReference
|
|
||||||
import com.tangem.core.ui.extensions.resourceReference
|
|
||||||
import com.tangem.core.ui.extensions.stringReference
|
|
||||||
import com.tangem.core.ui.extensions.wrappedList
|
|
||||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
|
||||||
import com.tangem.core.ui.format.bigdecimal.format
|
|
||||||
import com.tangem.core.ui.utils.toTimeFormat
|
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
|
||||||
import com.tangem.domain.models.network.TxInfo
|
|
||||||
import com.tangem.domain.models.network.TxInfo.TransactionType
|
|
||||||
import com.tangem.features.txhistory.impl.R
|
|
||||||
import com.tangem.features.txhistory.utils.TxHistoryUiActions
|
|
||||||
import com.tangem.utils.StringsSigns
|
|
||||||
import com.tangem.utils.annotations.RemoveWithToggle
|
|
||||||
import com.tangem.utils.converter.Converter
|
|
||||||
import com.tangem.utils.extensions.isZero
|
|
||||||
import com.tangem.utils.toBriefAddressFormat
|
|
||||||
|
|
||||||
@Deprecated("Remove with main toggle [DesignFeatureToggles.isRedesignEnabled]. Produces pre-redesign TransactionState.")
|
|
||||||
@RemoveWithToggle("APP_REDESIGN_ENABLED")
|
|
||||||
internal class TxHistoryItemToTransactionStateConverter(
|
|
||||||
private val currency: CryptoCurrency,
|
|
||||||
private val txHistoryUiActions: TxHistoryUiActions,
|
|
||||||
) : Converter<TxInfo, TransactionState> {
|
|
||||||
override fun convert(value: TxInfo): TransactionState {
|
|
||||||
return TransactionState.Content(
|
|
||||||
txHash = value.txHash,
|
|
||||||
amount = value.getAmount(),
|
|
||||||
time = value.timestampInMillis.toTimeFormat(),
|
|
||||||
status = value.status.toUiStatus(),
|
|
||||||
direction = value.extractDirection(),
|
|
||||||
iconRes = value.extractIcon(),
|
|
||||||
title = value.extractTitle(),
|
|
||||||
subtitle = value.extractSubtitle(),
|
|
||||||
timestamp = value.timestampInMillis,
|
|
||||||
onClick = { txHistoryUiActions.openTxInExplorer(value.txHash) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun TxInfo.extractIcon(): Int = if (status == TxInfo.TransactionStatus.Failed) {
|
|
||||||
R.drawable.ic_close_24
|
|
||||||
} else {
|
|
||||||
when (type) {
|
|
||||||
is TransactionType.YieldSupply.DeployContract,
|
|
||||||
is TransactionType.Approve,
|
|
||||||
-> R.drawable.ic_doc_24
|
|
||||||
is TransactionType.Staking.Stake,
|
|
||||||
is TransactionType.Staking.Vote,
|
|
||||||
is TransactionType.Staking.Restake,
|
|
||||||
-> R.drawable.ic_transaction_history_staking_24
|
|
||||||
is TransactionType.Staking.ClaimRewards,
|
|
||||||
-> R.drawable.ic_transaction_history_claim_rewards_24
|
|
||||||
is TransactionType.Staking.Unstake,
|
|
||||||
is TransactionType.Staking.Withdraw,
|
|
||||||
-> R.drawable.ic_transaction_history_unstaking_24
|
|
||||||
is TransactionType.YieldSupply.Enter -> R.drawable.ic_connect_24
|
|
||||||
is TransactionType.YieldSupply.InitializeToken -> R.drawable.ic_gear_24
|
|
||||||
is TransactionType.YieldSupply.ReactivateToken -> R.drawable.ic_refresh_24
|
|
||||||
is TransactionType.YieldSupply.Exit -> R.drawable.ic_disconnect_24
|
|
||||||
is TransactionType.Operation,
|
|
||||||
is TransactionType.Swap,
|
|
||||||
is TransactionType.Transfer,
|
|
||||||
is TransactionType.UnknownOperation,
|
|
||||||
is TransactionType.YieldSupply.Send,
|
|
||||||
TransactionType.YieldSupply.Topup,
|
|
||||||
TransactionType.GaslessFee,
|
|
||||||
-> if (isOutgoing) R.drawable.ic_arrow_up_24 else R.drawable.ic_arrow_down_24
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("CyclomaticComplexMethod")
|
|
||||||
private fun TxInfo.extractTitle(): TextReference = when (val type = type) {
|
|
||||||
is TransactionType.Approve -> resourceReference(R.string.common_approval)
|
|
||||||
is TransactionType.Operation -> stringReference(type.name)
|
|
||||||
is TransactionType.Swap -> resourceReference(R.string.common_swap)
|
|
||||||
is TransactionType.Transfer -> resourceReference(R.string.common_transfer)
|
|
||||||
is TransactionType.Staking.Stake -> resourceReference(R.string.common_stake)
|
|
||||||
is TransactionType.Staking.Unstake -> resourceReference(R.string.common_unstake)
|
|
||||||
is TransactionType.Staking.Vote -> resourceReference(R.string.staking_vote)
|
|
||||||
is TransactionType.Staking.ClaimRewards -> resourceReference(R.string.common_claim_rewards)
|
|
||||||
is TransactionType.Staking.Withdraw -> resourceReference(R.string.staking_withdraw)
|
|
||||||
is TransactionType.Staking.Restake -> resourceReference(R.string.staking_restake)
|
|
||||||
is TransactionType.YieldSupply -> when (type) {
|
|
||||||
is TransactionType.YieldSupply.Enter -> resourceReference(R.string.yield_module_transaction_enter)
|
|
||||||
is TransactionType.YieldSupply.Exit -> resourceReference(R.string.yield_module_transaction_exit)
|
|
||||||
TransactionType.YieldSupply.Topup -> resourceReference(R.string.yield_module_transaction_topup)
|
|
||||||
is TransactionType.YieldSupply.Send -> {
|
|
||||||
if (type.isYieldSupplyWithdraw || isOutgoing) {
|
|
||||||
resourceReference(R.string.yield_module_transaction_withdraw)
|
|
||||||
} else {
|
|
||||||
resourceReference(R.string.common_transfer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is TransactionType.YieldSupply.DeployContract -> resourceReference(
|
|
||||||
R.string
|
|
||||||
.yield_module_transaction_deploy_contract,
|
|
||||||
)
|
|
||||||
is TransactionType.YieldSupply.InitializeToken -> resourceReference(
|
|
||||||
R.string
|
|
||||||
.yield_module_transaction_initialize,
|
|
||||||
)
|
|
||||||
is TransactionType.YieldSupply.ReactivateToken -> resourceReference(
|
|
||||||
R.string
|
|
||||||
.yield_module_transaction_reactivate,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
is TransactionType.UnknownOperation -> resourceReference(R.string.transaction_history_operation)
|
|
||||||
TransactionType.GaslessFee -> resourceReference(R.string.gasless_transaction_fee)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun TxInfo.extractSubtitle(): TextReference {
|
|
||||||
return when (val type = this.type) {
|
|
||||||
is TransactionType.YieldSupply -> if (currency is CryptoCurrency.Coin) {
|
|
||||||
if (type is TransactionType.YieldSupply.Send) {
|
|
||||||
extractSubtitleByAddressType()
|
|
||||||
} else {
|
|
||||||
resourceReference(
|
|
||||||
R.string.transaction_history_transaction_for_address,
|
|
||||||
wrappedList(type.address?.toBriefAddressFormat().orEmpty()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
when (type) {
|
|
||||||
is TransactionType.YieldSupply.Enter -> {
|
|
||||||
val amount = amount.format { crypto(symbol = currency.symbol, decimals = currency.decimals) }
|
|
||||||
resourceReference(R.string.yield_module_transaction_enter_subtitle, wrappedList(amount))
|
|
||||||
}
|
|
||||||
TransactionType.YieldSupply.Topup -> {
|
|
||||||
val amount = amount.format { crypto(symbol = currency.symbol, decimals = currency.decimals) }
|
|
||||||
resourceReference(R.string.yield_module_transaction_topup_subtitle, wrappedList(amount))
|
|
||||||
}
|
|
||||||
is TransactionType.YieldSupply.Exit -> {
|
|
||||||
val amount = amount.format { crypto(symbol = currency.symbol, decimals = currency.decimals) }
|
|
||||||
resourceReference(R.string.yield_module_transaction_exit_subtitle, wrappedList(amount))
|
|
||||||
}
|
|
||||||
is TransactionType.YieldSupply.Send -> {
|
|
||||||
if (isOutgoing || !type.isYieldSupplyWithdraw) {
|
|
||||||
extractSubtitleByAddressType()
|
|
||||||
} else {
|
|
||||||
val amount =
|
|
||||||
amount.format { crypto(symbol = currency.symbol, decimals = currency.decimals) }
|
|
||||||
resourceReference(
|
|
||||||
R.string.yield_module_transaction_exit_subtitle,
|
|
||||||
wrappedList(amount),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> extractSubtitleByAddressType()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> extractSubtitleByAddressType()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun TxInfo.extractSubtitleByAddressType(): TextReference =
|
|
||||||
when (val interactionAddress = interactionAddressType) {
|
|
||||||
is TxInfo.InteractionAddressType.Contract -> resourceReference(
|
|
||||||
id = R.string.transaction_history_contract_address,
|
|
||||||
formatArgs = wrappedList(interactionAddress.address.toBriefAddressFormat()),
|
|
||||||
)
|
|
||||||
is TxInfo.InteractionAddressType.Multiple -> resourceReference(
|
|
||||||
id = if (isOutgoing) {
|
|
||||||
R.string.transaction_history_transaction_to_address
|
|
||||||
} else {
|
|
||||||
R.string.transaction_history_transaction_from_address
|
|
||||||
},
|
|
||||||
formatArgs = wrappedList(resourceReference(R.string.transaction_history_multiple_addresses)),
|
|
||||||
)
|
|
||||||
is TxInfo.InteractionAddressType.User -> resourceReference(
|
|
||||||
id = if (isOutgoing) {
|
|
||||||
R.string.transaction_history_transaction_to_address
|
|
||||||
} else {
|
|
||||||
R.string.transaction_history_transaction_from_address
|
|
||||||
},
|
|
||||||
formatArgs = wrappedList(interactionAddress.address.toBriefAddressFormat()),
|
|
||||||
)
|
|
||||||
is TxInfo.InteractionAddressType.Validator -> resourceReference(
|
|
||||||
id = R.string.transaction_history_transaction_validator,
|
|
||||||
formatArgs = wrappedList(interactionAddress.address.toBriefAddressFormat()),
|
|
||||||
)
|
|
||||||
null -> {
|
|
||||||
TextReference.EMPTY
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun TxInfo.extractDirection() =
|
|
||||||
if (isOutgoing) TransactionState.Content.Direction.OUTGOING else TransactionState.Content.Direction.INCOMING
|
|
||||||
|
|
||||||
@Suppress("ComplexCondition")
|
|
||||||
private fun TxInfo.getAmount(): String {
|
|
||||||
when (type) {
|
|
||||||
is TransactionType.Staking.Vote,
|
|
||||||
TransactionType.Staking.ClaimRewards,
|
|
||||||
TransactionType.Staking.Withdraw,
|
|
||||||
-> return ""
|
|
||||||
|
|
||||||
is TransactionType.YieldSupply -> {
|
|
||||||
if (currency is CryptoCurrency.Token && type == TransactionType.YieldSupply.Send && !isOutgoing) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> Unit
|
|
||||||
}
|
|
||||||
val prefix = when {
|
|
||||||
status == TxInfo.TransactionStatus.Failed -> ""
|
|
||||||
this.amount.isZero() -> ""
|
|
||||||
else -> if (isOutgoing) StringsSigns.MINUS else StringsSigns.PLUS
|
|
||||||
}
|
|
||||||
return prefix + amount.format { crypto(symbol = currency.symbol, decimals = currency.decimals) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun TxInfo.TransactionStatus.toUiStatus() = when (this) {
|
|
||||||
TxInfo.TransactionStatus.Confirmed -> TransactionState.Content.Status.Confirmed
|
|
||||||
TxInfo.TransactionStatus.Failed -> TransactionState.Content.Status.Failed
|
|
||||||
TxInfo.TransactionStatus.Unconfirmed -> TransactionState.Content.Status.Unconfirmed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -6,7 +6,6 @@ import com.tangem.core.decompose.di.ModelScoped
|
||||||
import com.tangem.core.decompose.model.Model
|
import com.tangem.core.decompose.model.Model
|
||||||
import com.tangem.core.decompose.model.ParamsContainer
|
import com.tangem.core.decompose.model.ParamsContainer
|
||||||
import com.tangem.core.navigation.url.UrlOpener
|
import com.tangem.core.navigation.url.UrlOpener
|
||||||
import com.tangem.core.ui.DesignFeatureToggles
|
|
||||||
import com.tangem.core.ui.utils.toDateFormatWithTodayYesterday
|
import com.tangem.core.ui.utils.toDateFormatWithTodayYesterday
|
||||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||||
import com.tangem.domain.account.status.utils.CryptoCurrencyStatusOperations.getCryptoCurrencyStatus
|
import com.tangem.domain.account.status.utils.CryptoCurrencyStatusOperations.getCryptoCurrencyStatus
|
||||||
|
|
@ -27,7 +26,6 @@ import com.tangem.features.txhistory.component.TxHistoryComponent
|
||||||
import com.tangem.features.txhistory.converter.ExpressTxToTransactionItemUMConverter
|
import com.tangem.features.txhistory.converter.ExpressTxToTransactionItemUMConverter
|
||||||
import com.tangem.features.txhistory.converter.TxHistoryInfoToTransactionItemUMConverter
|
import com.tangem.features.txhistory.converter.TxHistoryInfoToTransactionItemUMConverter
|
||||||
import com.tangem.features.txhistory.converter.TxHistoryItemToTransactionItemUMConverter
|
import com.tangem.features.txhistory.converter.TxHistoryItemToTransactionItemUMConverter
|
||||||
import com.tangem.features.txhistory.converter.TxHistoryItemToTransactionStateConverter
|
|
||||||
import com.tangem.features.txhistory.entity.TxHistoryItemsUM
|
import com.tangem.features.txhistory.entity.TxHistoryItemsUM
|
||||||
import com.tangem.features.txhistory.entity.TxHistoryUpdateListener
|
import com.tangem.features.txhistory.entity.TxHistoryUpdateListener
|
||||||
import com.tangem.features.txhistory.state.TxHistoryItemsSnapshot
|
import com.tangem.features.txhistory.state.TxHistoryItemsSnapshot
|
||||||
|
|
@ -56,7 +54,6 @@ internal class TxHistoryModel @Inject constructor(
|
||||||
private val urlOpener: UrlOpener,
|
private val urlOpener: UrlOpener,
|
||||||
private val txHistoryUpdateListener: TxHistoryUpdateListener,
|
private val txHistoryUpdateListener: TxHistoryUpdateListener,
|
||||||
private val stateController: TxHistoryStateController,
|
private val stateController: TxHistoryStateController,
|
||||||
private val designFeatureToggles: DesignFeatureToggles,
|
|
||||||
private val txHistoryFeatureToggle: TxHistoryFeatureToggles,
|
private val txHistoryFeatureToggle: TxHistoryFeatureToggles,
|
||||||
private val historyTxListManagerFactory: HistoryTxListManager.Factory,
|
private val historyTxListManagerFactory: HistoryTxListManager.Factory,
|
||||||
private val appTxHistoryFetcher: AppTxHistoryFetcher,
|
private val appTxHistoryFetcher: AppTxHistoryFetcher,
|
||||||
|
|
@ -67,17 +64,9 @@ internal class TxHistoryModel @Inject constructor(
|
||||||
|
|
||||||
private val params: TxHistoryComponent.Params = paramsContainer.require()
|
private val params: TxHistoryComponent.Params = paramsContainer.require()
|
||||||
|
|
||||||
private val lookupDataFlow: Flow<TxHistoryLookupContext> = if (designFeatureToggles.isRedesignEnabled) {
|
private val lookupDataFlow: Flow<TxHistoryLookupContext> = ownerLookupProducer()
|
||||||
ownerLookupProducer()
|
.flowOn(dispatchers.default)
|
||||||
.flowOn(dispatchers.default)
|
.shareIn(modelScope, SharingStarted.WhileSubscribed(), replay = 1)
|
||||||
.shareIn(modelScope, SharingStarted.WhileSubscribed(), replay = 1)
|
|
||||||
} else {
|
|
||||||
emptyFlow()
|
|
||||||
}
|
|
||||||
|
|
||||||
@RemoveWithToggle("APP_REDESIGN_ENABLED")
|
|
||||||
private val legacyTxHistoryItemConverter =
|
|
||||||
TxHistoryItemToTransactionStateConverter(currency = params.currency, txHistoryUiActions = this)
|
|
||||||
|
|
||||||
@RemoveWithToggle("AND_15767_NEW_TX_HISTORY_ENABLED")
|
@RemoveWithToggle("AND_15767_NEW_TX_HISTORY_ENABLED")
|
||||||
private val txHistoryListManager: TxHistoryListManager? = if (!txHistoryFeatureToggle.isNewTxHistoryEnabled) {
|
private val txHistoryListManager: TxHistoryListManager? = if (!txHistoryFeatureToggle.isNewTxHistoryEnabled) {
|
||||||
|
|
@ -86,10 +75,8 @@ internal class TxHistoryModel @Inject constructor(
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
userWalletId = params.userWalletId,
|
userWalletId = params.userWalletId,
|
||||||
currency = params.currency,
|
currency = params.currency,
|
||||||
designFeatureToggles = designFeatureToggles,
|
|
||||||
txHistoryUiActions = this,
|
txHistoryUiActions = this,
|
||||||
lookupDataFlow = lookupDataFlow,
|
lookupDataFlow = lookupDataFlow,
|
||||||
legacyTxHistoryItemConverter = legacyTxHistoryItemConverter,
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
|
|
@ -297,11 +284,7 @@ internal class TxHistoryModel @Inject constructor(
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
|
|
||||||
val combined: Flow<Pair<Option<CryptoCurrencyStatus>, TxHistoryLookupContext?>> =
|
val combined: Flow<Pair<Option<CryptoCurrencyStatus>, TxHistoryLookupContext?>> =
|
||||||
if (designFeatureToggles.isRedesignEnabled) {
|
combine(statusFlow, lookupDataFlow) { status, lookup -> status to lookup }
|
||||||
combine(statusFlow, lookupDataFlow) { status, lookup -> status to lookup }
|
|
||||||
} else {
|
|
||||||
statusFlow.map { it to null }
|
|
||||||
}
|
|
||||||
|
|
||||||
combined
|
combined
|
||||||
.onEach { (status, lookup) -> handlePendingTxsChanges(status, lookup) }
|
.onEach { (status, lookup) -> handlePendingTxsChanges(status, lookup) }
|
||||||
|
|
@ -324,7 +307,6 @@ internal class TxHistoryModel @Inject constructor(
|
||||||
)
|
)
|
||||||
pending.map(converter::convert).toPersistentList()
|
pending.map(converter::convert).toPersistentList()
|
||||||
},
|
},
|
||||||
legacyPendingTxs = { pending.map(legacyTxHistoryItemConverter::convert).toPersistentList() },
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,12 @@
|
||||||
package com.tangem.features.txhistory.state
|
package com.tangem.features.txhistory.state
|
||||||
|
|
||||||
import com.tangem.features.txhistory.entity.TxHistoryItemsUM
|
import com.tangem.features.txhistory.entity.TxHistoryItemsUM
|
||||||
import com.tangem.features.txhistory.entity.TxHistoryUM
|
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Snapshot of transaction history items emitted by [TxHistoryListManager]. Wraps either the
|
* Snapshot of transaction history items emitted by [TxHistoryListManager].
|
||||||
* primary or legacy item list so that one [Flow] can carry both pipelines, with the active
|
|
||||||
* variant chosen via the design feature toggle.
|
|
||||||
*/
|
*/
|
||||||
internal sealed interface TxHistoryItemsSnapshot {
|
internal sealed interface TxHistoryItemsSnapshot {
|
||||||
|
|
||||||
data class Items(val items: ImmutableList<TxHistoryItemsUM.TxHistoryItemUM>) : TxHistoryItemsSnapshot
|
data class Items(val items: ImmutableList<TxHistoryItemsUM.TxHistoryItemUM>) : TxHistoryItemsSnapshot
|
||||||
|
|
||||||
data class LegacyItems(val items: ImmutableList<TxHistoryUM.TxHistoryItemUM>) : TxHistoryItemsSnapshot
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,113 +1,71 @@
|
||||||
package com.tangem.features.txhistory.state
|
package com.tangem.features.txhistory.state
|
||||||
|
|
||||||
import com.tangem.core.decompose.di.ModelScoped
|
import com.tangem.core.decompose.di.ModelScoped
|
||||||
import com.tangem.core.ui.DesignFeatureToggles
|
|
||||||
import com.tangem.core.ui.components.transactions.state.TransactionItemUM
|
import com.tangem.core.ui.components.transactions.state.TransactionItemUM
|
||||||
import com.tangem.core.ui.components.transactions.state.TransactionState
|
|
||||||
import com.tangem.features.txhistory.entity.TxHistoryItemsUM
|
import com.tangem.features.txhistory.entity.TxHistoryItemsUM
|
||||||
import com.tangem.features.txhistory.entity.TxHistoryUM
|
import com.tangem.features.txhistory.entity.TxHistoryUM
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Owns the transaction history UI state and routes updates to either [legacyUiState] or
|
* Owns the transaction history UI state and routes updates to [uiState].
|
||||||
* [uiState] based on [DesignFeatureToggles.isRedesignEnabled]. Only the active pipeline gets
|
|
||||||
* emitted to; the inactive flow stays at its initial Loading value.
|
|
||||||
*/
|
*/
|
||||||
@ModelScoped
|
@ModelScoped
|
||||||
internal class TxHistoryStateController @Inject constructor(
|
internal class TxHistoryStateController @Inject constructor() {
|
||||||
private val designFeatureToggles: DesignFeatureToggles,
|
|
||||||
) {
|
|
||||||
|
|
||||||
private val _legacyUiState: MutableStateFlow<TxHistoryUM> =
|
/**
|
||||||
MutableStateFlow(TxHistoryUM.Loading(isBalanceHidden = true, onExploreClick = {}))
|
* Pre-redesign state. Kept only to satisfy consumers that still read the legacy UI (they are
|
||||||
val legacyUiState: StateFlow<TxHistoryUM> = _legacyUiState
|
* dead at runtime while the redesign is enabled); no longer populated.
|
||||||
|
*/
|
||||||
|
val legacyUiState: StateFlow<TxHistoryUM> =
|
||||||
|
MutableStateFlow(TxHistoryUM.Loading(isBalanceHidden = true, onExploreClick = {})).asStateFlow()
|
||||||
|
|
||||||
private val _uiState: MutableStateFlow<TxHistoryItemsUM> =
|
private val _uiState: MutableStateFlow<TxHistoryItemsUM> =
|
||||||
MutableStateFlow(TxHistoryItemsUM.Loading(isBalanceHidden = true, onExploreClick = {}))
|
MutableStateFlow(TxHistoryItemsUM.Loading(isBalanceHidden = true, onExploreClick = {}))
|
||||||
val uiState: StateFlow<TxHistoryItemsUM> = _uiState
|
val uiState: StateFlow<TxHistoryItemsUM> = _uiState
|
||||||
|
|
||||||
val isNotSupported: Boolean
|
val isNotSupported: Boolean
|
||||||
get() = if (designFeatureToggles.isRedesignEnabled) {
|
get() = _uiState.value is TxHistoryItemsUM.NotSupported
|
||||||
_uiState.value is TxHistoryItemsUM.NotSupported
|
|
||||||
} else {
|
|
||||||
_legacyUiState.value is TxHistoryUM.NotSupported
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setLoading(isBalanceHidden: Boolean, onExploreClick: () -> Unit) {
|
fun setLoading(isBalanceHidden: Boolean, onExploreClick: () -> Unit) {
|
||||||
if (designFeatureToggles.isRedesignEnabled) {
|
_uiState.value = TxHistoryItemsUM.Loading(
|
||||||
_uiState.value = TxHistoryItemsUM.Loading(
|
isBalanceHidden = isBalanceHidden,
|
||||||
isBalanceHidden = isBalanceHidden,
|
onExploreClick = onExploreClick,
|
||||||
onExploreClick = onExploreClick,
|
)
|
||||||
)
|
|
||||||
} else {
|
|
||||||
_legacyUiState.value = TxHistoryUM.Loading(
|
|
||||||
isBalanceHidden = isBalanceHidden,
|
|
||||||
onExploreClick = onExploreClick,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setLoadingIfNotContent(onExploreClick: () -> Unit) {
|
fun setLoadingIfNotContent(onExploreClick: () -> Unit) {
|
||||||
if (designFeatureToggles.isRedesignEnabled) {
|
_uiState.update { state ->
|
||||||
_uiState.update { state ->
|
state as? TxHistoryItemsUM.Content ?: TxHistoryItemsUM.Loading(state.isBalanceHidden, onExploreClick)
|
||||||
state as? TxHistoryItemsUM.Content ?: TxHistoryItemsUM.Loading(state.isBalanceHidden, onExploreClick)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_legacyUiState.update { state ->
|
|
||||||
state as? TxHistoryUM.Content ?: TxHistoryUM.Loading(state.isBalanceHidden, onExploreClick)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setError(onReloadClick: () -> Unit, onExploreClick: () -> Unit) {
|
fun setError(onReloadClick: () -> Unit, onExploreClick: () -> Unit) {
|
||||||
if (designFeatureToggles.isRedesignEnabled) {
|
_uiState.value = TxHistoryItemsUM.Error(
|
||||||
_uiState.value = TxHistoryItemsUM.Error(
|
isBalanceHidden = _uiState.value.isBalanceHidden,
|
||||||
isBalanceHidden = _uiState.value.isBalanceHidden,
|
onReloadClick = onReloadClick,
|
||||||
onReloadClick = onReloadClick,
|
onExploreClick = onExploreClick,
|
||||||
onExploreClick = onExploreClick,
|
)
|
||||||
)
|
|
||||||
} else {
|
|
||||||
_legacyUiState.value = TxHistoryUM.Error(
|
|
||||||
isBalanceHidden = _legacyUiState.value.isBalanceHidden,
|
|
||||||
onReloadClick = onReloadClick,
|
|
||||||
onExploreClick = onExploreClick,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setEmpty(onExploreClick: () -> Unit) {
|
fun setEmpty(onExploreClick: () -> Unit) {
|
||||||
if (designFeatureToggles.isRedesignEnabled) {
|
_uiState.value = TxHistoryItemsUM.Empty(
|
||||||
_uiState.value = TxHistoryItemsUM.Empty(
|
isBalanceHidden = _uiState.value.isBalanceHidden,
|
||||||
isBalanceHidden = _uiState.value.isBalanceHidden,
|
onExploreClick = onExploreClick,
|
||||||
onExploreClick = onExploreClick,
|
)
|
||||||
)
|
|
||||||
} else {
|
|
||||||
_legacyUiState.value = TxHistoryUM.Empty(
|
|
||||||
isBalanceHidden = _legacyUiState.value.isBalanceHidden,
|
|
||||||
onExploreClick = onExploreClick,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setNotSupported(onExploreClick: () -> Unit) {
|
fun setNotSupported(onExploreClick: () -> Unit) {
|
||||||
if (designFeatureToggles.isRedesignEnabled) {
|
_uiState.value = TxHistoryItemsUM.NotSupported(
|
||||||
_uiState.value = TxHistoryItemsUM.NotSupported(
|
isBalanceHidden = _uiState.value.isBalanceHidden,
|
||||||
isBalanceHidden = _uiState.value.isBalanceHidden,
|
pendingTransactions = persistentListOf(),
|
||||||
pendingTransactions = persistentListOf(),
|
onExploreClick = onExploreClick,
|
||||||
onExploreClick = onExploreClick,
|
)
|
||||||
)
|
|
||||||
} else {
|
|
||||||
_legacyUiState.value = TxHistoryUM.NotSupported(
|
|
||||||
isBalanceHidden = _legacyUiState.value.isBalanceHidden,
|
|
||||||
pendingTransactions = persistentListOf(),
|
|
||||||
onExploreClick = onExploreClick,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setContent(snapshot: TxHistoryItemsSnapshot, loadMore: () -> Boolean, onExploreClick: () -> Unit) {
|
fun setContent(snapshot: TxHistoryItemsSnapshot, loadMore: () -> Boolean, onExploreClick: () -> Unit) {
|
||||||
|
|
@ -129,27 +87,10 @@ internal class TxHistoryStateController @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is TxHistoryItemsSnapshot.LegacyItems -> _legacyUiState.update { state ->
|
|
||||||
if (snapshot.items.none { it is TxHistoryUM.TxHistoryItemUM.Transaction }) {
|
|
||||||
TxHistoryUM.Empty(
|
|
||||||
isBalanceHidden = state.isBalanceHidden,
|
|
||||||
onExploreClick = onExploreClick,
|
|
||||||
)
|
|
||||||
} else if (state is TxHistoryUM.Content) {
|
|
||||||
state.copy(items = snapshot.items)
|
|
||||||
} else {
|
|
||||||
TxHistoryUM.Content(
|
|
||||||
items = snapshot.items,
|
|
||||||
isBalanceHidden = state.isBalanceHidden,
|
|
||||||
loadMore = loadMore,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateLoadingMore(isLoadingMore: Boolean) {
|
fun updateLoadingMore(isLoadingMore: Boolean) {
|
||||||
if (!designFeatureToggles.isRedesignEnabled) return
|
|
||||||
_uiState.update { state ->
|
_uiState.update { state ->
|
||||||
if (state is TxHistoryItemsUM.Content && state.isLoadingMore != isLoadingMore) {
|
if (state is TxHistoryItemsUM.Content && state.isLoadingMore != isLoadingMore) {
|
||||||
state.copy(isLoadingMore = isLoadingMore)
|
state.copy(isLoadingMore = isLoadingMore)
|
||||||
|
|
@ -160,32 +101,15 @@ internal class TxHistoryStateController @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateBalanceHidden(isBalanceHidden: Boolean) {
|
fun updateBalanceHidden(isBalanceHidden: Boolean) {
|
||||||
if (designFeatureToggles.isRedesignEnabled) {
|
_uiState.update { state -> state.copySealed(isBalanceHidden = isBalanceHidden) }
|
||||||
_uiState.update { state -> state.copySealed(isBalanceHidden = isBalanceHidden) }
|
|
||||||
} else {
|
|
||||||
_legacyUiState.update { state -> state.copySealed(isBalanceHidden = isBalanceHidden) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updatePendingTransactions(
|
fun updatePendingTransactions(pendingTxs: () -> ImmutableList<TransactionItemUM>) {
|
||||||
pendingTxs: () -> ImmutableList<TransactionItemUM>,
|
_uiState.update { state ->
|
||||||
legacyPendingTxs: () -> ImmutableList<TransactionState>,
|
if (state is TxHistoryItemsUM.NotSupported) {
|
||||||
) {
|
state.copy(pendingTransactions = pendingTxs())
|
||||||
if (designFeatureToggles.isRedesignEnabled) {
|
} else {
|
||||||
_uiState.update { state ->
|
state
|
||||||
if (state is TxHistoryItemsUM.NotSupported) {
|
|
||||||
state.copy(pendingTransactions = pendingTxs())
|
|
||||||
} else {
|
|
||||||
state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_legacyUiState.update { state ->
|
|
||||||
if (state is TxHistoryUM.NotSupported) {
|
|
||||||
state.copy(pendingTransactions = legacyPendingTxs())
|
|
||||||
} else {
|
|
||||||
state
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
package com.tangem.features.txhistory.utils
|
|
||||||
|
|
||||||
import com.tangem.core.ui.utils.toDateFormatWithTodayYesterday
|
|
||||||
import com.tangem.domain.models.network.TxInfo
|
|
||||||
import com.tangem.domain.txhistory.models.PaginationWrapper
|
|
||||||
import com.tangem.features.txhistory.converter.TxHistoryItemToTransactionStateConverter
|
|
||||||
import com.tangem.features.txhistory.entity.TxHistoryUM
|
|
||||||
import com.tangem.pagination.Batch
|
|
||||||
import com.tangem.pagination.PaginationStatus
|
|
||||||
import com.tangem.utils.annotations.RemoveWithToggle
|
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
||||||
import kotlinx.coroutines.flow.*
|
|
||||||
|
|
||||||
@Deprecated("Remove with main toggle [DesignFeatureToggles.isRedesignEnabled]. Renders pre-redesign tx-history UI.")
|
|
||||||
@RemoveWithToggle("APP_REDESIGN_ENABLED")
|
|
||||||
internal class TxHistoryLegacyUiManager(
|
|
||||||
private val state: MutableStateFlow<TxHistoryListState>,
|
|
||||||
private val txHistoryItemConverter: TxHistoryItemToTransactionStateConverter,
|
|
||||||
private val txHistoryUiActions: TxHistoryUiActions,
|
|
||||||
) {
|
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
|
||||||
val items: Flow<ImmutableList<TxHistoryUM.TxHistoryItemUM>> = state
|
|
||||||
.filter { state ->
|
|
||||||
state.status !is PaginationStatus.None &&
|
|
||||||
state.status !is PaginationStatus.InitialLoading &&
|
|
||||||
state.status !is PaginationStatus.InitialLoadingError
|
|
||||||
}
|
|
||||||
.mapLatest { state ->
|
|
||||||
state.legacyUiBatches.asSequence()
|
|
||||||
.flatMap { it.data }
|
|
||||||
.toImmutableList()
|
|
||||||
}
|
|
||||||
.distinctUntilChanged()
|
|
||||||
|
|
||||||
fun createOrUpdateUiBatches(
|
|
||||||
newCurrencyBatches: List<Batch<Int, PaginationWrapper<TxInfo>>>,
|
|
||||||
shouldClearUiBatches: Boolean,
|
|
||||||
): List<Batch<Int, List<TxHistoryUM.TxHistoryItemUM>>> {
|
|
||||||
val currentUiBatches = state.value.legacyUiBatches
|
|
||||||
val batches = if (shouldClearUiBatches) mutableListOf() else currentUiBatches.toMutableList()
|
|
||||||
|
|
||||||
for ((key, data) in newCurrencyBatches) {
|
|
||||||
val existingBatchIndex = batches.indexOfFirst { it.key == key }
|
|
||||||
if (existingBatchIndex == -1) {
|
|
||||||
val items = generateUiItems(key, data)
|
|
||||||
batches.add(Batch(key = key, data = items))
|
|
||||||
} else if (currentUiBatches[existingBatchIndex].data.transactionItemsSizeNotEqual(data.items)) {
|
|
||||||
val items = generateUiItems(key, data)
|
|
||||||
batches[existingBatchIndex] = Batch(key = key, data = items)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return batches
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateUiItems(key: Int, data: PaginationWrapper<TxInfo>): List<TxHistoryUM.TxHistoryItemUM> {
|
|
||||||
val items = mutableListOf<TxHistoryUM.TxHistoryItemUM>()
|
|
||||||
|
|
||||||
if (key == 0) {
|
|
||||||
items.add(TxHistoryUM.TxHistoryItemUM.Title(onExploreClick = txHistoryUiActions::openExplorer))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.items.isNotEmpty()) {
|
|
||||||
val firstItem = data.items.first()
|
|
||||||
val firstDate = firstItem.timestampInMillis.toDateFormatWithTodayYesterday()
|
|
||||||
|
|
||||||
items.add(
|
|
||||||
TxHistoryUM.TxHistoryItemUM.GroupTitle(
|
|
||||||
title = firstDate,
|
|
||||||
itemKey = "$key-$firstDate",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
items.add(TxHistoryUM.TxHistoryItemUM.Transaction(txHistoryItemConverter.convert(firstItem)))
|
|
||||||
|
|
||||||
data.items.zipWithNext { current, next ->
|
|
||||||
val currentDate = current.timestampInMillis.toDateFormatWithTodayYesterday()
|
|
||||||
val nextDate = next.timestampInMillis.toDateFormatWithTodayYesterday()
|
|
||||||
|
|
||||||
if (currentDate != nextDate) {
|
|
||||||
items.add(
|
|
||||||
TxHistoryUM.TxHistoryItemUM.GroupTitle(
|
|
||||||
title = nextDate,
|
|
||||||
itemKey = "$key-$nextDate",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
items.add(TxHistoryUM.TxHistoryItemUM.Transaction(txHistoryItemConverter.convert(next)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return items
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun List<TxHistoryUM.TxHistoryItemUM>.transactionItemsSizeNotEqual(txInfos: List<TxInfo>): Boolean {
|
|
||||||
return this.filterIsInstance<TxHistoryUM.TxHistoryItemUM.Transaction>().size != txInfos.size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package com.tangem.features.txhistory.utils
|
package com.tangem.features.txhistory.utils
|
||||||
|
|
||||||
import com.tangem.core.ui.DesignFeatureToggles
|
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.network.TxInfo
|
import com.tangem.domain.models.network.TxInfo
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
|
@ -9,7 +8,6 @@ import com.tangem.domain.txhistory.model.TxHistoryListConfig
|
||||||
import com.tangem.domain.txhistory.models.PaginationWrapper
|
import com.tangem.domain.txhistory.models.PaginationWrapper
|
||||||
import com.tangem.domain.txhistory.repository.TxHistoryRepositoryV2
|
import com.tangem.domain.txhistory.repository.TxHistoryRepositoryV2
|
||||||
import com.tangem.features.txhistory.converter.TxHistoryItemToTransactionItemUMConverter
|
import com.tangem.features.txhistory.converter.TxHistoryItemToTransactionItemUMConverter
|
||||||
import com.tangem.features.txhistory.converter.TxHistoryItemToTransactionStateConverter
|
|
||||||
import com.tangem.features.txhistory.model.TxHistoryLookupContext
|
import com.tangem.features.txhistory.model.TxHistoryLookupContext
|
||||||
import com.tangem.features.txhistory.state.TxHistoryItemsSnapshot
|
import com.tangem.features.txhistory.state.TxHistoryItemsSnapshot
|
||||||
import com.tangem.pagination.BatchAction
|
import com.tangem.pagination.BatchAction
|
||||||
|
|
@ -34,10 +32,8 @@ internal class TxHistoryListManager(
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
private val userWalletId: UserWalletId,
|
private val userWalletId: UserWalletId,
|
||||||
private val currency: CryptoCurrency,
|
private val currency: CryptoCurrency,
|
||||||
private val designFeatureToggles: DesignFeatureToggles,
|
|
||||||
private val txHistoryUiActions: TxHistoryUiActions,
|
private val txHistoryUiActions: TxHistoryUiActions,
|
||||||
private val lookupDataFlow: Flow<TxHistoryLookupContext>,
|
private val lookupDataFlow: Flow<TxHistoryLookupContext>,
|
||||||
legacyTxHistoryItemConverter: TxHistoryItemToTransactionStateConverter,
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val jobHolder = JobHolder()
|
private val jobHolder = JobHolder()
|
||||||
|
|
@ -48,17 +44,8 @@ internal class TxHistoryListManager(
|
||||||
)
|
)
|
||||||
private val state: MutableStateFlow<TxHistoryListState> = MutableStateFlow(TxHistoryListState())
|
private val state: MutableStateFlow<TxHistoryListState> = MutableStateFlow(TxHistoryListState())
|
||||||
private val uiManager = TxHistoryUiManager(state = state)
|
private val uiManager = TxHistoryUiManager(state = state)
|
||||||
private val legacyUiManager = TxHistoryLegacyUiManager(
|
|
||||||
state = state,
|
|
||||||
txHistoryItemConverter = legacyTxHistoryItemConverter,
|
|
||||||
txHistoryUiActions = txHistoryUiActions,
|
|
||||||
)
|
|
||||||
|
|
||||||
val uiItems: Flow<TxHistoryItemsSnapshot> = if (designFeatureToggles.isRedesignEnabled) {
|
val uiItems: Flow<TxHistoryItemsSnapshot> = uiManager.items.map(TxHistoryItemsSnapshot::Items)
|
||||||
uiManager.items.map(TxHistoryItemsSnapshot::Items)
|
|
||||||
} else {
|
|
||||||
legacyUiManager.items.map(TxHistoryItemsSnapshot::LegacyItems)
|
|
||||||
}
|
|
||||||
val paginationStatus: Flow<PaginationStatus<*>> = state.map { it.status }.distinctUntilChanged()
|
val paginationStatus: Flow<PaginationStatus<*>> = state.map { it.status }.distinctUntilChanged()
|
||||||
|
|
||||||
suspend fun init() = coroutineScope {
|
suspend fun init() = coroutineScope {
|
||||||
|
|
@ -76,24 +63,16 @@ internal class TxHistoryListManager(
|
||||||
.launchIn(scope = this)
|
.launchIn(scope = this)
|
||||||
.saveIn(autoLoadMoreJobHolder)
|
.saveIn(autoLoadMoreJobHolder)
|
||||||
|
|
||||||
if (designFeatureToggles.isRedesignEnabled) {
|
var previousLookup: TxHistoryLookupContext? = null
|
||||||
var previousLookup: TxHistoryLookupContext? = null
|
combine(batchFlow.state, lookupDataFlow) { batchState, lookup -> batchState to lookup }
|
||||||
combine(batchFlow.state, lookupDataFlow) { batchState, lookup -> batchState to lookup }
|
.onEach { (batchState, lookup) ->
|
||||||
.onEach { (batchState, lookup) ->
|
val isLookupChanged = previousLookup != null && previousLookup != lookup
|
||||||
val isLookupChanged = previousLookup != null && previousLookup != lookup
|
previousLookup = lookup
|
||||||
previousLookup = lookup
|
updateState(batchState, lookup, isLookupChanged)
|
||||||
updateState(batchState, lookup, isLookupChanged)
|
}
|
||||||
}
|
.flowOn(dispatchers.default)
|
||||||
.flowOn(dispatchers.default)
|
.launchIn(scope = this)
|
||||||
.launchIn(scope = this)
|
.saveIn(jobHolder)
|
||||||
.saveIn(jobHolder)
|
|
||||||
} else {
|
|
||||||
batchFlow.state
|
|
||||||
.onEach { batchState -> updateState(batchState, lookupContext = null, isLookupChanged = false) }
|
|
||||||
.flowOn(dispatchers.default)
|
|
||||||
.launchIn(scope = this)
|
|
||||||
.saveIn(jobHolder)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun startLoading() {
|
suspend fun startLoading() {
|
||||||
|
|
@ -129,32 +108,19 @@ internal class TxHistoryListManager(
|
||||||
val isInitialToPaginating = state.status is PaginationStatus.InitialLoading &&
|
val isInitialToPaginating = state.status is PaginationStatus.InitialLoading &&
|
||||||
batchListState.status is PaginationStatus.Paginating
|
batchListState.status is PaginationStatus.Paginating
|
||||||
val shouldClearUiBatches = isInitialToPaginating || isLookupChanged
|
val shouldClearUiBatches = isInitialToPaginating || isLookupChanged
|
||||||
val isRedesignEnabled = designFeatureToggles.isRedesignEnabled
|
val converter = TxHistoryItemToTransactionItemUMConverter(
|
||||||
|
currency = currency,
|
||||||
|
txHistoryUiActions = txHistoryUiActions,
|
||||||
|
lookupContext = lookupContext,
|
||||||
|
)
|
||||||
state.copy(
|
state.copy(
|
||||||
status = batchListState.status,
|
status = batchListState.status,
|
||||||
rawBatches = batchListState.data,
|
rawBatches = batchListState.data,
|
||||||
uiBatches = if (isRedesignEnabled) {
|
uiBatches = uiManager.createOrUpdateUiBatches(
|
||||||
val converter = TxHistoryItemToTransactionItemUMConverter(
|
newCurrencyBatches = batchListState.data,
|
||||||
currency = currency,
|
shouldClearUiBatches = shouldClearUiBatches,
|
||||||
txHistoryUiActions = txHistoryUiActions,
|
converter = converter,
|
||||||
lookupContext = lookupContext,
|
),
|
||||||
)
|
|
||||||
uiManager.createOrUpdateUiBatches(
|
|
||||||
newCurrencyBatches = batchListState.data,
|
|
||||||
shouldClearUiBatches = shouldClearUiBatches,
|
|
||||||
converter = converter,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
state.uiBatches
|
|
||||||
},
|
|
||||||
legacyUiBatches = if (isRedesignEnabled) {
|
|
||||||
state.legacyUiBatches
|
|
||||||
} else {
|
|
||||||
legacyUiManager.createOrUpdateUiBatches(
|
|
||||||
newCurrencyBatches = batchListState.data,
|
|
||||||
shouldClearUiBatches = shouldClearUiBatches,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package com.tangem.features.txhistory.utils
|
||||||
import com.tangem.domain.models.network.TxInfo
|
import com.tangem.domain.models.network.TxInfo
|
||||||
import com.tangem.domain.txhistory.models.PaginationWrapper
|
import com.tangem.domain.txhistory.models.PaginationWrapper
|
||||||
import com.tangem.features.txhistory.entity.TxHistoryItemsUM
|
import com.tangem.features.txhistory.entity.TxHistoryItemsUM
|
||||||
import com.tangem.features.txhistory.entity.TxHistoryUM
|
|
||||||
import com.tangem.pagination.Batch
|
import com.tangem.pagination.Batch
|
||||||
import com.tangem.pagination.PaginationStatus
|
import com.tangem.pagination.PaginationStatus
|
||||||
import com.tangem.utils.annotations.RemoveWithToggle
|
import com.tangem.utils.annotations.RemoveWithToggle
|
||||||
|
|
@ -14,5 +13,4 @@ internal data class TxHistoryListState(
|
||||||
val status: PaginationStatus<*> = PaginationStatus.None,
|
val status: PaginationStatus<*> = PaginationStatus.None,
|
||||||
val rawBatches: List<Batch<Int, PaginationWrapper<TxInfo>>> = emptyList(),
|
val rawBatches: List<Batch<Int, PaginationWrapper<TxInfo>>> = emptyList(),
|
||||||
val uiBatches: List<Batch<Int, List<TxHistoryItemsUM.TxHistoryItemUM>>> = emptyList(),
|
val uiBatches: List<Batch<Int, List<TxHistoryItemsUM.TxHistoryItemUM>>> = emptyList(),
|
||||||
val legacyUiBatches: List<Batch<Int, List<TxHistoryUM.TxHistoryItemUM>>> = emptyList(),
|
|
||||||
)
|
)
|
||||||
|
|
@ -2,11 +2,7 @@ package com.tangem.features.txhistory.state
|
||||||
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import com.tangem.core.ui.components.transactions.state.TransactionItemUM
|
import com.tangem.core.ui.components.transactions.state.TransactionItemUM
|
||||||
import com.tangem.core.ui.components.transactions.state.TransactionState
|
|
||||||
import com.tangem.features.txhistory.entity.TxHistoryItemsUM
|
import com.tangem.features.txhistory.entity.TxHistoryItemsUM
|
||||||
import com.tangem.features.txhistory.entity.TxHistoryUM
|
|
||||||
import io.mockk.every
|
|
||||||
import io.mockk.mockk
|
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
|
|
@ -14,12 +10,7 @@ import org.junit.jupiter.api.TestInstance
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
internal class TxHistoryStateControllerTest {
|
internal class TxHistoryStateControllerTest {
|
||||||
|
|
||||||
private val controller = TxHistoryStateController(
|
private val controller = TxHistoryStateController()
|
||||||
designFeatureToggles = mockk { every { isRedesignEnabled } returns true },
|
|
||||||
)
|
|
||||||
private val legacyController = TxHistoryStateController(
|
|
||||||
designFeatureToggles = mockk { every { isRedesignEnabled } returns false },
|
|
||||||
)
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `GIVEN empty items snapshot WHEN setContent THEN Empty state with explorer action`() {
|
fun `GIVEN empty items snapshot WHEN setContent THEN Empty state with explorer action`() {
|
||||||
|
|
@ -79,41 +70,4 @@ internal class TxHistoryStateControllerTest {
|
||||||
|
|
||||||
assertThat(controller.uiState.value).isInstanceOf(TxHistoryItemsUM.Empty::class.java)
|
assertThat(controller.uiState.value).isInstanceOf(TxHistoryItemsUM.Empty::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
// region Legacy (e.g. Solana: probe reports HasTransactions but the mapped page is empty)
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `GIVEN legacy snapshot with only a title WHEN setContent THEN legacy Empty state with explorer`() {
|
|
||||||
val onExploreClick = {}
|
|
||||||
|
|
||||||
legacyController.setContent(
|
|
||||||
snapshot = TxHistoryItemsSnapshot.LegacyItems(
|
|
||||||
persistentListOf(TxHistoryUM.TxHistoryItemUM.Title(onExploreClick = {})),
|
|
||||||
),
|
|
||||||
loadMore = { true },
|
|
||||||
onExploreClick = onExploreClick,
|
|
||||||
)
|
|
||||||
|
|
||||||
val state = legacyController.legacyUiState.value
|
|
||||||
assertThat(state).isInstanceOf(TxHistoryUM.Empty::class.java)
|
|
||||||
assertThat((state as TxHistoryUM.Empty).onExploreClick).isEqualTo(onExploreClick)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `GIVEN legacy snapshot with transactions WHEN setContent THEN legacy Content state`() {
|
|
||||||
legacyController.setContent(
|
|
||||||
snapshot = TxHistoryItemsSnapshot.LegacyItems(
|
|
||||||
persistentListOf(
|
|
||||||
TxHistoryUM.TxHistoryItemUM.Title(onExploreClick = {}),
|
|
||||||
TxHistoryUM.TxHistoryItemUM.Transaction(TransactionState.Loading("hash")),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
loadMore = { true },
|
|
||||||
onExploreClick = {},
|
|
||||||
)
|
|
||||||
|
|
||||||
assertThat(legacyController.legacyUiState.value).isInstanceOf(TxHistoryUM.Content::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
}
|
}
|
||||||
|
|
@ -12,14 +12,12 @@ import com.tangem.domain.txhistory.models.Page
|
||||||
import com.tangem.domain.txhistory.models.PaginationWrapper
|
import com.tangem.domain.txhistory.models.PaginationWrapper
|
||||||
import com.tangem.domain.txhistory.repository.ExpressHistoryPage
|
import com.tangem.domain.txhistory.repository.ExpressHistoryPage
|
||||||
import com.tangem.domain.txhistory.repository.TxHistoryRepositoryV2
|
import com.tangem.domain.txhistory.repository.TxHistoryRepositoryV2
|
||||||
import com.tangem.features.txhistory.converter.TxHistoryItemToTransactionStateConverter
|
|
||||||
import com.tangem.pagination.BatchFetchResult
|
import com.tangem.pagination.BatchFetchResult
|
||||||
import com.tangem.pagination.BatchListSource
|
import com.tangem.pagination.BatchListSource
|
||||||
import com.tangem.pagination.PaginationStatus
|
import com.tangem.pagination.PaginationStatus
|
||||||
import com.tangem.pagination.fetcher.BatchFetcher
|
import com.tangem.pagination.fetcher.BatchFetcher
|
||||||
import com.tangem.pagination.toBatchFlow
|
import com.tangem.pagination.toBatchFlow
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import io.mockk.every
|
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
|
@ -152,10 +150,8 @@ internal class TxHistoryListManagerTest {
|
||||||
dispatchers = repository.dispatchers,
|
dispatchers = repository.dispatchers,
|
||||||
userWalletId = userWalletId,
|
userWalletId = userWalletId,
|
||||||
currency = currency,
|
currency = currency,
|
||||||
designFeatureToggles = mockk { every { isRedesignEnabled } returns false },
|
|
||||||
txHistoryUiActions = mockk(relaxed = true),
|
txHistoryUiActions = mockk(relaxed = true),
|
||||||
lookupDataFlow = emptyFlow(),
|
lookupDataFlow = emptyFlow(),
|
||||||
legacyTxHistoryItemConverter = mockk<TxHistoryItemToTransactionStateConverter>(relaxed = true),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun page(itemCount: Int, isLast: Boolean): Page2Spec = Page2Spec(itemCount = itemCount, isLast = isLast)
|
private fun page(itemCount: Int, isLast: Boolean): Page2Spec = Page2Spec(itemCount = itemCount, isLast = isLast)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue