Updated on 2026-08-14
This commit is contained in:
parent
4e38602301
commit
2317703e37
29 changed files with 369 additions and 169 deletions
|
|
@ -14,7 +14,6 @@ import com.tangem.domain.wallets.models.UserWallet
|
|||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.tap.common.analytics.converters.TopUpEventConverter
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.extensions.copy
|
||||
import com.tangem.tap.domain.model.TotalFiatBalance
|
||||
import com.tangem.tap.domain.model.WalletDataModel
|
||||
import com.tangem.tap.domain.model.WalletStoreModel
|
||||
|
|
@ -22,6 +21,7 @@ import com.tangem.tap.domain.walletCurrencies.WalletCurrenciesManager
|
|||
import com.tangem.tap.domain.walletStores.WalletStoresManager
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.utils.extensions.copy
|
||||
import kotlinx.coroutines.launch
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
fun <T> MutableList<T>.removeBy(predicate: (T) -> Boolean): Boolean {
|
||||
val toRemove = this.filter(predicate)
|
||||
this.removeAll(toRemove)
|
||||
return toRemove.isNotEmpty()
|
||||
}
|
||||
|
||||
fun <T> MutableList<T>.replaceBy(item: T, predicate: (T) -> Boolean): Boolean {
|
||||
val toRemove = this.filter(predicate)
|
||||
if (toRemove.isEmpty()) return false
|
||||
|
||||
val indexes = toRemove.map { indexOf(it) }
|
||||
this.removeAll(toRemove)
|
||||
indexes.forEach { this.add(it, item) }
|
||||
return true
|
||||
}
|
||||
|
||||
fun <T> MutableList<T>.replaceByOrAdd(item: T, predicate: (T) -> Boolean) {
|
||||
if (!replaceBy(item, predicate)) add(item)
|
||||
}
|
||||
|
||||
fun <T> MutableList<T>.copy(): MutableList<T> {
|
||||
return this.map { it }.toMutableList()
|
||||
}
|
||||
|
|
@ -2,11 +2,11 @@ package com.tangem.tap.common.redux.global
|
|||
|
||||
import com.tangem.domain.redux.domainStore
|
||||
import com.tangem.domain.redux.global.DomainGlobalAction
|
||||
import com.tangem.tap.common.extensions.replaceBy
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.features.onboarding.OnboardingManager
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
import com.tangem.utils.extensions.replaceBy
|
||||
import org.rekotlin.Action
|
||||
|
||||
@Suppress("LongMethod", "ComplexMethod")
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ internal class RuntimeUserWalletsStore(
|
|||
private val walletsStateHolder: WalletsStateHolder,
|
||||
) : UserWalletsStore {
|
||||
|
||||
override suspend fun getOrNull(userWalletId: UserWalletId): UserWallet? {
|
||||
override suspend fun getSyncOrNull(key: UserWalletId): UserWallet? {
|
||||
return walletsStateHolder.userWalletsListManager
|
||||
?.userWallets
|
||||
?.firstOrNull()
|
||||
?.singleOrNull { it.walletId == userWalletId }
|
||||
?.singleOrNull { it.walletId == key }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.tap.domain.configurable.warningMessage
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tap.common.extensions.removeBy
|
||||
import com.tangem.utils.extensions.removeBy
|
||||
import com.tangem.wallet.R
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ import com.tangem.common.flatMap
|
|||
import com.tangem.common.services.secure.SecureStorage
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.tap.common.extensions.replaceByOrAdd
|
||||
import com.tangem.tap.domain.userWalletList.model.UserWalletPublicInformation
|
||||
import com.tangem.tap.domain.userWalletList.repository.UserWalletsPublicInformationRepository
|
||||
import com.tangem.tap.domain.userWalletList.utils.publicInformation
|
||||
import com.tangem.utils.extensions.plusOrReplace
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
|
|
@ -29,12 +29,9 @@ internal class DefaultUserWalletsPublicInformationRepository(
|
|||
getAll()
|
||||
.flatMap { savedInformation ->
|
||||
val infoToSave = withContext(Dispatchers.Default) {
|
||||
savedInformation.toMutableList()
|
||||
.apply {
|
||||
replaceByOrAdd(userWallet.publicInformation) {
|
||||
userWallet.walletId == it.walletId
|
||||
}
|
||||
}
|
||||
savedInformation.plusOrReplace(userWallet.publicInformation) {
|
||||
userWallet.walletId == it.walletId
|
||||
}
|
||||
}
|
||||
|
||||
save(infoToSave)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import com.tangem.domain.wallets.models.UserWallet
|
|||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.tap.common.TestActions
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.common.extensions.replaceByOrAdd
|
||||
import com.tangem.tap.domain.model.WalletStoreModel
|
||||
import com.tangem.tap.domain.walletStores.WalletStoresError
|
||||
import com.tangem.tap.domain.walletStores.repository.WalletAmountsRepository
|
||||
|
|
@ -32,6 +31,7 @@ import com.tangem.tap.features.wallet.models.getPendingTransactions
|
|||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.extensions.plusOrReplace
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import timber.log.Timber
|
||||
|
|
@ -428,11 +428,10 @@ internal class DefaultWalletAmountsRepository(
|
|||
private suspend fun updateWalletManagerInStorage(userWalletId: UserWalletId, walletManager: WalletManager) =
|
||||
withContext(Dispatchers.Default) {
|
||||
WalletManagerStorage.update { prevManagers ->
|
||||
val newManagersForUserWallet = prevManagers[userWalletId].orEmpty().toMutableList().apply {
|
||||
replaceByOrAdd(walletManager) {
|
||||
val newManagersForUserWallet = prevManagers[userWalletId].orEmpty()
|
||||
.plusOrReplace(walletManager) {
|
||||
it.wallet.blockchain == walletManager.wallet.blockchain
|
||||
}
|
||||
}
|
||||
|
||||
prevManagers.apply {
|
||||
set(userWalletId, newManagersForUserWallet)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
package com.tangem.datasource.di
|
||||
|
||||
import com.tangem.datasource.local.cache.CacheKeysStore
|
||||
import com.tangem.datasource.local.cache.RuntimeCacheKeysStore
|
||||
import com.tangem.datasource.local.cache.DefaultCacheKeysStore
|
||||
import com.tangem.datasource.local.datastore.RuntimeDataStore
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -15,6 +16,8 @@ internal object CacheKeysStoreModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideCacheKeysStore(): CacheKeysStore {
|
||||
return RuntimeCacheKeysStore()
|
||||
return DefaultCacheKeysStore(
|
||||
dataStore = RuntimeDataStore(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
package com.tangem.datasource.di
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.files.FileReader
|
||||
import com.tangem.datasource.local.token.FileUserTokensStore
|
||||
import com.tangem.datasource.local.datastore.FileDataStore
|
||||
import com.tangem.datasource.local.token.DefaultUserTokensStore
|
||||
import com.tangem.datasource.local.token.UserTokensStore
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -17,6 +19,8 @@ internal object UserTokensStoreModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideUserTokensStore(fileReader: FileReader, @NetworkMoshi moshi: Moshi): UserTokensStore {
|
||||
return FileUserTokensStore(fileReader, moshi)
|
||||
return DefaultUserTokensStore(
|
||||
dataStore = FileDataStore(fileReader, moshi.adapter(UserTokensResponse::class.java)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.tangem.datasource.di
|
||||
|
||||
import com.tangem.datasource.local.datastore.RuntimeDataStore
|
||||
import com.tangem.datasource.local.walletmanager.DefaultWalletManagersStore
|
||||
import com.tangem.datasource.local.walletmanager.WalletManagersStore
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object WalletManagersStoreModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideWalletManagersStore(): WalletManagersStore {
|
||||
return DefaultWalletManagersStore(
|
||||
dataStore = RuntimeDataStore(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -19,4 +19,8 @@ class AndroidFileReader @Inject constructor(@ApplicationContext private val cont
|
|||
stream.write(content.toByteArray(), 0, content.length)
|
||||
}
|
||||
}
|
||||
|
||||
override fun removeFile(fileName: String) {
|
||||
context.deleteFile(fileName)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,10 @@
|
|||
package com.tangem.datasource.files
|
||||
|
||||
interface FileReader {
|
||||
|
||||
fun readFile(fileName: String): String
|
||||
|
||||
fun rewriteFile(content: String, fileName: String)
|
||||
|
||||
fun removeFile(fileName: String)
|
||||
}
|
||||
|
|
@ -4,11 +4,11 @@ import com.tangem.datasource.local.cache.model.CacheKey
|
|||
|
||||
interface CacheKeysStore {
|
||||
|
||||
fun getOrNull(id: String): CacheKey?
|
||||
suspend fun getSyncOrNull(key: String): CacheKey?
|
||||
|
||||
fun addOrReplace(key: CacheKey)
|
||||
suspend fun store(key: CacheKey)
|
||||
|
||||
fun remove(id: String)
|
||||
suspend fun remove(key: String)
|
||||
|
||||
fun clear()
|
||||
suspend fun clear()
|
||||
}
|
||||
18
core/datasource/src/main/java/com/tangem/datasource/local/cache/DefaultCacheKeysStore.kt
vendored
Normal file
18
core/datasource/src/main/java/com/tangem/datasource/local/cache/DefaultCacheKeysStore.kt
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.datasource.local.cache
|
||||
|
||||
import com.tangem.datasource.local.cache.model.CacheKey
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStoreDecorator
|
||||
|
||||
internal class DefaultCacheKeysStore(
|
||||
dataStore: StringKeyDataStore<CacheKey>,
|
||||
) : CacheKeysStore, StringKeyDataStoreDecorator<String, CacheKey>(dataStore) {
|
||||
|
||||
override fun provideStringKey(key: String): String {
|
||||
return key
|
||||
}
|
||||
|
||||
override suspend fun store(key: CacheKey) {
|
||||
store(key.id, key)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
package com.tangem.datasource.local.cache
|
||||
|
||||
import com.tangem.datasource.local.cache.model.CacheKey
|
||||
import com.tangem.datasource.local.datastore.RuntimeDataStore
|
||||
|
||||
internal class RuntimeCacheKeysStore : CacheKeysStore {
|
||||
|
||||
private val store = RuntimeDataStore(keyProvider = CacheKey::id)
|
||||
|
||||
override fun getOrNull(id: String): CacheKey? {
|
||||
return store.getSync { it.id == id }.singleOrNull()
|
||||
}
|
||||
|
||||
override fun addOrReplace(key: CacheKey) {
|
||||
store.addOrReplace(key)
|
||||
}
|
||||
|
||||
override fun remove(id: String) {
|
||||
store.remove { it.id == id }
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
store.clear()
|
||||
}
|
||||
}
|
||||
|
|
@ -2,47 +2,59 @@ package com.tangem.datasource.local.datastore
|
|||
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.tangem.datasource.files.FileReader
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
|
||||
import com.tangem.datasource.local.datastore.model.WriteTrigger
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.*
|
||||
import timber.log.Timber
|
||||
|
||||
internal class FileDataStore<Data : Any>(
|
||||
private val fileNameProvider: (key: String) -> String,
|
||||
internal class FileDataStore<Value : Any>(
|
||||
private val fileReader: FileReader,
|
||||
private val adapter: JsonAdapter<Data>,
|
||||
) {
|
||||
private val adapter: JsonAdapter<Value>,
|
||||
) : StringKeyDataStore<Value> {
|
||||
|
||||
private val writeTrigger = MutableSharedFlow<WriteTrigger>(
|
||||
replay = 1,
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST,
|
||||
)
|
||||
|
||||
fun get(key: String): Flow<Data> {
|
||||
override fun get(key: String): Flow<Value> {
|
||||
return writeTrigger
|
||||
.onEmpty { emit(WriteTrigger) }
|
||||
.map { getInternal(fileNameProvider(key)) }
|
||||
.map { getInternal(key) }
|
||||
.filterNotNull()
|
||||
}
|
||||
|
||||
fun getSyncOrNull(key: String): Data? {
|
||||
return getInternal(fileNameProvider(key))
|
||||
override suspend fun getSyncOrNull(key: String): Value? {
|
||||
return getInternal(key)
|
||||
}
|
||||
|
||||
fun store(key: String, content: Data) {
|
||||
val fileName = fileNameProvider(key)
|
||||
|
||||
override suspend fun store(key: String, item: Value) {
|
||||
try {
|
||||
val json = adapter.toJson(content)
|
||||
val json = adapter.toJson(item)
|
||||
|
||||
fileReader.rewriteFile(json, fileName)
|
||||
fileReader.rewriteFile(json, key)
|
||||
writeTrigger.tryEmit(WriteTrigger)
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e, "Unable to write file: $fileName")
|
||||
Timber.e(e, "Unable to write file: $key")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getInternal(fileName: String): Data? {
|
||||
override suspend fun store(items: Map<String, Value>) {
|
||||
items.forEach { (key, item) ->
|
||||
store(key, item)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun remove(key: String) {
|
||||
fileReader.removeFile(key)
|
||||
}
|
||||
|
||||
override suspend fun clear() {
|
||||
// TODO: Implement if needed
|
||||
}
|
||||
|
||||
private fun getInternal(fileName: String): Value? {
|
||||
return try {
|
||||
val json = fileReader.readFile(fileName)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,59 +1,49 @@
|
|||
package com.tangem.datasource.local.datastore
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.update
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
internal class RuntimeDataStore<Key : Any, Data : Any>(private val keyProvider: (Data) -> Key) {
|
||||
internal class RuntimeDataStore<Data : Any> : StringKeyDataStore<Data> {
|
||||
|
||||
private val store = MutableStateFlow<HashMap<Key, Data>>(hashMapOf())
|
||||
private val store = MutableStateFlow<HashMap<String, Data>>(hashMapOf())
|
||||
|
||||
fun get(selector: (Data) -> Boolean = { true }): Flow<List<Data>> {
|
||||
return store.map { getInternal(it, selector) }
|
||||
override fun get(key: String): Flow<Data> {
|
||||
return store
|
||||
.map { value -> value[key] }
|
||||
.filterNotNull()
|
||||
}
|
||||
|
||||
fun getSync(selector: (Data) -> Boolean = { true }): List<Data> {
|
||||
return getInternal(store.value, selector)
|
||||
override suspend fun getSyncOrNull(key: String): Data? {
|
||||
return store.value[key]
|
||||
}
|
||||
|
||||
// TODO: Uncomment if needed
|
||||
// fun addOrReplace(items: Collection<Data>) {
|
||||
// if (items.isEmpty()) return
|
||||
//
|
||||
// val storeValue = store.value
|
||||
//
|
||||
// items.forEach { item ->
|
||||
// storeValue[keyProvider(item)] = item
|
||||
// }
|
||||
//
|
||||
// store.value = storeValue
|
||||
// }
|
||||
override suspend fun store(key: String, item: Data) {
|
||||
store.update { value ->
|
||||
value[key] = item
|
||||
|
||||
fun addOrReplace(item: Data) {
|
||||
val storeValue = store.value
|
||||
storeValue[keyProvider(item)] = item
|
||||
|
||||
store.value = storeValue
|
||||
}
|
||||
|
||||
fun remove(selector: (Data) -> Boolean) {
|
||||
val storeValue = store.value
|
||||
|
||||
storeValue.forEach { (key, item) ->
|
||||
if (selector(item)) {
|
||||
storeValue.remove(key)
|
||||
}
|
||||
value
|
||||
}
|
||||
|
||||
store.value = storeValue
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
override suspend fun store(items: Map<String, Data>) {
|
||||
store.update { value ->
|
||||
items.forEach { (key, item) ->
|
||||
value[key] = item
|
||||
}
|
||||
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun remove(key: String) {
|
||||
store.update { value ->
|
||||
value.remove(key)
|
||||
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun clear() {
|
||||
store.update { hashMapOf() }
|
||||
}
|
||||
|
||||
private fun getInternal(store: HashMap<Key, Data>, selector: (Data) -> Boolean): List<Data> {
|
||||
return store.values.filter { selector(it) }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.datasource.local.datastore.core
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
internal interface DataStore<Key : Any, Value : Any> {
|
||||
|
||||
fun get(key: Key): Flow<Value>
|
||||
|
||||
suspend fun getSyncOrNull(key: Key): Value?
|
||||
|
||||
suspend fun store(key: Key, item: Value)
|
||||
|
||||
suspend fun store(items: Map<Key, Value>)
|
||||
|
||||
suspend fun remove(key: Key)
|
||||
|
||||
suspend fun clear()
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.tangem.datasource.local.datastore.core
|
||||
|
||||
internal interface StringKeyDataStore<Value : Any> : DataStore<String, Value>
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.tangem.datasource.local.datastore.core
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
internal abstract class StringKeyDataStoreDecorator<Key : Any, Value : Any>(
|
||||
private val dataStore: StringKeyDataStore<Value>,
|
||||
) : DataStore<Key, Value> {
|
||||
|
||||
abstract fun provideStringKey(key: Key): String
|
||||
|
||||
override fun get(key: Key): Flow<Value> {
|
||||
return dataStore.get(provideStringKey(key))
|
||||
}
|
||||
|
||||
override suspend fun getSyncOrNull(key: Key): Value? {
|
||||
return dataStore.getSyncOrNull(provideStringKey(key))
|
||||
}
|
||||
|
||||
override suspend fun store(key: Key, item: Value) {
|
||||
dataStore.store(provideStringKey(key), item)
|
||||
}
|
||||
|
||||
override suspend fun store(items: Map<Key, Value>) {
|
||||
dataStore.store(
|
||||
items = items.mapKeys { (key, _) -> provideStringKey(key) },
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun remove(key: Key) {
|
||||
dataStore.remove(provideStringKey(key))
|
||||
}
|
||||
|
||||
override suspend fun clear() {
|
||||
dataStore.clear()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.datasource.local.token
|
||||
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStoreDecorator
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
internal class DefaultUserTokensStore(
|
||||
dataStore: StringKeyDataStore<UserTokensResponse>,
|
||||
) : UserTokensStore, StringKeyDataStoreDecorator<UserWalletId, UserTokensResponse>(dataStore) {
|
||||
|
||||
override fun provideStringKey(key: UserWalletId): String {
|
||||
return key.stringValue
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
package com.tangem.datasource.local.token
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.files.FileReader
|
||||
import com.tangem.datasource.local.datastore.FileDataStore
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
internal class FileUserTokensStore(fileReader: FileReader, moshi: Moshi) : UserTokensStore {
|
||||
|
||||
private val store = FileDataStore<UserTokensResponse>(
|
||||
fileNameProvider = { key -> "user_tokens_$key" },
|
||||
fileReader = fileReader,
|
||||
adapter = moshi.adapter(UserTokensResponse::class.java),
|
||||
)
|
||||
|
||||
override fun get(userWalletId: UserWalletId): Flow<UserTokensResponse> {
|
||||
return store.get(userWalletId.stringValue)
|
||||
}
|
||||
|
||||
override suspend fun getSyncOrNull(userWalletId: UserWalletId): UserTokensResponse? {
|
||||
return store.getSyncOrNull(userWalletId.stringValue)
|
||||
}
|
||||
|
||||
override suspend fun store(userWalletId: UserWalletId, tokens: UserTokensResponse) {
|
||||
store.store(userWalletId.stringValue, tokens)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,9 +6,9 @@ import kotlinx.coroutines.flow.Flow
|
|||
|
||||
interface UserTokensStore {
|
||||
|
||||
fun get(userWalletId: UserWalletId): Flow<UserTokensResponse>
|
||||
fun get(key: UserWalletId): Flow<UserTokensResponse>
|
||||
|
||||
suspend fun getSyncOrNull(userWalletId: UserWalletId): UserTokensResponse?
|
||||
suspend fun getSyncOrNull(key: UserWalletId): UserTokensResponse?
|
||||
|
||||
suspend fun store(userWalletId: UserWalletId, tokens: UserTokensResponse)
|
||||
suspend fun store(key: UserWalletId, item: UserTokensResponse)
|
||||
}
|
||||
|
|
@ -5,5 +5,5 @@ import com.tangem.domain.wallets.models.UserWalletId
|
|||
|
||||
interface UserWalletsStore {
|
||||
|
||||
suspend fun getOrNull(userWalletId: UserWalletId): UserWallet?
|
||||
suspend fun getSyncOrNull(key: UserWalletId): UserWallet?
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package com.tangem.datasource.local.walletmanager
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStoreDecorator
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.extensions.plusOrReplace
|
||||
|
||||
internal class DefaultWalletManagersStore(
|
||||
dataStore: StringKeyDataStore<List<WalletManager>>,
|
||||
) : WalletManagersStore, StringKeyDataStoreDecorator<UserWalletId, List<WalletManager>>(dataStore) {
|
||||
|
||||
override fun provideStringKey(key: UserWalletId): String {
|
||||
return key.stringValue
|
||||
}
|
||||
|
||||
override suspend fun getSyncOrNull(
|
||||
userWalletId: UserWalletId,
|
||||
blockchain: Blockchain,
|
||||
derivationPath: String?,
|
||||
): WalletManager? {
|
||||
val walletManagers = getSyncOrNull(userWalletId)
|
||||
|
||||
return walletManagers?.singleOrNull {
|
||||
it.wallet.blockchain == blockchain &&
|
||||
it.wallet.publicKey.derivationPath?.rawPath == derivationPath
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun store(userWalletId: UserWalletId, walletManager: WalletManager) {
|
||||
val walletManagers = getSyncOrNull(userWalletId)
|
||||
|
||||
val updatedWalletManagers = walletManagers
|
||||
?.plusOrReplace(walletManager) {
|
||||
it.wallet.blockchain == walletManager.wallet.blockchain &&
|
||||
it.wallet.publicKey == walletManager.wallet.publicKey
|
||||
}
|
||||
?: listOf(walletManager)
|
||||
|
||||
store(userWalletId, updatedWalletManagers)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.datasource.local.walletmanager
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
interface WalletManagersStore {
|
||||
|
||||
suspend fun getSyncOrNull(
|
||||
userWalletId: UserWalletId,
|
||||
blockchain: Blockchain,
|
||||
derivationPath: String?,
|
||||
): WalletManager?
|
||||
|
||||
suspend fun store(userWalletId: UserWalletId, walletManager: WalletManager)
|
||||
|
||||
suspend fun clear()
|
||||
}
|
||||
|
|
@ -3,4 +3,96 @@ package com.tangem.utils.extensions
|
|||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
fun <T> Collection<T>.isSingleItem(): Boolean = this.size == 1
|
||||
|
||||
/**
|
||||
* Checks if the [Collection] contains a single item.
|
||||
*
|
||||
* @return [Boolean] indicating whether the [Collection] contains exactly one element.
|
||||
*/
|
||||
fun <T> Collection<T>.isSingleItem(): Boolean = this.size == 1
|
||||
|
||||
/**
|
||||
* Creates a shallow copy of this [Collection].
|
||||
*
|
||||
* @return A copy of the [Collection].
|
||||
*/
|
||||
fun <T> Collection<T>.copy(): Collection<T> {
|
||||
return this.map { it }
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified element to the collection or replaces an existing element.
|
||||
* The predicate defines the condition to replace the existing element.
|
||||
*
|
||||
* @param item The element to be added or replace the existing one.
|
||||
* @param predicate The condition to replace an existing element.
|
||||
* @return The modified [List] after adding or replacing the element.
|
||||
*/
|
||||
inline fun <T> Collection<T>.plusOrReplace(item: T, predicate: (T) -> Boolean): List<T> {
|
||||
val mutableList = this as? MutableList ?: ArrayList(this)
|
||||
|
||||
mutableList.addOrReplace(item, predicate)
|
||||
|
||||
return mutableList
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified element to the collection or replaces an existing element.
|
||||
* The predicate defines the condition to replace the existing element.
|
||||
*
|
||||
* @param item The element to be added or replace the existing one.
|
||||
* @param predicate The condition to replace an existing element.
|
||||
*/
|
||||
inline fun <T> MutableCollection<T>.addOrReplace(item: T, predicate: (T) -> Boolean) {
|
||||
val isReplaced = replaceBy(item, predicate)
|
||||
|
||||
if (!isReplaced) {
|
||||
add(item)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an element from the collection based on the provided predicate.
|
||||
*
|
||||
* @param predicate The condition to remove an element.
|
||||
* @return [Boolean] indicating whether an element was removed.
|
||||
*/
|
||||
inline fun <T> MutableCollection<T>.removeBy(predicate: (T) -> Boolean): Boolean {
|
||||
var removed = false
|
||||
val iterator = this.iterator()
|
||||
|
||||
for (e in iterator) {
|
||||
if (predicate(e)) {
|
||||
iterator.remove()
|
||||
removed = true
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return removed
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces an element in the collection with the provided item based on the predicate.
|
||||
*
|
||||
* @param item The element to replace the existing one.
|
||||
* @param predicate The condition to replace an existing element.
|
||||
* @return [Boolean] indicating whether an element was replaced.
|
||||
*/
|
||||
inline fun <T> MutableCollection<T>.replaceBy(item: T, predicate: (T) -> Boolean): Boolean {
|
||||
var replaced = false
|
||||
val mutableList = this as? MutableList ?: ArrayList(this)
|
||||
val iterator = mutableList.listIterator()
|
||||
|
||||
for (e in iterator) {
|
||||
if (predicate(e)) {
|
||||
iterator.set(item)
|
||||
replaced = true
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return replaced
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ internal class DefaultCacheRegistry(
|
|||
) : CacheRegistry {
|
||||
|
||||
override suspend fun isExpired(key: String): Boolean {
|
||||
val cacheKey = cacheKeysStore.getOrNull(key) ?: return true
|
||||
val cacheKey = cacheKeysStore.getSyncOrNull(key) ?: return true
|
||||
|
||||
return cacheKey.updatedAt
|
||||
.plus(cacheKey.expiresIn)
|
||||
|
|
@ -34,7 +34,7 @@ internal class DefaultCacheRegistry(
|
|||
val isExpired = isExpired(key) || skipCache
|
||||
if (!isExpired) return
|
||||
|
||||
cacheKeysStore.addOrReplace(
|
||||
cacheKeysStore.store(
|
||||
key = CacheKey(
|
||||
id = key,
|
||||
updatedAt = LocalDateTime.now(),
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ internal class DefaultTokensRepository(
|
|||
|
||||
override fun getTokens(userWalletId: UserWalletId, refresh: Boolean): Flow<Set<Token>> = channelFlow {
|
||||
val userWallet = withContext(dispatchers.io) {
|
||||
requireNotNull(userWalletsStore.getOrNull(userWalletId)) {
|
||||
requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
|
||||
"Unable to find a user wallet with provided ID: $userWalletId"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue