diff --git a/app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt b/app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt index ba23fa52dd..b23f4a5b54 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt @@ -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 diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt b/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt deleted file mode 100644 index ee2867d25c..0000000000 --- a/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.tangem.tap.common.extensions - -fun MutableList.removeBy(predicate: (T) -> Boolean): Boolean { - val toRemove = this.filter(predicate) - this.removeAll(toRemove) - return toRemove.isNotEmpty() -} - -fun MutableList.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 MutableList.replaceByOrAdd(item: T, predicate: (T) -> Boolean) { - if (!replaceBy(item, predicate)) add(item) -} - -fun MutableList.copy(): MutableList { - return this.map { it }.toMutableList() -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt index 15b8d10c94..d26e36149c 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt @@ -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") diff --git a/app/src/main/java/com/tangem/tap/data/RuntimeUserWalletsStore.kt b/app/src/main/java/com/tangem/tap/data/RuntimeUserWalletsStore.kt index 9b2167ff12..d46dfadcad 100644 --- a/app/src/main/java/com/tangem/tap/data/RuntimeUserWalletsStore.kt +++ b/app/src/main/java/com/tangem/tap/data/RuntimeUserWalletsStore.kt @@ -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 } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt index 9239ac0d3f..183ba76207 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt @@ -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 diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt index cadd73b606..4ab48a80a9 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt @@ -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) diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletAmountsRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletAmountsRepository.kt index b89be4b97f..6fa441f106 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletAmountsRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletAmountsRepository.kt @@ -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) diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/CacheKeysStoreModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/CacheKeysStoreModule.kt index ae96179015..ee39ca9041 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/CacheKeysStoreModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/CacheKeysStoreModule.kt @@ -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(), + ) } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/UserTokensStoreModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/UserTokensStoreModule.kt index 73d19f644d..757c01cef5 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/UserTokensStoreModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/UserTokensStoreModule.kt @@ -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)), + ) } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/WalletManagersStoreModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/WalletManagersStoreModule.kt new file mode 100644 index 0000000000..7d7beb051e --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/di/WalletManagersStoreModule.kt @@ -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(), + ) + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/files/AndroidFileReader.kt b/core/datasource/src/main/java/com/tangem/datasource/files/AndroidFileReader.kt index 01b45b3a15..5957268681 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/files/AndroidFileReader.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/files/AndroidFileReader.kt @@ -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) + } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/files/FileReader.kt b/core/datasource/src/main/java/com/tangem/datasource/files/FileReader.kt index bdeb127b9f..27b0f8a8e1 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/files/FileReader.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/files/FileReader.kt @@ -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) } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/cache/CacheKeysStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/cache/CacheKeysStore.kt index 1283fdd97d..962c09661a 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/cache/CacheKeysStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/cache/CacheKeysStore.kt @@ -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() } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/cache/DefaultCacheKeysStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/cache/DefaultCacheKeysStore.kt new file mode 100644 index 0000000000..a2788d6a87 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/cache/DefaultCacheKeysStore.kt @@ -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, +) : CacheKeysStore, StringKeyDataStoreDecorator(dataStore) { + + override fun provideStringKey(key: String): String { + return key + } + + override suspend fun store(key: CacheKey) { + store(key.id, key) + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/cache/RuntimeCacheKeysStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/cache/RuntimeCacheKeysStore.kt deleted file mode 100644 index 61ec6ce85c..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/cache/RuntimeCacheKeysStore.kt +++ /dev/null @@ -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() - } -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/FileDataStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/FileDataStore.kt index dc647aad1c..c69c6d5c64 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/FileDataStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/FileDataStore.kt @@ -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( - private val fileNameProvider: (key: String) -> String, +internal class FileDataStore( private val fileReader: FileReader, - private val adapter: JsonAdapter, -) { + private val adapter: JsonAdapter, +) : StringKeyDataStore { private val writeTrigger = MutableSharedFlow( replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST, ) - fun get(key: String): Flow { + override fun get(key: String): Flow { 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) { + 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) diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/RuntimeDataStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/RuntimeDataStore.kt index fced00f858..d1c4eff1eb 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/RuntimeDataStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/RuntimeDataStore.kt @@ -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(private val keyProvider: (Data) -> Key) { +internal class RuntimeDataStore : StringKeyDataStore { - private val store = MutableStateFlow>(hashMapOf()) + private val store = MutableStateFlow>(hashMapOf()) - fun get(selector: (Data) -> Boolean = { true }): Flow> { - return store.map { getInternal(it, selector) } + override fun get(key: String): Flow { + return store + .map { value -> value[key] } + .filterNotNull() } - fun getSync(selector: (Data) -> Boolean = { true }): List { - return getInternal(store.value, selector) + override suspend fun getSyncOrNull(key: String): Data? { + return store.value[key] } - // TODO: Uncomment if needed - // fun addOrReplace(items: Collection) { - // 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) { + 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, selector: (Data) -> Boolean): List { - return store.values.filter { selector(it) } - } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/core/DataStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/core/DataStore.kt new file mode 100644 index 0000000000..7374c56980 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/core/DataStore.kt @@ -0,0 +1,18 @@ +package com.tangem.datasource.local.datastore.core + +import kotlinx.coroutines.flow.Flow + +internal interface DataStore { + + fun get(key: Key): Flow + + suspend fun getSyncOrNull(key: Key): Value? + + suspend fun store(key: Key, item: Value) + + suspend fun store(items: Map) + + suspend fun remove(key: Key) + + suspend fun clear() +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/core/StringKeyDataStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/core/StringKeyDataStore.kt new file mode 100644 index 0000000000..4c04a89dc5 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/core/StringKeyDataStore.kt @@ -0,0 +1,3 @@ +package com.tangem.datasource.local.datastore.core + +internal interface StringKeyDataStore : DataStore \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/core/StringKeyDataStoreDecorator.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/core/StringKeyDataStoreDecorator.kt new file mode 100644 index 0000000000..92daa1a731 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/core/StringKeyDataStoreDecorator.kt @@ -0,0 +1,36 @@ +package com.tangem.datasource.local.datastore.core + +import kotlinx.coroutines.flow.Flow + +internal abstract class StringKeyDataStoreDecorator( + private val dataStore: StringKeyDataStore, +) : DataStore { + + abstract fun provideStringKey(key: Key): String + + override fun get(key: Key): Flow { + 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) { + dataStore.store( + items = items.mapKeys { (key, _) -> provideStringKey(key) }, + ) + } + + override suspend fun remove(key: Key) { + dataStore.remove(provideStringKey(key)) + } + + override suspend fun clear() { + dataStore.clear() + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/token/DefaultUserTokensStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/token/DefaultUserTokensStore.kt new file mode 100644 index 0000000000..43e895bc89 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/token/DefaultUserTokensStore.kt @@ -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, +) : UserTokensStore, StringKeyDataStoreDecorator(dataStore) { + + override fun provideStringKey(key: UserWalletId): String { + return key.stringValue + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/token/FileUserTokensStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/token/FileUserTokensStore.kt deleted file mode 100644 index 3c6e41fd4a..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/token/FileUserTokensStore.kt +++ /dev/null @@ -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( - fileNameProvider = { key -> "user_tokens_$key" }, - fileReader = fileReader, - adapter = moshi.adapter(UserTokensResponse::class.java), - ) - - override fun get(userWalletId: UserWalletId): Flow { - 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) - } -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/token/UserTokensStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/token/UserTokensStore.kt index 8b64c93767..ed175a871a 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/token/UserTokensStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/token/UserTokensStore.kt @@ -6,9 +6,9 @@ import kotlinx.coroutines.flow.Flow interface UserTokensStore { - fun get(userWalletId: UserWalletId): Flow + fun get(key: UserWalletId): Flow - 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) } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/userwallet/UserWalletsStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/userwallet/UserWalletsStore.kt index 4b47934b9a..a42253ee50 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/userwallet/UserWalletsStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/userwallet/UserWalletsStore.kt @@ -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? } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/walletmanager/DefaultWalletManagersStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/walletmanager/DefaultWalletManagersStore.kt new file mode 100644 index 0000000000..bed4e6a55c --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/walletmanager/DefaultWalletManagersStore.kt @@ -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>, +) : WalletManagersStore, StringKeyDataStoreDecorator>(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) + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/walletmanager/WalletManagersStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/walletmanager/WalletManagersStore.kt new file mode 100644 index 0000000000..c025cf6911 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/walletmanager/WalletManagersStore.kt @@ -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() +} \ No newline at end of file diff --git a/core/utils/src/main/java/com/tangem/utils/extensions/Collection.kt b/core/utils/src/main/java/com/tangem/utils/extensions/Collection.kt index 89fd92851b..36ef7e1ad6 100644 --- a/core/utils/src/main/java/com/tangem/utils/extensions/Collection.kt +++ b/core/utils/src/main/java/com/tangem/utils/extensions/Collection.kt @@ -3,4 +3,96 @@ package com.tangem.utils.extensions /** [REDACTED_AUTHOR] */ -fun Collection.isSingleItem(): Boolean = this.size == 1 \ No newline at end of file + +/** + * Checks if the [Collection] contains a single item. + * + * @return [Boolean] indicating whether the [Collection] contains exactly one element. + */ +fun Collection.isSingleItem(): Boolean = this.size == 1 + +/** + * Creates a shallow copy of this [Collection]. + * + * @return A copy of the [Collection]. + */ +fun Collection.copy(): Collection { + 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 Collection.plusOrReplace(item: T, predicate: (T) -> Boolean): List { + 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 MutableCollection.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 MutableCollection.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 MutableCollection.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 +} \ No newline at end of file diff --git a/data/common/src/main/kotlin/com/tangem/data/common/cache/DefaultCacheRegistry.kt b/data/common/src/main/kotlin/com/tangem/data/common/cache/DefaultCacheRegistry.kt index 47bfbc3675..b8380debee 100644 --- a/data/common/src/main/kotlin/com/tangem/data/common/cache/DefaultCacheRegistry.kt +++ b/data/common/src/main/kotlin/com/tangem/data/common/cache/DefaultCacheRegistry.kt @@ -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(), diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultTokensRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultTokensRepository.kt index 9d4a246327..5fa21ece16 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultTokensRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultTokensRepository.kt @@ -48,7 +48,7 @@ internal class DefaultTokensRepository( override fun getTokens(userWalletId: UserWalletId, refresh: Boolean): Flow> = channelFlow { val userWallet = withContext(dispatchers.io) { - requireNotNull(userWalletsStore.getOrNull(userWalletId)) { + requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { "Unable to find a user wallet with provided ID: $userWalletId" } }