Updated on 2026-08-14

This commit is contained in:
Tangem 2023-10-10 08:58:37 +03:00
parent 7d9da40464
commit 519f5332f1
23 changed files with 379 additions and 132 deletions

View file

@ -10,6 +10,8 @@ android {
}
dependencies {
implementation(projects.data.common)
implementation(projects.core.utils)
implementation(projects.core.datasource)
implementation(projects.domain.legacy)
@ -20,7 +22,8 @@ dependencies {
implementation(deps.kotlin.coroutines)
implementation(deps.androidx.paging.runtime)
implementation(deps.arrow.core)
implementation(deps.timber)
implementation(deps.jodatime)
implementation(deps.hilt.core)
kapt(deps.hilt.kapt)

View file

@ -1,6 +1,8 @@
package com.tangem.data.txhistory.di
import com.tangem.data.common.cache.CacheRegistry
import com.tangem.data.txhistory.repository.DefaultTxHistoryRepository
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.walletmanager.WalletManagersFacade
@ -17,10 +19,14 @@ internal object TxHistoryDataModule {
@Provides
@Singleton
fun provideTxHistoryRepository(
cacheRegistry: CacheRegistry,
walletManagersFacade: WalletManagersFacade,
userWalletsStore: UserWalletsStore,
txHistoryItemsStore: TxHistoryItemsStore,
): TxHistoryRepository = DefaultTxHistoryRepository(
walletManagersFacade = walletManagersFacade,
userWalletsStore = userWalletsStore,
cacheRegistry,
walletManagersFacade,
userWalletsStore,
txHistoryItemsStore,
)
}

View file

@ -3,7 +3,9 @@ package com.tangem.data.txhistory.repository
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.tangem.data.common.cache.CacheRegistry
import com.tangem.data.txhistory.repository.paging.TxHistoryPagingSource
import com.tangem.datasource.local.txhistory.TxHistoryItemsStore
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
@ -13,50 +15,57 @@ import com.tangem.domain.txhistory.models.TxHistoryStateError
import com.tangem.domain.txhistory.repository.TxHistoryRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.flow.Flow
class DefaultTxHistoryRepository(
private val cacheRegistry: CacheRegistry,
private val walletManagersFacade: WalletManagersFacade,
private val userWalletsStore: UserWalletsStore,
private val txHistoryItemsStore: TxHistoryItemsStore,
) : TxHistoryRepository {
override suspend fun getTxHistoryItemsCount(network: Network): Int {
val userWallet = getUserWallet()
override suspend fun getTxHistoryItemsCount(userWalletId: UserWalletId, network: Network): Int {
val userWallet = getUserWallet(userWalletId)
val state = walletManagersFacade.getTxHistoryState(
userWalletId = userWallet.walletId,
network = network,
)
return when (state) {
is TxHistoryState.Failed.FetchError -> throw TxHistoryStateError.DataError(state.exception)
TxHistoryState.NotImplemented -> throw TxHistoryStateError.TxHistoryNotImplemented
TxHistoryState.Success.Empty -> throw TxHistoryStateError.EmptyTxHistories
is TxHistoryState.NotImplemented -> throw TxHistoryStateError.TxHistoryNotImplemented
is TxHistoryState.Success.Empty -> throw TxHistoryStateError.EmptyTxHistories
is TxHistoryState.Success.HasTransactions -> state.txCount
}
}
override fun getTxHistoryItems(currency: CryptoCurrency, pageSize: Int): Flow<PagingData<TxHistoryItem>> {
val userWallet = getUserWallet()
return Pager(
override fun getTxHistoryItems(
userWalletId: UserWalletId,
currency: CryptoCurrency,
pageSize: Int,
refresh: Boolean,
): Flow<PagingData<TxHistoryItem>> {
val pager = Pager(
config = PagingConfig(
pageSize = pageSize,
initialLoadSize = pageSize,
),
pagingSourceFactory = {
TxHistoryPagingSource(
loadPage = { page: Int, pageSize: Int ->
walletManagersFacade.getTxHistoryItems(
userWalletId = userWallet.walletId,
currency = currency,
page = page,
pageSize = pageSize,
)
},
sourceParams = TxHistoryPagingSource.Params(userWalletId, currency, pageSize, refresh),
txHistoryItemsStore = txHistoryItemsStore,
walletManagersFacade = walletManagersFacade,
cacheRegistry = cacheRegistry,
)
},
).flow
)
return pager.flow
}
private fun getUserWallet(): UserWallet = requireNotNull(userWalletsStore.selectedUserWalletOrNull) {
"Selected wallet must not be null"
private suspend fun getUserWallet(userWalletId: UserWalletId): UserWallet {
return requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
"Unable to find user wallet with provided ID: $userWalletId"
}
}
}

View file

@ -2,34 +2,96 @@ package com.tangem.data.txhistory.repository.paging
import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.tangem.data.common.cache.CacheRegistry
import com.tangem.datasource.local.txhistory.TxHistoryItemsStore
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.txhistory.models.PaginationWrapper
import com.tangem.domain.txhistory.models.TxHistoryItem
private const val INITIAL_PAGE = 1
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UserWalletId
import timber.log.Timber
internal class TxHistoryPagingSource(
private val loadPage: suspend (page: Int, pageSize: Int) -> PaginationWrapper<TxHistoryItem>,
private val sourceParams: Params,
private val txHistoryItemsStore: TxHistoryItemsStore,
private val walletManagersFacade: WalletManagersFacade,
private val cacheRegistry: CacheRegistry,
) : PagingSource<Int, TxHistoryItem>() {
private val storeKey = TxHistoryItemsStore.Key(sourceParams.userWalletId, sourceParams.currency)
override fun getRefreshKey(state: PagingState<Int, TxHistoryItem>): Int? {
return state.anchorPosition?.let { anchorPosition ->
state.closestPageToPosition(anchorPosition)?.prevKey?.plus(other = 1)
?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(other = 1)
val anchorPage = state.closestPageToPosition(anchorPosition)
anchorPage?.prevKey?.inc() ?: anchorPage?.nextKey?.dec()
}
}
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, TxHistoryItem> {
val currentPage = params.key ?: INITIAL_PAGE
return try {
val result = loadPage(currentPage, params.loadSize)
val pageToLoad = params.key ?: INITIAL_PAGE
LoadResult.Page(
data = result.items,
prevKey = if (currentPage > INITIAL_PAGE) currentPage.minus(1) else null,
nextKey = if (result.page < result.totalPages) currentPage.plus(1) else null,
return try {
val wrappedItems = loadItems(
pageToLoad = pageToLoad,
pageSize = sourceParams.pageSize,
refresh = sourceParams.refresh && params is LoadParams.Refresh,
)
} catch (e: Exception) {
val items = wrappedItems.items
val prevPage = when {
items.isEmpty() -> null
pageToLoad > INITIAL_PAGE -> pageToLoad.dec()
else -> null
}
val nextPage = when {
items.isEmpty() -> INITIAL_PAGE
pageToLoad < wrappedItems.totalPages -> pageToLoad.inc()
else -> null
}
LoadResult.Page(items, prevKey = prevPage, nextKey = nextPage)
} catch (e: Throwable) {
Timber.e(e, "Unable to load the transaction history for the requested page: $pageToLoad")
LoadResult.Error(e)
}
}
private suspend fun loadItems(pageToLoad: Int, pageSize: Int, refresh: Boolean): PaginationWrapper<TxHistoryItem> {
cacheRegistry.invokeOnExpire(
key = getTxHistoryPageKey(pageToLoad),
skipCache = refresh,
block = { fetch(pageToLoad, pageSize) },
)
return requireNotNull(txHistoryItemsStore.getSyncOrNull(storeKey, pageToLoad)) {
"The transaction history page #$pageToLoad could not be retrieved"
}
}
private suspend fun fetch(pageToLoad: Int, pageSize: Int) {
val wrappedItems = walletManagersFacade.getTxHistoryItems(
userWalletId = sourceParams.userWalletId,
currency = sourceParams.currency,
page = pageToLoad,
pageSize = pageSize,
)
txHistoryItemsStore.store(storeKey, wrappedItems)
}
private fun getTxHistoryPageKey(page: Int): String {
return "tx_history_page_${sourceParams.currency}_${sourceParams.userWalletId}_$page"
}
data class Params(
val userWalletId: UserWalletId,
val currency: CryptoCurrency,
val pageSize: Int,
val refresh: Boolean,
)
private companion object {
private const val INITIAL_PAGE = 1
}
}