Updated on 2026-08-14

This commit is contained in:
Tangem 2026-07-07 17:09:10 +04:00
parent 9efe573323
commit 9af7bad7b8
22 changed files with 1680 additions and 238 deletions

View file

@ -27,5 +27,6 @@ dependencies {
api(projects.domain.express.models)
api(projects.domain.models)
api(projects.domain.txhistory.models)
api(projects.domain.visa.models)
// endregion
}

View file

@ -0,0 +1,74 @@
package com.tangem.domain.txhistory.list
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.txhistory.list.HistoryTxListManager.HistoryState
import com.tangem.domain.txhistory.model.TxHistoryInfo
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
/**
* Reads the unified transaction history for a currency: the on-chain backbone (BSDK / TangemPay / index-backed
* (wallet, currency) via [Factory].
*/
interface HistoryTxListManager {
/** The whole history state, newest first — the single source of truth for the UI. */
val state: StateFlow<HistoryState>
val historySources: Flow<HistorySources>
fun reload()
fun loadMore()
/** Universal history state: the caller renders exactly one of these. */
sealed interface HistoryState {
data object Loading : HistoryState
data object Unavailable : HistoryState
data object Empty : HistoryState
data object Error : HistoryState
data class Content(
val items: List<TxHistoryInfo>,
val isLoadingMore: Boolean,
val hasMore: Boolean,
) : HistoryState
}
/** How the history starts for a currency: which on-chain backbone exists and whether express is available. */
data class HistorySources(
val onChainSource: OnChainSource,
val isExchangeAvailable: Boolean,
val isOnrampAvailable: Boolean,
)
data class HistoryEnvironment(
val userWalletId: UserWalletId,
val currency: CryptoCurrency,
val modelScope: CoroutineScope,
)
enum class OnChainSource { BSDK, TangemPay, IndexTable }
interface Factory {
fun create(
userWalletId: UserWalletId,
currency: CryptoCurrency,
modelScope: CoroutineScope,
): HistoryTxListManager
}
}
/**
* Reactive stream of a single row tracked by its [TxHistoryInfo.txId], for the in-app details sheet.
*
* Seeded with the tapped [item] so the sheet always has an immediate snapshot, then re-emits the matching row from
* the live merged list as its status changes. The seed also covers rows not present in [state] yet (e.g. a pending
* tx surfaced from the currency status), which would otherwise never resolve.
*/
fun HistoryTxListManager.txHistoryInfoFlow(item: TxHistoryInfo): Flow<TxHistoryInfo> = state
.mapNotNull { (it as? HistoryState.Content)?.items }
.mapNotNull { list -> list.firstOrNull { it.txId == item.txId } }
.onStart { emit(item) }
.distinctUntilChanged()

View file

@ -4,6 +4,7 @@ import com.tangem.domain.express.models.ExchangeTransaction
import com.tangem.domain.express.models.ExpressProvider
import com.tangem.domain.express.models.OnrampTransaction
import com.tangem.domain.models.network.TxInfo
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
/**
* A single row of the unified transaction history shown on the token-details screen.
@ -38,11 +39,10 @@ sealed interface OnChainTx : TxHistoryInfo {
override val timestampMillis: Long get() = txInfo.timestampInMillis
}
// todo txHistory next step
/*data class TangemPay(val txInfo: TangemPayTxHistoryItem) : OnChainTx {
data class TangemPay(val txInfo: TangemPayTxHistoryItem) : OnChainTx {
override val txId: String get() = txInfo.id
override val timestampMillis: Long get() = txInfo.date.millis
}*/
}
// todo txHistory next step
/*data class Gateway() : OnChainTx {
@ -71,6 +71,13 @@ fun TxInfo.identityKey(): String = "$txHash|$type"
inline val TxHistoryInfo.explorerHash: String?
get() = when (this) {
is OnChainTx.BSDK -> txInfo.txHash
is OnChainTx.TangemPay -> when (val item = txInfo) {
is TangemPayTxHistoryItem.Payment -> item.transactionHash
is TangemPayTxHistoryItem.Collateral -> item.transactionHash
is TangemPayTxHistoryItem.Spend,
is TangemPayTxHistoryItem.Fee,
-> null
}
is ExpressTx -> matchHash
}
@ -78,6 +85,7 @@ inline val TxHistoryInfo.explorerHash: String?
inline val TxHistoryInfo.idToCopy: String
get() = when (this) {
is OnChainTx.BSDK -> txInfo.txHash
is OnChainTx.TangemPay -> explorerHash ?: txId
is ExpressTx -> txId
}