Updated on 2026-08-14
This commit is contained in:
parent
9491a9cae6
commit
9abc118480
7 changed files with 91 additions and 66 deletions
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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<Map<String, String?>>,
|
||||
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<UserWallet> = 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? {
|
||||
|
|
|
|||
|
|
@ -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<Map<String, String?>> = 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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ArchivedAccount>
|
||||
|
||||
|
|
@ -37,32 +35,19 @@ class GetArchivedAccountsUseCase(
|
|||
* @param userWalletId the unique identifier of the user wallet
|
||||
*/
|
||||
operator fun invoke(userWalletId: UserWalletId): LceFlow<Throwable, ArchivedAccountList> = 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<Throwable, ArchivedAccountList> {
|
||||
return Either.catch {
|
||||
crudRepository.getArchivedAccountListSync(userWalletId = userWalletId).getOrElse {
|
||||
error("Archived accounts not found for user wallet: $userWalletId")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchArchivedAccounts(userWalletId: UserWalletId): Either<Throwable, Unit> {
|
||||
return Either.catch { crudRepository.fetchArchivedAccounts(userWalletId) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue