diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/token/DefaultUserTokensResponseStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/token/DefaultUserTokensResponseStore.kt index 0a80baa680..89f266e889 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/token/DefaultUserTokensResponseStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/token/DefaultUserTokensResponseStore.kt @@ -39,5 +39,13 @@ internal class DefaultUserTokensResponseStore( ) } + override suspend fun clear(userWalletId: UserWalletId) { + appPreferencesStore.updateData { preferences -> + val key = createPreferencesKey(userWalletId = userWalletId.stringValue) + + preferences.toMutablePreferences().apply { remove(key) } + } + } + private fun createPreferencesKey(userWalletId: String) = stringPreferencesKey(name = "user_tokens_$userWalletId") } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/token/UserTokensResponseStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/token/UserTokensResponseStore.kt index 9631d25c78..9da0c72837 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/token/UserTokensResponseStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/token/UserTokensResponseStore.kt @@ -17,4 +17,6 @@ interface UserTokensResponseStore { suspend fun getSyncOrNull(userWalletId: UserWalletId): UserTokensResponse? suspend fun store(userWalletId: UserWalletId, response: UserTokensResponse) + + suspend fun clear(userWalletId: UserWalletId) } \ No newline at end of file diff --git a/data/account/src/main/kotlin/com/tangem/data/account/converter/GetWalletAccountsResponseConverter.kt b/data/account/src/main/kotlin/com/tangem/data/account/converter/GetWalletAccountsResponseConverter.kt index 4ae6bf8b41..3f741ba0b5 100644 --- a/data/account/src/main/kotlin/com/tangem/data/account/converter/GetWalletAccountsResponseConverter.kt +++ b/data/account/src/main/kotlin/com/tangem/data/account/converter/GetWalletAccountsResponseConverter.kt @@ -14,7 +14,6 @@ import dagger.assisted.AssistedInject */ internal class GetWalletAccountsResponseConverter @AssistedInject constructor( @Assisted private val userWallet: UserWallet, - @Assisted val version: Int, cryptoPortfolioConverterFactory: CryptoPortfolioConverter.Factory, ) : Converter { @@ -25,7 +24,6 @@ internal class GetWalletAccountsResponseConverter @AssistedInject constructor( override fun convert(value: AccountList): GetWalletAccountsResponse { return GetWalletAccountsResponse( wallet = GetWalletAccountsResponse.Wallet( - version = version, group = TokensGroupTypeConverter.convertBack(value.groupType), sort = TokensSortTypeConverter.convertBack(value.sortType), totalAccounts = value.totalAccounts, @@ -39,6 +37,6 @@ internal class GetWalletAccountsResponseConverter @AssistedInject constructor( @AssistedFactory interface Factory { - fun create(userWallet: UserWallet, version: Int): GetWalletAccountsResponseConverter + fun create(userWallet: UserWallet): GetWalletAccountsResponseConverter } } \ No newline at end of file diff --git a/data/account/src/main/kotlin/com/tangem/data/account/fetcher/FetchWalletAccountsErrorHandler.kt b/data/account/src/main/kotlin/com/tangem/data/account/fetcher/FetchWalletAccountsErrorHandler.kt index 7142520d72..520868e3f6 100644 --- a/data/account/src/main/kotlin/com/tangem/data/account/fetcher/FetchWalletAccountsErrorHandler.kt +++ b/data/account/src/main/kotlin/com/tangem/data/account/fetcher/FetchWalletAccountsErrorHandler.kt @@ -104,9 +104,10 @@ internal class FetchWalletAccountsErrorHandler @Inject constructor( ): UserTokensResponse? { if (this != null) return this - return userTokensResponseStore.getSyncOrNull( - userWalletId = userWalletProvider().walletId, - ) + val userWalletId = userWalletProvider().walletId + + return userTokensResponseStore.getSyncOrNull(userWalletId) + .also { userTokensResponseStore.clear(userWalletId) } } private fun UserTokensResponse?.orDefault(userWalletProvider: Provider): UserTokensResponse { diff --git a/data/account/src/main/kotlin/com/tangem/data/account/repository/DefaultAccountsCRUDRepository.kt b/data/account/src/main/kotlin/com/tangem/data/account/repository/DefaultAccountsCRUDRepository.kt index 3d148930d5..333001ed0e 100644 --- a/data/account/src/main/kotlin/com/tangem/data/account/repository/DefaultAccountsCRUDRepository.kt +++ b/data/account/src/main/kotlin/com/tangem/data/account/repository/DefaultAccountsCRUDRepository.kt @@ -112,10 +112,7 @@ internal class DefaultAccountsCRUDRepository( .getOrThrow() } - val converter = convertersContainer.getWalletAccountsResponseCF.create( - userWallet = accountList.userWallet, - version = version, - ) + val converter = convertersContainer.getWalletAccountsResponseCF.create(userWallet = accountList.userWallet) val accountsResponse = converter.convert(value = accountList) diff --git a/data/account/src/test/java/com/tangem/data/account/converter/GetWalletAccountsResponseConverterTest.kt b/data/account/src/test/java/com/tangem/data/account/converter/GetWalletAccountsResponseConverterTest.kt index b6dddb7aa5..766544b58a 100644 --- a/data/account/src/test/java/com/tangem/data/account/converter/GetWalletAccountsResponseConverterTest.kt +++ b/data/account/src/test/java/com/tangem/data/account/converter/GetWalletAccountsResponseConverterTest.kt @@ -26,7 +26,6 @@ class GetWalletAccountsResponseConverterTest { private val cryptoPortfolioConverter = mockk() private val converter = GetWalletAccountsResponseConverter( userWallet = userWallet, - version = 0, cryptoPortfolioConverterFactory = cryptoPortfolioConverterFactory, ) diff --git a/data/account/src/test/java/com/tangem/data/account/fetcher/FetchWalletAccountsErrorHandlerTest.kt b/data/account/src/test/java/com/tangem/data/account/fetcher/FetchWalletAccountsErrorHandlerTest.kt index 5d41955371..532b33975b 100644 --- a/data/account/src/test/java/com/tangem/data/account/fetcher/FetchWalletAccountsErrorHandlerTest.kt +++ b/data/account/src/test/java/com/tangem/data/account/fetcher/FetchWalletAccountsErrorHandlerTest.kt @@ -30,7 +30,7 @@ class FetchWalletAccountsErrorHandlerTest { private val userTokensSaver: UserTokensSaver = mockk(relaxUnitFun = true) private val userWalletsStore: UserWalletsStore = mockk() - private val userTokensResponseStore: UserTokensResponseStore = mockk() + private val userTokensResponseStore: UserTokensResponseStore = mockk(relaxUnitFun = true) private val cryptoPortfolioCF: CryptoPortfolioConverter.Factory = mockk() private val cryptoPortfolioConverter = mockk() private val userTokensResponseFactory: UserTokensResponseFactory = mockk() diff --git a/data/account/src/test/java/com/tangem/data/account/repository/DefaultAccountsCRUDRepositoryTest.kt b/data/account/src/test/java/com/tangem/data/account/repository/DefaultAccountsCRUDRepositoryTest.kt index 0002eb553d..8fbcf92aea 100644 --- a/data/account/src/test/java/com/tangem/data/account/repository/DefaultAccountsCRUDRepositoryTest.kt +++ b/data/account/src/test/java/com/tangem/data/account/repository/DefaultAccountsCRUDRepositoryTest.kt @@ -595,7 +595,7 @@ class DefaultAccountsCRUDRepositoryTest { } every { - convertersContainer.getWalletAccountsResponseCF.create(userWallet = userWallet, version = version) + convertersContainer.getWalletAccountsResponseCF.create(userWallet = userWallet) } returns converter coEvery { accountsResponseStore.updateData(transform = any()) } returns accountsResponse @@ -610,7 +610,7 @@ class DefaultAccountsCRUDRepositoryTest { accountsResponseStoreFactory.create(userWalletId) accountsResponseStore.data tangemTechApi.saveWalletAccounts(userWalletId.stringValue, version.toString(), body) - convertersContainer.getWalletAccountsResponseCF.create(userWallet, version) + convertersContainer.getWalletAccountsResponseCF.create(userWallet) converter.convert(accountList) accountsResponseStore.updateData(any()) } @@ -656,7 +656,7 @@ class DefaultAccountsCRUDRepositoryTest { } coVerify(inverse = true) { - convertersContainer.getWalletAccountsResponseCF.create(any(), any()) + convertersContainer.getWalletAccountsResponseCF.create(any()) accountsResponseStore.updateData(any()) } } diff --git a/data/common/src/main/kotlin/com/tangem/data/common/cache/etag/DefaultETagsStore.kt b/data/common/src/main/kotlin/com/tangem/data/common/cache/etag/DefaultETagsStore.kt new file mode 100644 index 0000000000..5c8a9b2dc1 --- /dev/null +++ b/data/common/src/main/kotlin/com/tangem/data/common/cache/etag/DefaultETagsStore.kt @@ -0,0 +1,40 @@ +package com.tangem.data.common.cache.etag + +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.stringPreferencesKey +import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.datasource.local.preferences.utils.getSyncOrNull +import com.tangem.datasource.local.preferences.utils.store +import com.tangem.domain.models.wallet.UserWalletId +import timber.log.Timber + +/** + * Default implementation of the [ETagsStore] interface for managing ETag values + * + * @property appPreferencesStore the preferences store used for saving and retrieving ETag values + */ +internal class DefaultETagsStore( + private val appPreferencesStore: AppPreferencesStore, +) : ETagsStore { + + override suspend fun getSyncOrNull(userWalletId: UserWalletId, key: ETagsStore.Key): String? { + val key = getAccountsETagKey(userWalletId = userWalletId, key = key) + + return appPreferencesStore.getSyncOrNull(key = key) + } + + override suspend fun store(userWalletId: UserWalletId, key: ETagsStore.Key, value: String) { + if (value.isBlank()) { + Timber.e("ETag value is blank, not storing it. userWalletId: $userWalletId, key: $key") + return + } + + val key = getAccountsETagKey(userWalletId = userWalletId, key = key) + + appPreferencesStore.store(key = key, value = value) + } + + private fun getAccountsETagKey(userWalletId: UserWalletId, key: ETagsStore.Key): Preferences.Key { + return stringPreferencesKey(name = "etag_${key}_${userWalletId.stringValue}") + } +} \ No newline at end of file diff --git a/data/common/src/main/kotlin/com/tangem/data/common/cache/etag/ETagsStore.kt b/data/common/src/main/kotlin/com/tangem/data/common/cache/etag/ETagsStore.kt new file mode 100644 index 0000000000..430e9a0290 --- /dev/null +++ b/data/common/src/main/kotlin/com/tangem/data/common/cache/etag/ETagsStore.kt @@ -0,0 +1,34 @@ +package com.tangem.data.common.cache.etag + +import com.tangem.domain.models.wallet.UserWalletId + +/** + * Interface for working with ETag (Entity Tag), which is used for data caching and validation. + * +[REDACTED_AUTHOR] + */ +interface ETagsStore { + + /** + * Retrieves the stored ETag value for the specified wallet and key + * + * @param userWalletId identifier of the user wallet + * @param key the key for which to get the ETag value + */ + suspend fun getSyncOrNull(userWalletId: UserWalletId, key: Key): String? + + /** + * Stores the ETag value for the specified wallet and key + * + * @param userWalletId identifier of the user wallet + * @param key the key for which to get the ETag value + */ + suspend fun store(userWalletId: UserWalletId, key: Key, value: String) + + /** Enumeration of possible keys for storing ETag values */ + enum class Key { + WalletAccounts, + UserTokens, + ; + } +} \ No newline at end of file diff --git a/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensResponseAccountIdEnricher.kt b/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensResponseAccountIdEnricher.kt index f91e9f40f3..3594f6f1a0 100644 --- a/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensResponseAccountIdEnricher.kt +++ b/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensResponseAccountIdEnricher.kt @@ -50,9 +50,9 @@ object UserTokensResponseAccountIdEnricher { if (enrichedTokens.isEmpty()) return tokens + val enrichedTokenMap = enrichedTokens.associateBy { it } return tokens.map { token -> - val enrichedToken = enrichedTokens.firstOrNull { it == token } - enrichedToken ?: token + enrichedTokenMap[token] ?: token } } diff --git a/data/common/src/main/kotlin/com/tangem/data/common/di/DataCommonModule.kt b/data/common/src/main/kotlin/com/tangem/data/common/di/DataCommonModule.kt index 9b18e1b8fd..79af422911 100644 --- a/data/common/src/main/kotlin/com/tangem/data/common/di/DataCommonModule.kt +++ b/data/common/src/main/kotlin/com/tangem/data/common/di/DataCommonModule.kt @@ -1,10 +1,13 @@ package com.tangem.data.common.di import com.tangem.blockchainsdk.utils.ExcludedBlockchains +import com.tangem.data.common.cache.etag.DefaultETagsStore +import com.tangem.data.common.cache.etag.ETagsStore import com.tangem.data.common.currency.* import com.tangem.data.common.quote.DefaultQuotesFetcher import com.tangem.data.common.quote.QuotesFetcher import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.token.UserTokensResponseStore import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles @@ -76,4 +79,10 @@ internal object DataCommonModule { fun provideQuotesFetcher(tangemTechApi: TangemTechApi, dispatchers: CoroutineDispatcherProvider): QuotesFetcher { return DefaultQuotesFetcher(tangemTechApi = tangemTechApi, dispatchers = dispatchers) } + + @Provides + @Singleton + fun provideETagsStore(appPreferencesStore: AppPreferencesStore): ETagsStore { + return DefaultETagsStore(appPreferencesStore = appPreferencesStore) + } } \ No newline at end of file