diff --git a/data/account/src/main/kotlin/com/tangem/data/account/di/AccountDataModule.kt b/data/account/src/main/kotlin/com/tangem/data/account/di/AccountDataModule.kt index 002af1472c..b996298a5e 100644 --- a/data/account/src/main/kotlin/com/tangem/data/account/di/AccountDataModule.kt +++ b/data/account/src/main/kotlin/com/tangem/data/account/di/AccountDataModule.kt @@ -10,9 +10,9 @@ import com.tangem.data.account.store.ArchivedAccountsStoreFactory import com.tangem.data.account.tokens.DefaultMainAccountTokensMigration import com.tangem.data.common.account.WalletAccountsFetcher import com.tangem.data.common.account.WalletAccountsSaver -import com.tangem.data.common.cache.etag.ETagsStore import com.tangem.data.common.currency.UserTokensSaver import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.datasource.local.datastore.RuntimeStateStore import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles import com.tangem.domain.account.repository.AccountsCRUDRepository @@ -42,7 +42,6 @@ internal object AccountDataModule { accountsResponseStoreFactory: AccountsResponseStoreFactory, userWalletsStore: UserWalletsStore, userTokensSaver: UserTokensSaver, - eTagsStore: ETagsStore, accountConverterFactoryContainer: AccountConverterFactoryContainer, dispatchers: CoroutineDispatcherProvider, ): AccountsCRUDRepository { @@ -53,7 +52,7 @@ internal object AccountDataModule { archivedAccountsStoreFactory = ArchivedAccountsStoreFactory, userWalletsStore = userWalletsStore, userTokensSaver = userTokensSaver, - eTagsStore = eTagsStore, + archivedAccountsETagStore = RuntimeStateStore(emptyMap()), convertersContainer = accountConverterFactoryContainer, dispatchers = dispatchers, ) 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 b9962e4deb..a71e7e66c3 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 @@ -10,12 +10,15 @@ import com.tangem.data.account.store.AccountsResponseStoreFactory import com.tangem.data.account.store.ArchivedAccountsStore import com.tangem.data.account.store.ArchivedAccountsStoreFactory import com.tangem.data.common.account.WalletAccountsSaver -import com.tangem.data.common.cache.etag.ETagsStore +import com.tangem.data.common.api.safeApiCall import com.tangem.data.common.currency.UserTokensSaver -import com.tangem.datasource.api.common.response.getOrThrow +import com.tangem.datasource.api.common.response.ApiResponse +import com.tangem.datasource.api.common.response.ApiResponseError.HttpException +import com.tangem.datasource.api.common.response.ETAG_HEADER import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse import com.tangem.datasource.api.tangemTech.models.account.toUserTokensResponse +import com.tangem.datasource.local.datastore.RuntimeStateStore import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.datasource.utils.getSyncOrNull import com.tangem.domain.account.models.AccountList @@ -43,7 +46,7 @@ internal class DefaultAccountsCRUDRepository( private val archivedAccountsStoreFactory: ArchivedAccountsStoreFactory, private val userWalletsStore: UserWalletsStore, private val userTokensSaver: UserTokensSaver, - private val eTagsStore: ETagsStore, + private val archivedAccountsETagStore: RuntimeStateStore>, private val convertersContainer: AccountConverterFactoryContainer, private val dispatchers: CoroutineDispatcherProvider, ) : AccountsCRUDRepository { @@ -90,19 +93,38 @@ internal class DefaultAccountsCRUDRepository( } override suspend fun fetchArchivedAccounts(userWalletId: UserWalletId) { - val response = withContext(dispatchers.io) { - tangemTechApi.getWalletArchivedAccounts( - walletId = userWalletId.stringValue, - eTag = getETag(userWalletId), - ).getOrThrow() - } - + val eTag = archivedAccountsETagStore.getSyncOrNull()?.get(key = userWalletId.stringValue) val store = getArchivedAccountsStore(userWalletId = userWalletId) - val converter = ArchivedAccountConverter(userWalletId = userWalletId) - val archivedAccounts = converter.convertList(input = response.accounts) + val response = safeApiCall( + call = { + val apiResponse = withContext(dispatchers.io) { + tangemTechApi.getWalletArchivedAccounts( + walletId = userWalletId.stringValue, + eTag = eTag, + ) + } - store.store(value = archivedAccounts) + saveETag(userWalletId, apiResponse) + + apiResponse.bind() + }, + onError = { + if (it is HttpException && it.code == HttpException.Code.NOT_MODIFIED) { + null + } else { + throw it + } + }, + ) + + if (response != null) { + val converter = ArchivedAccountConverter(userWalletId = userWalletId) + + val archivedAccounts = converter.convertList(input = response.accounts) + + store.store(value = archivedAccounts) + } } override suspend fun saveAccounts(accountList: AccountList) { @@ -167,8 +189,12 @@ internal class DefaultAccountsCRUDRepository( override fun getUserWalletsSync(): List = userWalletsStore.userWalletsSync - private suspend fun getETag(userWalletId: UserWalletId): String? { - return eTagsStore.getSyncOrNull(userWalletId = userWalletId, key = ETagsStore.Key.WalletAccounts) + private suspend fun saveETag(userWalletId: UserWalletId, apiResponse: ApiResponse<*>) { + val eTag = apiResponse.headers[ETAG_HEADER]?.firstOrNull() + + archivedAccountsETagStore.update { + it + (userWalletId.stringValue to eTag) + } } private suspend fun getAccountsResponseSync(userWalletId: UserWalletId): GetWalletAccountsResponse? { 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 5881a3194e..8d17be910e 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 @@ -10,7 +10,6 @@ import com.tangem.data.account.store.AccountsResponseStoreFactory import com.tangem.data.account.store.ArchivedAccountsStore import com.tangem.data.account.store.ArchivedAccountsStoreFactory import com.tangem.data.common.account.WalletAccountsSaver -import com.tangem.data.common.cache.etag.ETagsStore import com.tangem.data.common.currency.UserTokensSaver import com.tangem.datasource.api.common.response.ApiResponse import com.tangem.datasource.api.tangemTech.TangemTechApi @@ -54,7 +53,7 @@ class DefaultAccountsCRUDRepositoryTest { private val userWalletsStore: UserWalletsStore = mockk() private val userTokensSaver: UserTokensSaver = mockk() - private val eTagsStore: ETagsStore = mockk() + private val archivedAccountsETagStore: RuntimeStateStore> = mockk(relaxUnitFun = true) private val convertersContainer: AccountConverterFactoryContainer = mockk() private val accountListConverter: AccountListConverter = mockk() @@ -67,7 +66,7 @@ class DefaultAccountsCRUDRepositoryTest { archivedAccountsStoreFactory = archivedAccountsStoreFactory, userWalletsStore = userWalletsStore, userTokensSaver = userTokensSaver, - eTagsStore = eTagsStore, + archivedAccountsETagStore = archivedAccountsETagStore, convertersContainer = convertersContainer, dispatchers = TestingCoroutineDispatcherProvider(), ) @@ -532,7 +531,7 @@ class DefaultAccountsCRUDRepositoryTest { val archivedAccount = ArchivedAccountConverter(userWalletId).convert(accountDTO) - coEvery { eTagsStore.getSyncOrNull(userWalletId, ETagsStore.Key.WalletAccounts) } returns eTag + coEvery { archivedAccountsETagStore.getSyncOrNull() } returns mapOf(userWalletId.stringValue to eTag) coEvery { tangemTechApi.getWalletArchivedAccounts(userWalletId.stringValue, eTag) @@ -546,9 +545,10 @@ class DefaultAccountsCRUDRepositoryTest { Truth.assertThat(actual).containsExactly(archivedAccount) coVerifyOrder { - eTagsStore.getSyncOrNull(userWalletId, ETagsStore.Key.WalletAccounts) - tangemTechApi.getWalletArchivedAccounts(userWalletId.stringValue, eTag) + archivedAccountsETagStore.getSyncOrNull() archivedAccountsStoreFactory.create(userWalletId) + tangemTechApi.getWalletArchivedAccounts(userWalletId.stringValue, eTag) + archivedAccountsETagStore.update(any()) } } @@ -558,7 +558,7 @@ class DefaultAccountsCRUDRepositoryTest { val eTag = "etag123" val exception = Exception("API error") - coEvery { eTagsStore.getSyncOrNull(userWalletId, ETagsStore.Key.WalletAccounts) } returns eTag + coEvery { archivedAccountsETagStore.getSyncOrNull() } returns mapOf(userWalletId.stringValue to eTag) coEvery { tangemTechApi.getWalletArchivedAccounts(userWalletId.stringValue, eTag) } throws exception // Act @@ -570,7 +570,7 @@ class DefaultAccountsCRUDRepositoryTest { Truth.assertThat(archivedAccountsStore.getSyncOrNull()).isNull() coVerifyOrder { - eTagsStore.getSyncOrNull(userWalletId, ETagsStore.Key.WalletAccounts) + archivedAccountsETagStore.getSyncOrNull() tangemTechApi.getWalletArchivedAccounts(userWalletId.stringValue, eTag) } } diff --git a/domain/account/src/main/java/com/tangem/domain/account/usecase/GetArchivedAccountsUseCase.kt b/domain/account/src/main/java/com/tangem/domain/account/usecase/GetArchivedAccountsUseCase.kt index afaa2a587e..697b9e2001 100644 --- a/domain/account/src/main/java/com/tangem/domain/account/usecase/GetArchivedAccountsUseCase.kt +++ b/domain/account/src/main/java/com/tangem/domain/account/usecase/GetArchivedAccountsUseCase.kt @@ -1,7 +1,6 @@ package com.tangem.domain.account.usecase import arrow.core.Either -import arrow.core.getOrElse import com.tangem.domain.account.models.ArchivedAccount import com.tangem.domain.account.repository.AccountsCRUDRepository import com.tangem.domain.core.lce.Lce @@ -16,7 +15,6 @@ import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.retryWhen -import kotlinx.coroutines.launch typealias ArchivedAccountList = List @@ -37,32 +35,19 @@ class GetArchivedAccountsUseCase( * @param userWalletId the unique identifier of the user wallet */ operator fun invoke(userWalletId: UserWalletId): LceFlow = channelFlow { - val archivedAccounts = getArchivedAccounts(userWalletId = userWalletId) + send(lceLoading()) - archivedAccounts - .onRight { send(it.lceContent()) } - .onLeft { - send(lceLoading()) - - launch { - fetchArchivedAccounts(userWalletId).getOrElse { - send(it.lceError()) - } - } + fetchArchivedAccounts(userWalletId = userWalletId) + .onRight { + subscribeOnArchivedAccounts(userWalletId) + } + .onLeft { + send(it.lceError()) + return@channelFlow } - - subscribeOnArchivedAccounts(userWalletId) } .distinctUntilChanged() - private suspend fun getArchivedAccounts(userWalletId: UserWalletId): Either { - return Either.catch { - crudRepository.getArchivedAccountListSync(userWalletId = userWalletId).getOrElse { - error("Archived accounts not found for user wallet: $userWalletId") - } - } - } - private suspend fun fetchArchivedAccounts(userWalletId: UserWalletId): Either { return Either.catch { crudRepository.fetchArchivedAccounts(userWalletId) } } diff --git a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/entity/WalletSettingsItemUM.kt b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/entity/WalletSettingsItemUM.kt index fe8eafb722..c6d8ddb1a3 100644 --- a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/entity/WalletSettingsItemUM.kt +++ b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/entity/WalletSettingsItemUM.kt @@ -73,7 +73,7 @@ internal sealed class WalletSettingsAccountsUM : WalletSettingsItemUM() { data class Footer( override val id: String, val addAccount: AddAccountUM, - val archivedAccounts: BlockUM, + val archivedAccounts: BlockUM?, val showDescription: Boolean, val description: TextReference, ) : WalletSettingsAccountsUM() { diff --git a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/ui/WalletSettingsScreen.kt b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/ui/WalletSettingsScreen.kt index 7a01f6760f..4e33a73d35 100644 --- a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/ui/WalletSettingsScreen.kt +++ b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/ui/WalletSettingsScreen.kt @@ -1,6 +1,7 @@ package com.tangem.feature.walletsettings.ui import android.content.res.Configuration +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* @@ -325,17 +326,25 @@ private fun AccountsFooter(model: WalletSettingsAccountsUM.Footer, modifier: Mod ), ) { AddAccountRow(model.addAccount) - HorizontalDivider( - thickness = TangemTheme.dimens.size0_5, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = TangemTheme.dimens.spacing12), - color = TangemTheme.colors.stroke.primary, - ) - BlockItem( - modifier = Modifier.fillMaxWidth(), - model = model.archivedAccounts, - ) + + AnimatedVisibility(visible = model.archivedAccounts != null) { + model.archivedAccounts ?: return@AnimatedVisibility + + Column { + HorizontalDivider( + thickness = TangemTheme.dimens.size0_5, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = TangemTheme.dimens.spacing12), + color = TangemTheme.colors.stroke.primary, + ) + + BlockItem( + modifier = Modifier.fillMaxWidth(), + model = model.archivedAccounts, + ) + } + } } if (!model.showDescription) return SpacerH8() diff --git a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/utils/AccountItemsDelegate.kt b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/utils/AccountItemsDelegate.kt index ed1a0254c5..93f0217d80 100644 --- a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/utils/AccountItemsDelegate.kt +++ b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/utils/AccountItemsDelegate.kt @@ -85,6 +85,8 @@ internal class AccountItemsDelegate @Inject constructor( val addAccountEnabled = accounts.size < AccountList.MAX_ACCOUNTS_COUNT val showDescription = accounts.size > 1 + val isArchivedAccountsEnabled = accountStatusList.accountStatuses.size != accountStatusList.totalAccounts + WalletSettingsAccountsUM.Footer( id = "accounts_footer", addAccount = AddAccountUM( @@ -94,11 +96,15 @@ internal class AccountItemsDelegate @Inject constructor( if (addAccountEnabled) openAddAccount(userWalletId) else canNotAddAccountDialog() }, ), - archivedAccounts = BlockUM( - text = resourceReference(R.string.account_archived_accounts), - iconRes = R.drawable.ic_archive_24, - onClick = { openArchivedAccounts(userWalletId) }, - ), + archivedAccounts = if (isArchivedAccountsEnabled) { + BlockUM( + text = resourceReference(R.string.account_archived_accounts), + iconRes = R.drawable.ic_archive_24, + onClick = { openArchivedAccounts(userWalletId) }, + ) + } else { + null + }, showDescription = showDescription, description = resourceReference(R.string.account_reorder_description), ).let(::add)