Updated on 2026-08-14
This commit is contained in:
parent
1437c14019
commit
3f671e3e0f
46 changed files with 1454 additions and 43 deletions
|
|
@ -14,6 +14,7 @@ dependencies {
|
|||
|
||||
implementation(projects.core.utils)
|
||||
implementation(projects.core.datasource)
|
||||
implementation(projects.core.pagination)
|
||||
implementation(projects.domain.legacy)
|
||||
implementation(projects.libs.blockchainSdk)
|
||||
implementation(projects.domain.tokens.models)
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@ package com.tangem.data.txhistory.di
|
|||
|
||||
import com.tangem.data.common.cache.CacheRegistry
|
||||
import com.tangem.data.txhistory.repository.DefaultTxHistoryRepository
|
||||
import com.tangem.data.txhistory.repository.RefactoredTxHistoryRepository
|
||||
import com.tangem.datasource.local.txhistory.TxHistoryItemsStore
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.txhistory.repository.TxHistoryRepository
|
||||
import com.tangem.domain.txhistory.repository.TxHistoryRepositoryV2
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
|
|
@ -32,4 +34,18 @@ internal object TxHistoryDataModule {
|
|||
txHistoryItemsStore,
|
||||
dispatchers,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideTxHistoryRepositoryV2(
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
txHistoryItemsStore: TxHistoryItemsStore,
|
||||
cacheRegistry: CacheRegistry,
|
||||
): TxHistoryRepositoryV2 = RefactoredTxHistoryRepository(
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
dispatchers = dispatchers,
|
||||
txHistoryItemsStore = txHistoryItemsStore,
|
||||
cacheRegistry = cacheRegistry,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
package com.tangem.data.txhistory.repository
|
||||
|
||||
import com.tangem.data.common.cache.CacheRegistry
|
||||
import com.tangem.data.txhistory.repository.paging.TxHistoryPageBatchFetcher
|
||||
import com.tangem.datasource.local.txhistory.TxHistoryItemsStore
|
||||
import com.tangem.domain.txhistory.model.TxHistoryListBatchFlow
|
||||
import com.tangem.domain.txhistory.model.TxHistoryListBatchingContext
|
||||
import com.tangem.domain.txhistory.model.TxHistoryListConfig
|
||||
import com.tangem.domain.txhistory.models.Page
|
||||
import com.tangem.domain.txhistory.models.PaginationWrapper
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.domain.txhistory.repository.TxHistoryRepositoryV2
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.walletmanager.utils.SdkPageConverter
|
||||
import com.tangem.pagination.BatchFetchResult
|
||||
import com.tangem.pagination.BatchListSource
|
||||
import com.tangem.pagination.toBatchFlow
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import timber.log.Timber
|
||||
|
||||
internal class RefactoredTxHistoryRepository(
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val txHistoryItemsStore: TxHistoryItemsStore,
|
||||
private val cacheRegistry: CacheRegistry,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : TxHistoryRepositoryV2 {
|
||||
|
||||
private val sdkPageConverter = SdkPageConverter()
|
||||
private val TxHistoryListConfig.storeKey get() = TxHistoryItemsStore.Key(userWalletId, currency)
|
||||
|
||||
override fun getTxHistoryBatchFlow(batchSize: Int, context: TxHistoryListBatchingContext): TxHistoryListBatchFlow {
|
||||
return BatchListSource(
|
||||
fetchDispatcher = dispatchers.io,
|
||||
context = context,
|
||||
generateNewKey = { keys -> keys.lastOrNull()?.inc() ?: 0 },
|
||||
batchFetcher = createFetcher(batchSize),
|
||||
).toBatchFlow()
|
||||
}
|
||||
|
||||
private fun createFetcher(
|
||||
batchSize: Int,
|
||||
): TxHistoryPageBatchFetcher<TxHistoryListConfig, PaginationWrapper<TxHistoryItem>> =
|
||||
TxHistoryPageBatchFetcher { request, _ ->
|
||||
val wrappedItems = loadItems(request, batchSize)
|
||||
BatchFetchResult.Success(
|
||||
data = wrappedItems,
|
||||
empty = wrappedItems.items.isEmpty(),
|
||||
last = wrappedItems.nextPage is Page.LastPage,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun loadItems(
|
||||
request: TxHistoryPageBatchFetcher.Request<TxHistoryListConfig>,
|
||||
batchSize: Int,
|
||||
): PaginationWrapper<TxHistoryItem> {
|
||||
cacheRegistry.invokeOnExpire(
|
||||
key = getTxHistoryPageKey(request.page, request.params),
|
||||
skipCache = request.params.refresh,
|
||||
block = { fetch(request, batchSize) },
|
||||
)
|
||||
|
||||
return txHistoryItemsStore.getSync(request.page, request.params)
|
||||
}
|
||||
|
||||
private suspend fun fetch(request: TxHistoryPageBatchFetcher.Request<TxHistoryListConfig>, batchSize: Int) {
|
||||
val wrappedItems = walletManagersFacade.getTxHistoryItems(
|
||||
userWalletId = request.params.userWalletId,
|
||||
currency = request.params.currency,
|
||||
page = sdkPageConverter.convertBack(request.page),
|
||||
pageSize = batchSize,
|
||||
)
|
||||
|
||||
txHistoryItemsStore.store(key = request.params.storeKey, value = wrappedItems)
|
||||
}
|
||||
|
||||
private suspend fun TxHistoryItemsStore.getSync(
|
||||
pageToLoad: Page,
|
||||
config: TxHistoryListConfig,
|
||||
): PaginationWrapper<TxHistoryItem> {
|
||||
val storedItems = requireNotNull(getSyncOrNull(config.storeKey, pageToLoad)) {
|
||||
"The transaction history page #$pageToLoad could not be retrieved"
|
||||
}
|
||||
|
||||
return if (pageToLoad is Page.Initial) storedItems.addRecentTransactions(config) else storedItems
|
||||
}
|
||||
|
||||
private suspend fun PaginationWrapper<TxHistoryItem>.addRecentTransactions(
|
||||
config: TxHistoryListConfig,
|
||||
): PaginationWrapper<TxHistoryItem> {
|
||||
val recentItems = walletManagersFacade.getRecentTransactions(
|
||||
userWalletId = config.userWalletId,
|
||||
currency = config.currency,
|
||||
)
|
||||
.filterUnconfirmedTransaction()
|
||||
.sortedByDescending { it.timestampInMillis }
|
||||
.filterIfTxAlreadyAdded(apiItems = items)
|
||||
|
||||
return if (recentItems.isEmpty()) {
|
||||
Timber.d("Nothing to add to TxHistory")
|
||||
this
|
||||
} else {
|
||||
Timber.d(
|
||||
"Recent transactions were added to TxHistory: %s",
|
||||
recentItems.joinToString(
|
||||
prefix = "[",
|
||||
postfix = "]",
|
||||
transform = TxHistoryItem::txHash,
|
||||
),
|
||||
)
|
||||
|
||||
return copy(items = recentItems + items)
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<TxHistoryItem>.filterUnconfirmedTransaction(): List<TxHistoryItem> {
|
||||
return filter { it.status == TxHistoryItem.TransactionStatus.Unconfirmed }
|
||||
}
|
||||
|
||||
private fun List<TxHistoryItem>.filterIfTxAlreadyAdded(apiItems: List<TxHistoryItem>): List<TxHistoryItem> {
|
||||
return filter { item -> apiItems.none { it.txHash == item.txHash } }
|
||||
}
|
||||
|
||||
private fun getTxHistoryPageKey(page: Page, config: TxHistoryListConfig): String {
|
||||
return "tx_history_page_${config.currency}_${config.userWalletId}_$page"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
package com.tangem.data.txhistory.repository.paging
|
||||
|
||||
import com.tangem.domain.txhistory.models.Page
|
||||
import com.tangem.domain.txhistory.models.PaginationWrapper
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.pagination.BatchFetchResult
|
||||
import com.tangem.pagination.exception.EndOfPaginationException
|
||||
import com.tangem.pagination.fetcher.BatchFetcher
|
||||
import kotlinx.coroutines.currentCoroutineContext
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
internal class TxHistoryPageBatchFetcher<TRequestParams : Any, TData : PaginationWrapper<TxHistoryItem>>(
|
||||
private val subFetcher: SubFetcher<TRequestParams, TData>,
|
||||
) : BatchFetcher<TRequestParams, TData> {
|
||||
data class Request<TRequestParams>(val page: Page, val params: TRequestParams)
|
||||
fun interface SubFetcher<TRequestParams : Any, TData> {
|
||||
suspend fun fetch(
|
||||
request: Request<TRequestParams>,
|
||||
lastResult: BatchFetchResult<TData>?,
|
||||
): BatchFetchResult<TData>
|
||||
}
|
||||
|
||||
private val lastRequest = MutableStateFlow<Request<TRequestParams>?>(null)
|
||||
|
||||
override suspend fun fetchFirst(requestParams: TRequestParams): BatchFetchResult<TData> {
|
||||
val req = Request(
|
||||
page = Page.Initial,
|
||||
params = requestParams,
|
||||
)
|
||||
|
||||
val res = runCatching {
|
||||
subFetcher.fetch(request = req, lastResult = null)
|
||||
}.getOrElse {
|
||||
currentCoroutineContext().ensureActive()
|
||||
BatchFetchResult.Error(it)
|
||||
}
|
||||
|
||||
lastRequest.value = req
|
||||
return res
|
||||
}
|
||||
|
||||
override suspend fun fetchNext(
|
||||
overrideRequestParams: TRequestParams?,
|
||||
lastResult: BatchFetchResult<TData>,
|
||||
): BatchFetchResult<TData> {
|
||||
val last = lastRequest.value
|
||||
requireNotNull(last)
|
||||
|
||||
val req = if (lastResult is BatchFetchResult.Success) {
|
||||
if (lastResult.last && overrideRequestParams == null) {
|
||||
return BatchFetchResult.Error(EndOfPaginationException())
|
||||
}
|
||||
|
||||
Request(
|
||||
page = lastResult.data.nextPage,
|
||||
params = overrideRequestParams ?: last.params,
|
||||
)
|
||||
} else {
|
||||
last
|
||||
}
|
||||
|
||||
val res = runCatching {
|
||||
subFetcher.fetch(request = req, lastResult = lastResult)
|
||||
}.getOrElse {
|
||||
currentCoroutineContext().ensureActive()
|
||||
BatchFetchResult.Error(it)
|
||||
}
|
||||
|
||||
lastRequest.value = req
|
||||
return res
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue