From 9ebc1ac551a4697a41453c21d09f1907aa08d7fe Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 15 Sep 2025 21:10:37 +0700 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/AccountDomainModule.kt | 6 ++ .../archived/ArchivedAccountListModel.kt | 70 ++++++++++++++++--- .../archived/entity/AccountArchivedUM.kt | 3 +- .../entity/AccountArchivedUMBuilder.kt | 55 +++++++++++++++ .../archived/ui/ArchivedAccountListContent.kt | 17 +++-- .../createedit/AccountCreateEditModel.kt | 33 ++++++++- .../createedit/entity/AccountCreateEditUM.kt | 1 + .../entity/AccountCreateEditUMBuilder.kt | 5 ++ .../createedit/ui/AccountCreateEditContent.kt | 3 + .../account/details/AccountDetailsModel.kt | 18 ++++- .../details/entity/AccountDetailsUM.kt | 12 +++- .../details/ui/AccountDetailsContent.kt | 32 +++++---- 12 files changed, 224 insertions(+), 31 deletions(-) create mode 100644 features/account/impl/src/main/java/com/tangem/features/account/archived/entity/AccountArchivedUMBuilder.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/AccountDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/AccountDomainModule.kt index c4036dd181..d90cfcd00a 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/AccountDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/AccountDomainModule.kt @@ -43,6 +43,12 @@ internal object AccountDomainModule { return RecoverCryptoPortfolioUseCase(crudRepository = accountsCRUDRepository) } + @Provides + @Singleton + fun provideGetArchivedAccountsUseCase(accountsCRUDRepository: AccountsCRUDRepository): GetArchivedAccountsUseCase { + return GetArchivedAccountsUseCase(crudRepository = accountsCRUDRepository) + } + @Provides @Singleton fun provideGetUnoccupiedAccountIndexUseCase( diff --git a/features/account/impl/src/main/java/com/tangem/features/account/archived/ArchivedAccountListModel.kt b/features/account/impl/src/main/java/com/tangem/features/account/archived/ArchivedAccountListModel.kt index f170b6653b..f1c4734092 100644 --- a/features/account/impl/src/main/java/com/tangem/features/account/archived/ArchivedAccountListModel.kt +++ b/features/account/impl/src/main/java/com/tangem/features/account/archived/ArchivedAccountListModel.kt @@ -10,34 +10,78 @@ import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.message.DialogMessage import com.tangem.core.ui.message.EventMessageAction +import com.tangem.core.ui.message.ToastMessage +import com.tangem.domain.account.models.ArchivedAccount +import com.tangem.domain.account.usecase.ArchivedAccountList +import com.tangem.domain.account.usecase.GetArchivedAccountsUseCase import com.tangem.domain.account.usecase.RecoverCryptoPortfolioUseCase -import com.tangem.domain.models.account.Account +import com.tangem.domain.core.lce.Lce import com.tangem.domain.models.account.AccountId import com.tangem.features.account.ArchivedAccountListComponent import com.tangem.features.account.archived.entity.AccountArchivedUM +import com.tangem.features.account.archived.entity.AccountArchivedUMBuilder import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow +import com.tangem.utils.coroutines.JobHolder +import com.tangem.utils.coroutines.saveIn +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch +import timber.log.Timber import javax.inject.Inject -@Suppress("UnusedPrivateMember") // todo account +@Suppress("LongParameterList") internal class ArchivedAccountListModel @Inject constructor( paramsContainer: ParamsContainer, private val messageSender: UiMessageSender, private val router: Router, override val dispatchers: CoroutineDispatcherProvider, private val recoverCryptoPortfolioUseCase: RecoverCryptoPortfolioUseCase, + private val getArchivedAccountsUseCase: GetArchivedAccountsUseCase, + private val umBuilder: AccountArchivedUMBuilder, ) : Model() { private val params = paramsContainer.require() + private val onCloseClick = { router.pop() } val uiState: StateFlow get() = _uiState private val _uiState: MutableStateFlow = MutableStateFlow(getInitialState()) + private var getArchivedAccountsJob = JobHolder() - private fun confirmRecoverDialog(accountId: AccountId) { - val account: Account? = null // todo account find - account ?: return + init { + getArchivedAccounts() + } + + private fun getArchivedAccounts() { + getArchivedAccountsUseCase(params.userWalletId) + .conflate() + .distinctUntilChanged() + .onEach { lce -> + val newState = when (lce) { + is Lce.Content -> umBuilder.mapContent( + accounts = lce.content, + onCloseClick = onCloseClick, + confirmRecoverDialog = { confirmRecoverDialog(it) }, + ) + is Lce.Error -> umBuilder.mapError( + throwable = lce.error, + onCloseClick = onCloseClick, + getArchivedAccounts = { getArchivedAccounts() }, + ) + is Lce.Loading -> lce.partialContent?.let { content -> + umBuilder.mapContent( + accounts = content, + onCloseClick = onCloseClick, + confirmRecoverDialog = { confirmRecoverDialog(it) }, + ) + } + } + newState?.let { _uiState.value = newState } + } + .flowOn(dispatchers.default) + .launchIn(modelScope) + .saveIn(getArchivedAccountsJob) + } + + private fun confirmRecoverDialog(account: ArchivedAccount) { val secondAction = EventMessageAction( title = resourceReference(R.string.common_cancel), onClick = {}, @@ -51,7 +95,7 @@ internal class ArchivedAccountListModel @Inject constructor( title = resourceReference(R.string.account_archived_recover_dialog_title), message = resourceReference( id = R.string.account_archived_recover_dialog_description, - formatArgs = wrappedList(account.accountName.toUM().value), + formatArgs = wrappedList(account.name.toUM().value), ), firstActionBuilder = { firstAction }, secondActionBuilder = { secondAction }, @@ -61,11 +105,19 @@ internal class ArchivedAccountListModel @Inject constructor( private fun recoverCryptoPortfolio(accountId: AccountId) = modelScope.launch { recoverCryptoPortfolioUseCase(accountId) + .onLeft { Timber.e(it.toString()) } + .onRight { showSuccessRecoverMessage() } + router.pop() + } + + private fun showSuccessRecoverMessage() { + val message = resourceReference(R.string.account_recover_success_message) + messageSender.send(ToastMessage(message = message)) } private fun getInitialState(): AccountArchivedUM { return AccountArchivedUM.Loading( - onCloseClick = { router.pop() }, + onCloseClick = onCloseClick, ) } } \ No newline at end of file diff --git a/features/account/impl/src/main/java/com/tangem/features/account/archived/entity/AccountArchivedUM.kt b/features/account/impl/src/main/java/com/tangem/features/account/archived/entity/AccountArchivedUM.kt index bd72960062..a29481e093 100644 --- a/features/account/impl/src/main/java/com/tangem/features/account/archived/entity/AccountArchivedUM.kt +++ b/features/account/impl/src/main/java/com/tangem/features/account/archived/entity/AccountArchivedUM.kt @@ -23,5 +23,6 @@ internal data class ArchivedAccountUM( val accountName: TextReference, val accountIconUM: CryptoPortfolioIconUM, val tokensInfo: TextReference, - val onClick: (accountId: String) -> Unit, + val networksInfo: TextReference, + val onClick: () -> Unit, ) \ No newline at end of file diff --git a/features/account/impl/src/main/java/com/tangem/features/account/archived/entity/AccountArchivedUMBuilder.kt b/features/account/impl/src/main/java/com/tangem/features/account/archived/entity/AccountArchivedUMBuilder.kt new file mode 100644 index 0000000000..1b7b801cf3 --- /dev/null +++ b/features/account/impl/src/main/java/com/tangem/features/account/archived/entity/AccountArchivedUMBuilder.kt @@ -0,0 +1,55 @@ +package com.tangem.features.account.archived.entity + +import com.tangem.common.ui.account.toUM +import com.tangem.core.res.R +import com.tangem.core.ui.extensions.pluralReference +import com.tangem.core.ui.extensions.wrappedList +import com.tangem.domain.account.models.ArchivedAccount +import com.tangem.domain.account.usecase.ArchivedAccountList +import kotlinx.collections.immutable.toImmutableList +import timber.log.Timber +import javax.inject.Inject + +internal class AccountArchivedUMBuilder @Inject constructor() { + + fun mapContent( + accounts: ArchivedAccountList, + onCloseClick: () -> Unit, + confirmRecoverDialog: (account: ArchivedAccount) -> Unit, + ) = AccountArchivedUM.Content( + onCloseClick = onCloseClick, + accounts = accounts + .map { account -> account.mapArchivedAccountUM(confirmRecoverDialog) } + .toImmutableList(), + ) + + fun ArchivedAccount.mapArchivedAccountUM(confirmRecoverDialog: (account: ArchivedAccount) -> Unit) = + ArchivedAccountUM( + accountId = accountId.value, + accountName = name.toUM().value, + accountIconUM = icon.toUM(), + tokensInfo = pluralReference( + R.plurals.common_tokens_count, + count = tokensCount, + formatArgs = wrappedList(tokensCount), + ), + networksInfo = pluralReference( + R.plurals.common_networks_count, + count = networksCount, + formatArgs = wrappedList(networksCount), + ), + onClick = { confirmRecoverDialog(this) }, + ) + + fun mapError( + throwable: Throwable, + onCloseClick: () -> Unit, + getArchivedAccounts: () -> Unit, + ): AccountArchivedUM.Error { + Timber.e(throwable) + return AccountArchivedUM.Error( + onCloseClick = onCloseClick, + onRetryClick = { getArchivedAccounts() }, + ) + } +} \ No newline at end of file diff --git a/features/account/impl/src/main/java/com/tangem/features/account/archived/ui/ArchivedAccountListContent.kt b/features/account/impl/src/main/java/com/tangem/features/account/archived/ui/ArchivedAccountListContent.kt index a563f952d3..fca08ec492 100644 --- a/features/account/impl/src/main/java/com/tangem/features/account/archived/ui/ArchivedAccountListContent.kt +++ b/features/account/impl/src/main/java/com/tangem/features/account/archived/ui/ArchivedAccountListContent.kt @@ -21,6 +21,7 @@ import com.tangem.core.ui.components.appbar.AppBarWithBackButton import com.tangem.core.ui.components.buttons.SecondarySmallButton import com.tangem.core.ui.components.buttons.SmallButtonConfig import com.tangem.core.ui.decorations.roundedShapeItemDecoration +import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.stringResourceSafe @@ -125,14 +126,21 @@ private fun ArchivedAccountRow(item: ArchivedAccountUM, modifier: Modifier = Mod Row( modifier = modifier .fillMaxWidth() - .clickable(onClick = { item.onClick(item.accountId) }) + .clickable(onClick = item.onClick) .padding(all = TangemTheme.dimens.spacing12), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), ) { + val subtitle = stringResourceSafe( + id = R.string.account_label_tokens_info, + formatArgs = arrayOf( + item.tokensInfo.resolveReference(), + item.networksInfo.resolveReference(), + ), + ) AccountRow( title = item.accountName, - subtitle = item.tokensInfo, + subtitle = stringReference(subtitle), icon = item.accountIconUM, modifier = Modifier.weight(1f), ) @@ -140,7 +148,7 @@ private fun ArchivedAccountRow(item: ArchivedAccountUM, modifier: Modifier = Mod SecondarySmallButton( config = SmallButtonConfig( text = resourceReference(R.string.account_archived_recover), - onClick = { item.onClick(item.accountId) }, + onClick = item.onClick, ), ) } @@ -166,7 +174,8 @@ private class PreviewStateProvider : CollectionPreviewParameterProvider Unit, val text: TextReference, ) diff --git a/features/account/impl/src/main/java/com/tangem/features/account/createedit/entity/AccountCreateEditUMBuilder.kt b/features/account/impl/src/main/java/com/tangem/features/account/createedit/entity/AccountCreateEditUMBuilder.kt index 87947361a4..3b710ed06d 100644 --- a/features/account/impl/src/main/java/com/tangem/features/account/createedit/entity/AccountCreateEditUMBuilder.kt +++ b/features/account/impl/src/main/java/com/tangem/features/account/createedit/entity/AccountCreateEditUMBuilder.kt @@ -77,6 +77,7 @@ internal class AccountCreateEditUMBuilder( } return AccountCreateEditUM.Button( isButtonEnabled = false, + showProgress = false, onConfirmClick = onConfirmClick, text = text, ) @@ -117,6 +118,10 @@ internal class AccountCreateEditUMBuilder( return this.copy(buttonState = this.buttonState.copy(isButtonEnabled = isButtonEnabled)) } + fun AccountCreateEditUM.toggleProgress(showProgress: Boolean): AccountCreateEditUM { + return this.copy(buttonState = this.buttonState.copy(showProgress = showProgress)) + } + fun AccountCreateEditUM.updateDerivationIndex(derivationIndex: Int): AccountCreateEditUM { return this.copy( account = this.account.copy( diff --git a/features/account/impl/src/main/java/com/tangem/features/account/createedit/ui/AccountCreateEditContent.kt b/features/account/impl/src/main/java/com/tangem/features/account/createedit/ui/AccountCreateEditContent.kt index 2b7635a7df..a1f8f283a7 100644 --- a/features/account/impl/src/main/java/com/tangem/features/account/createedit/ui/AccountCreateEditContent.kt +++ b/features/account/impl/src/main/java/com/tangem/features/account/createedit/ui/AccountCreateEditContent.kt @@ -80,6 +80,7 @@ internal fun AccountCreateEditContent(state: AccountCreateEditUM, modifier: Modi .fillMaxWidth() .padding(16.dp), enabled = state.buttonState.isButtonEnabled, + showProgress = state.buttonState.showProgress, text = state.buttonState.text.resolveReference(), onClick = state.buttonState.onConfirmClick, ) @@ -309,6 +310,7 @@ private class PreviewStateProvider : CollectionPreviewParameterProvider when (account.isMainAccount) { + true -> AccountDetailsUM.ArchiveMode.None + false -> AccountDetailsUM.ArchiveMode.Available( + onArchiveAccountClick = ::onArchiveAccountClick, + ) + } + } return AccountDetailsUM( accountName = params.account.accountName.toUM().value, accountIcon = params.account.portfolioIcon.toUM(), onCloseClick = { router.pop() }, onAccountEditClick = ::onEditAccountClick, onManageTokensClick = ::onManageTokensClick, - onArchiveAccountClick = ::onArchiveAccountClick, + archiveMode = archiveMode, ) } } \ No newline at end of file diff --git a/features/account/impl/src/main/java/com/tangem/features/account/details/entity/AccountDetailsUM.kt b/features/account/impl/src/main/java/com/tangem/features/account/details/entity/AccountDetailsUM.kt index 6d1bb241b8..3938e757fb 100644 --- a/features/account/impl/src/main/java/com/tangem/features/account/details/entity/AccountDetailsUM.kt +++ b/features/account/impl/src/main/java/com/tangem/features/account/details/entity/AccountDetailsUM.kt @@ -6,8 +6,16 @@ import com.tangem.core.ui.extensions.TextReference internal data class AccountDetailsUM( val accountName: TextReference, val accountIcon: CryptoPortfolioIconUM, + val archiveMode: ArchiveMode, val onCloseClick: () -> Unit, val onAccountEditClick: () -> Unit, val onManageTokensClick: () -> Unit, - val onArchiveAccountClick: () -> Unit, -) \ No newline at end of file +) { + + sealed interface ArchiveMode { + data object None : ArchiveMode + data class Available( + val onArchiveAccountClick: () -> Unit, + ) : ArchiveMode + } +} \ No newline at end of file diff --git a/features/account/impl/src/main/java/com/tangem/features/account/details/ui/AccountDetailsContent.kt b/features/account/impl/src/main/java/com/tangem/features/account/details/ui/AccountDetailsContent.kt index 510f776f53..bd4a0e319f 100644 --- a/features/account/impl/src/main/java/com/tangem/features/account/details/ui/AccountDetailsContent.kt +++ b/features/account/impl/src/main/java/com/tangem/features/account/details/ui/AccountDetailsContent.kt @@ -65,21 +65,26 @@ internal fun AccountDetailsContent(state: AccountDetailsUM, modifier: Modifier = AccountRow(state) SpacerH16() ManageTokensRow(state) - SpacerH16() - ArchiveAccountRow(state) - SpacerH(8.dp) - Text( - modifier = Modifier.padding(horizontal = 12.dp), - text = stringResourceSafe(R.string.account_details_archive_description), - style = TangemTheme.typography.caption2, - color = TangemTheme.colors.text.tertiary, - ) + when (state.archiveMode) { + is AccountDetailsUM.ArchiveMode.Available -> { + SpacerH16() + ArchiveAccountRow(state.archiveMode) + SpacerH(8.dp) + Text( + modifier = Modifier.padding(horizontal = 12.dp), + text = stringResourceSafe(R.string.account_details_archive_description), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + ) + } + AccountDetailsUM.ArchiveMode.None -> Unit + } } } } @Composable -private fun ArchiveAccountRow(state: AccountDetailsUM) { +private fun ArchiveAccountRow(state: AccountDetailsUM.ArchiveMode.Available) { Row( modifier = Modifier .fillMaxWidth() @@ -176,12 +181,15 @@ private class PreviewStateProvider : CollectionPreviewParameterProvider