Updated on 2026-08-14
This commit is contained in:
parent
70a269a5b5
commit
054a7f982f
11 changed files with 97 additions and 44 deletions
|
|
@ -72,14 +72,26 @@ internal class DefaultNetworksStatusesStore(
|
|||
|
||||
override suspend fun storeAll(key: UserWalletId, values: Set<NetworkStatus>) {
|
||||
coroutineScope {
|
||||
val updatedValues = getSyncOrNull(key).orEmpty()
|
||||
.addOrReplace(items = values) { prev, new -> prev.network == new.network }
|
||||
|
||||
launch { runtimeDataStore.store(key = provideStringKey(key), value = updatedValues) }
|
||||
launch { storeNetworkStatusInPersistence(userWalletId = key, statuses = updatedValues) }
|
||||
launch { storeInRuntimeStore(key = key, statuses = values) }
|
||||
launch { storeInPersistenceStore(userWalletId = key, statuses = values) }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun refresh(key: UserWalletId, networks: Set<Network>) {
|
||||
val currentStatuses = getSyncOrNull(key).orEmpty()
|
||||
|
||||
storeInRuntimeStore(
|
||||
key = key,
|
||||
statuses = networks.mapNotNullTo(hashSetOf()) { network ->
|
||||
val status = currentStatuses.firstOrNull { it.network.id == network.id } ?: return@mapNotNullTo null
|
||||
|
||||
status.copy(
|
||||
value = status.value.copySealed(source = StatusSource.CACHE),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge [cachedStatuses] with [runtimeStatuses]
|
||||
* The resulting set contains statuses from both sets.
|
||||
|
|
@ -108,7 +120,6 @@ internal class DefaultNetworksStatusesStore(
|
|||
val updatedCachedStatus = when (val status = cached.value) {
|
||||
is NetworkStatus.NoAccount -> status.copy(source = StatusSource.ONLY_CACHE)
|
||||
is NetworkStatus.Verified -> status.copy(source = StatusSource.ONLY_CACHE)
|
||||
is NetworkStatus.Refreshing,
|
||||
is NetworkStatus.Unreachable,
|
||||
is NetworkStatus.MissedDerivation,
|
||||
-> null
|
||||
|
|
@ -121,13 +132,23 @@ internal class DefaultNetworksStatusesStore(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun storeNetworkStatusInPersistence(userWalletId: UserWalletId, statuses: Set<NetworkStatus>) {
|
||||
private suspend fun storeInRuntimeStore(key: UserWalletId, statuses: Set<NetworkStatus>) {
|
||||
val updatedValues = getSyncOrNull(key).orEmpty()
|
||||
.addOrReplace(items = statuses) { prev, new -> prev.network == new.network }
|
||||
|
||||
runtimeDataStore.store(key = provideStringKey(key), value = updatedValues)
|
||||
}
|
||||
|
||||
private suspend fun storeInPersistenceStore(userWalletId: UserWalletId, statuses: Set<NetworkStatus>) {
|
||||
// Converter will return null if the network status is not supported
|
||||
val newStatuses = NetworkStatusDataModelConverter.convertSet(input = statuses).filterNotNull().toSet()
|
||||
|
||||
persistenceDataStore.updateData { storedStatuses ->
|
||||
storedStatuses.toMutableMap().apply {
|
||||
this[userWalletId.stringValue] = newStatuses
|
||||
val updatedValues = this[userWalletId.stringValue].orEmpty()
|
||||
.addOrReplace(newStatuses) { prev, new -> prev.networkId == new.networkId }
|
||||
|
||||
this[userWalletId.stringValue] = updatedValues
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,4 +16,6 @@ interface NetworksStatusesStore {
|
|||
suspend fun store(key: UserWalletId, value: NetworkStatus)
|
||||
|
||||
suspend fun storeAll(key: UserWalletId, values: Set<NetworkStatus>)
|
||||
|
||||
suspend fun refresh(key: UserWalletId, networks: Set<Network>)
|
||||
}
|
||||
|
|
@ -52,17 +52,30 @@ internal class DefaultQuotesStore(
|
|||
|
||||
override suspend fun store(response: QuotesResponse) {
|
||||
coroutineScope {
|
||||
launch { storeInRuntimeStore(response = response) }
|
||||
launch {
|
||||
storeInRuntimeStore(
|
||||
values = QuoteConverter(isCached = false).convertSet(input = response.quotes.entries),
|
||||
)
|
||||
}
|
||||
launch { storeInPersistenceStore(response = response) }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun storeEmptyQuotes(currenciesIds: Set<CryptoCurrency.RawID>) {
|
||||
runtimeStore.update(default = emptySet()) { saved ->
|
||||
val new = currenciesIds.map { Quote.Empty(it) }
|
||||
storeInRuntimeStore(values = currenciesIds.map(Quote::Empty).toSet())
|
||||
}
|
||||
|
||||
(saved + new).distinctBy { it.rawCurrencyId }.toSet()
|
||||
}
|
||||
override suspend fun refresh(currenciesIds: Set<CryptoCurrency.RawID>) {
|
||||
val currentStatuses = getSync(currenciesIds)
|
||||
|
||||
storeInRuntimeStore(
|
||||
values = currentStatuses.mapTo(hashSetOf()) { quote ->
|
||||
when (quote) {
|
||||
is Quote.Empty -> quote
|
||||
is Quote.Value -> quote.copy(source = StatusSource.CACHE)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun getCachedQuotes(currenciesIds: Set<CryptoCurrency.RawID>): Set<Quote.Value> {
|
||||
|
|
@ -95,11 +108,9 @@ internal class DefaultQuotesStore(
|
|||
?: Quote.Empty(currencyId)
|
||||
}
|
||||
|
||||
private suspend fun storeInRuntimeStore(response: QuotesResponse) {
|
||||
val new = QuoteConverter(isCached = false).convertSet(input = response.quotes.entries)
|
||||
|
||||
private suspend fun storeInRuntimeStore(values: Set<Quote>) {
|
||||
runtimeStore.update(default = emptySet()) { saved ->
|
||||
(saved + new).distinctBy { it.rawCurrencyId }.toSet()
|
||||
(saved + values).distinctBy { it.rawCurrencyId }.toSet()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,4 +19,6 @@ interface QuotesStore {
|
|||
|
||||
/** Store [Quote.Empty] for [currenciesIds] */
|
||||
suspend fun storeEmptyQuotes(currenciesIds: Set<CryptoCurrency.RawID>)
|
||||
|
||||
suspend fun refresh(currenciesIds: Set<CryptoCurrency.RawID>)
|
||||
}
|
||||
|
|
@ -72,15 +72,26 @@ internal class DefaultStakingBalanceStore(
|
|||
override suspend fun store(userWalletId: UserWalletId, items: Set<YieldBalanceWrapperDTO>) {
|
||||
coroutineScope {
|
||||
launch {
|
||||
storeInRuntimeStore(
|
||||
userWalletId = userWalletId,
|
||||
items = YieldBalanceConverter(isCached = false).convertSet(input = items),
|
||||
)
|
||||
updateRuntimeStore(userWalletId = userWalletId) {
|
||||
YieldBalanceConverter(isCached = false).convertSet(input = items)
|
||||
}
|
||||
}
|
||||
launch { storeInPersistenceStore(userWalletId = userWalletId, items = items) }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun refresh(userWalletId: UserWalletId) {
|
||||
updateRuntimeStore(userWalletId = userWalletId) { saved ->
|
||||
saved.mapTo(hashSetOf()) {
|
||||
when (it) {
|
||||
is YieldBalance.Data -> it.copy(source = StatusSource.CACHE)
|
||||
is YieldBalance.Empty -> it.copy(source = StatusSource.CACHE)
|
||||
is YieldBalance.Error -> it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun store(
|
||||
userWalletId: UserWalletId,
|
||||
integrationId: String,
|
||||
|
|
@ -107,13 +118,16 @@ internal class DefaultStakingBalanceStore(
|
|||
?.addOrReplace(newBalance) { it.integrationId == integrationId && it.address == address }
|
||||
?: setOf(newBalance)
|
||||
|
||||
storeInRuntimeStore(userWalletId = userWalletId, items = balances)
|
||||
updateRuntimeStore(userWalletId = userWalletId) { balances }
|
||||
}
|
||||
|
||||
private suspend fun storeInRuntimeStore(userWalletId: UserWalletId, items: Set<YieldBalance>) {
|
||||
runtimeStore.update(default = emptyMap()) {
|
||||
it.toMutableMap().apply {
|
||||
this[userWalletId] = items
|
||||
private suspend fun updateRuntimeStore(
|
||||
userWalletId: UserWalletId,
|
||||
function: (Set<YieldBalance>) -> Set<YieldBalance>,
|
||||
) {
|
||||
runtimeStore.update(default = emptyMap()) { saved ->
|
||||
saved.toMutableMap().apply {
|
||||
this[userWalletId] = function(this[userWalletId].orEmpty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,4 +26,6 @@ interface StakingBalanceStore {
|
|||
|
||||
/** Store [item] by [userWalletId], [integrationId] and [address] */
|
||||
suspend fun store(userWalletId: UserWalletId, integrationId: String, address: String, item: YieldBalanceWrapperDTO)
|
||||
|
||||
suspend fun refresh(userWalletId: UserWalletId)
|
||||
}
|
||||
|
|
@ -402,6 +402,10 @@ internal class DefaultStakingRepository(
|
|||
cryptoCurrencies: List<CryptoCurrency>,
|
||||
refresh: Boolean,
|
||||
) = withContext(dispatchers.io) {
|
||||
if (refresh) {
|
||||
stakingBalanceStore.refresh(userWalletId = userWalletId)
|
||||
}
|
||||
|
||||
cacheRegistry.invokeOnExpire(
|
||||
key = getYieldBalancesKey(userWalletId),
|
||||
skipCache = refresh,
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ internal class DefaultNetworksRepository(
|
|||
networks: Set<Network>,
|
||||
): Flow<Set<NetworkStatus>> {
|
||||
return networksStatusesStore.get(userWalletId, networks)
|
||||
.distinctUntilChanged()
|
||||
.flowOn(dispatchers.io)
|
||||
}
|
||||
|
||||
|
|
@ -120,17 +121,7 @@ internal class DefaultNetworksRepository(
|
|||
refresh: Boolean,
|
||||
) = coroutineScope {
|
||||
if (refresh) {
|
||||
val statusesToRefresh = networksStatusesStore.getSyncOrNull(userWalletId)?.mapNotNull {
|
||||
if (it.network in networks) {
|
||||
it.copy(value = NetworkStatus.Refreshing)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
if (statusesToRefresh != null) {
|
||||
networksStatusesStore.storeAll(key = userWalletId, values = statusesToRefresh.toSet())
|
||||
}
|
||||
networksStatusesStore.refresh(key = userWalletId, networks = networks)
|
||||
}
|
||||
|
||||
val currencies = getCurrencies(userWalletId, networks)
|
||||
|
|
|
|||
|
|
@ -100,6 +100,10 @@ internal class DefaultQuotesRepository(
|
|||
// it changes after filterExpiredCurrenciesIds
|
||||
// calls with different coroutines and lead to fetchQuotes
|
||||
mutex.withLock {
|
||||
if (refresh) {
|
||||
quotesStore.refresh(currenciesIds)
|
||||
}
|
||||
|
||||
val expiredCurrenciesIds = filterExpiredCurrenciesIds(
|
||||
currenciesIds = currenciesIds,
|
||||
refresh = refresh || quotesFetchedForAppCurrency != appCurrencyId,
|
||||
|
|
|
|||
|
|
@ -23,13 +23,16 @@ data class NetworkStatus(
|
|||
sealed class Value {
|
||||
|
||||
abstract val source: StatusSource
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the state where the network is refreshing.
|
||||
*/
|
||||
data object Refreshing : Value() {
|
||||
override val source: StatusSource = StatusSource.ACTUAL
|
||||
fun copySealed(source: StatusSource): Value {
|
||||
return when (this) {
|
||||
is NoAccount -> copy(source = source)
|
||||
is Verified -> copy(source = source)
|
||||
is Unreachable,
|
||||
is MissedDerivation,
|
||||
-> this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ internal class CurrencyStatusOperations(
|
|||
private fun createStatus(): CryptoCurrencyStatus.Value {
|
||||
return when (val status = networkStatus?.value) {
|
||||
null,
|
||||
is NetworkStatus.Refreshing,
|
||||
-> CryptoCurrencyStatus.Loading
|
||||
is NetworkStatus.MissedDerivation -> createMissedDerivationStatus()
|
||||
is NetworkStatus.Unreachable -> createUnreachableStatus(status)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue