From fe09323d6459bfb14cf5098ecf740fd89a107a77 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 19 Aug 2024 13:09:15 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../managetokens/model/ManageTokensModel.kt | 13 ++--- .../managetokens/ui/ManageTokensScreen.kt | 18 +++---- .../utils/list/ChangedCurrenciesManager.kt | 21 ++++++-- .../utils/list/ManageTokensListManager.kt | 53 ++++++++++--------- .../utils/list/ManageTokensListState.kt | 2 + .../utils/list/ManageTokensUiActions.kt | 16 ++++++ .../utils/list/ManageTokensUiManager.kt | 49 +++++++++-------- 7 files changed, 103 insertions(+), 69 deletions(-) create mode 100644 features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensUiActions.kt diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt index 2d2ce96fe0..c56ae2b5b9 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt @@ -55,7 +55,7 @@ internal class ManageTokensModel @Inject constructor( ).launchIn(modelScope) modelScope.launch { - manageTokensListManager.launch(params.userWalletId) + manageTokensListManager.launchPagination(params.userWalletId) } } @@ -110,7 +110,7 @@ internal class ManageTokensModel @Inject constructor( onActiveChange = ::toggleSearchBar, ), hasChanges = false, - saveChanges = ::onSaveClick, + saveChanges = ::saveChanges, loadMore = ::loadMoreItems, ) } @@ -126,7 +126,9 @@ internal class ManageTokensModel @Inject constructor( private fun updatePaginationStatus(status: PaginationStatus<*>) { state.update { state -> when (status) { - is PaginationStatus.InitialLoading -> { + is PaginationStatus.None, + is PaginationStatus.InitialLoading, + -> { if (state.search.isActive) { state } else { @@ -151,7 +153,6 @@ internal class ManageTokensModel @Inject constructor( isNextBatchLoading = false, ) } - is PaginationStatus.None, is PaginationStatus.Paginating, is PaginationStatus.EndOfPagination, -> state.copySealed( @@ -172,7 +173,7 @@ internal class ManageTokensModel @Inject constructor( private fun loadMoreItems(): Boolean { val state = state.value - if (state.isInitialBatchLoading) return false + if (state.isInitialBatchLoading || state.isNextBatchLoading) return false modelScope.launch { manageTokensListManager.loadMore( @@ -192,7 +193,7 @@ internal class ManageTokensModel @Inject constructor( // TODO: [REDACTED_JIRA] } - private fun onSaveClick() { + private fun saveChanges() { // TODO: [REDACTED_JIRA] } diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/ManageTokensScreen.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/ManageTokensScreen.kt index 00a647c307..2104369a82 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/ManageTokensScreen.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/ManageTokensScreen.kt @@ -3,7 +3,6 @@ package com.tangem.features.managetokens.ui import android.content.res.Configuration import androidx.compose.animation.* import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* @@ -173,7 +172,6 @@ private fun Content(state: ManageTokensUM, modifier: Modifier = Modifier) { } } -@OptIn(ExperimentalFoundationApi::class) @Composable private fun Currencies( items: ImmutableList, @@ -215,15 +213,13 @@ private fun Currencies( } } - item(key = "loading_indicator") { - AnimatedVisibility( - modifier = Modifier - .padding(vertical = TangemTheme.dimens.spacing16) - .fillMaxWidth(), - visible = showLoadingItem, - label = "loading_indicator_visibility", - ) { - ProgressIndicator() + if (showLoadingItem) { + item(key = "loading_item") { + ProgressIndicator( + modifier = Modifier + .padding(vertical = TangemTheme.dimens.spacing16) + .fillMaxWidth(), + ) } } } diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ChangedCurrenciesManager.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ChangedCurrenciesManager.kt index c991cf95bb..3e2705f9e6 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ChangedCurrenciesManager.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ChangedCurrenciesManager.kt @@ -7,12 +7,25 @@ import kotlinx.coroutines.flow.update internal typealias ChangedCurrencies = Map> -internal interface ChangedCurrenciesManager { +internal class ChangedCurrenciesManager { - val currenciesToAdd: MutableStateFlow - val currenciesToRemove: MutableStateFlow + val currenciesToAdd: MutableStateFlow = MutableStateFlow(emptyMap()) + val currenciesToRemove: MutableStateFlow = MutableStateFlow(emptyMap()) - fun updateChangedItems( + fun addCurrency(currencyId: ManagedCryptoCurrency.ID, networkId: Network.ID) { + updateChangedItems(currencyId, networkId, currenciesToRemove, currenciesToAdd) + } + + fun removeCurrency(currencyId: ManagedCryptoCurrency.ID, networkId: Network.ID) { + updateChangedItems(currencyId, networkId, currenciesToAdd, currenciesToRemove) + } + + fun containsCurrency(currencyId: ManagedCryptoCurrency.ID, networkId: Network.ID): Boolean { + return networkId in currenciesToAdd.value[currencyId].orEmpty() || + networkId in currenciesToRemove.value[currencyId].orEmpty() + } + + private fun updateChangedItems( currencyId: ManagedCryptoCurrency.ID, networkId: Network.ID, removeFromIfPresent: MutableStateFlow, diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensListManager.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensListManager.kt index ebf3369bce..df07878c62 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensListManager.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensListManager.kt @@ -19,11 +19,11 @@ import com.tangem.features.managetokens.impl.R import com.tangem.pagination.BatchAction import com.tangem.pagination.BatchListState import com.tangem.pagination.PaginationStatus +import com.tangem.utils.Provider import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.JobHolder import com.tangem.utils.coroutines.saveIn import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.BufferOverflow @@ -38,37 +38,37 @@ internal class ManageTokensListManager @Inject constructor( private val checkHasLinkedTokensUseCase: CheckHasLinkedTokensUseCase, private val messageSender: UiMessageSender, private val dispatchers: CoroutineDispatcherProvider, -) : ChangedCurrenciesManager, - ManageTokensUiManager( - messageSender = messageSender, - dispatchers = dispatchers, - ) { +) : ManageTokensUiActions { - override lateinit var scope: CoroutineScope + private lateinit var scope: CoroutineScope private val jobHolder = JobHolder() - private val actionsFlow = MutableSharedFlow( + private val actionsFlow: MutableSharedFlow = MutableSharedFlow( replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST, ) - override val state: MutableStateFlow = MutableStateFlow(ManageTokensListState()) + private val state: MutableStateFlow = MutableStateFlow(ManageTokensListState()) - val paginationStatus: MutableStateFlow> = MutableStateFlow(PaginationStatus.None) + private val changedCurrenciesManager = ChangedCurrenciesManager() + private val uiManager = ManageTokensUiManager( + state = state, + messageSender = messageSender, + dispatchers = dispatchers, + actions = this, + scopeProvider = Provider { scope }, + ) - override val currenciesToAdd: MutableStateFlow = MutableStateFlow(emptyMap()) - override val currenciesToRemove: MutableStateFlow = MutableStateFlow(emptyMap()) + val currenciesToAdd: StateFlow = changedCurrenciesManager.currenciesToAdd + val currenciesToRemove: StateFlow = changedCurrenciesManager.currenciesToRemove @OptIn(ExperimentalCoroutinesApi::class) - val uiItems: Flow> = state - .mapLatest { state -> - state.uiBatches.asSequence() - .flatMap { it.data } - .toImmutableList() - } + val paginationStatus: Flow> = state + .mapLatest { it.status } .distinctUntilChanged() + val uiItems: Flow> = uiManager.items - suspend fun launch(userWalletId: UserWalletId?) = coroutineScope { + suspend fun launchPagination(userWalletId: UserWalletId?) = coroutineScope { scope = this val batchFlow = getManagedTokensUseCase( @@ -120,7 +120,11 @@ internal class ManageTokensListManager @Inject constructor( batchListState: BatchListState>, userWalletId: UserWalletId?, ) { - paginationStatus.value = batchListState.status + state.update { state -> + state.copy( + status = batchListState.status, + ) + } state.update { state -> val newBatches = batchListState.data @@ -138,20 +142,20 @@ internal class ManageTokensListManager @Inject constructor( state.copy( userWalletId = userWalletId, currencyBatches = newBatches, - uiBatches = getUiBatches(newBatches, canEditItems), + uiBatches = uiManager.createOrUpdateUiBatches(newBatches, canEditItems), canEditItems = canEditItems, ) } } override fun addCurrency(batchKey: Int, currencyId: ManagedCryptoCurrency.ID, networkId: Network.ID) { - updateChangedItems(currencyId, networkId, currenciesToRemove, currenciesToAdd) + changedCurrenciesManager.addCurrency(currencyId, networkId) sendSelectCurrencyAction(batchKey, currencyId, networkId, isSelected = true) } override fun removeCurrency(batchKey: Int, currencyId: ManagedCryptoCurrency.ID, networkId: Network.ID) { - updateChangedItems(currencyId, networkId, currenciesToAdd, currenciesToRemove) + changedCurrenciesManager.removeCurrency(currencyId, networkId) sendSelectCurrencyAction(batchKey, currencyId, networkId, isSelected = false) } @@ -159,8 +163,7 @@ internal class ManageTokensListManager @Inject constructor( override fun checkNeedToShowRemoveNetworkWarning( currencyId: ManagedCryptoCurrency.ID, networkId: Network.ID, - ): Boolean = networkId !in currenciesToRemove.value[currencyId].orEmpty() && - networkId !in currenciesToAdd.value[currencyId].orEmpty() + ): Boolean = !changedCurrenciesManager.containsCurrency(currencyId, networkId) private fun sendSelectCurrencyAction( batchKey: Int, diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensListState.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensListState.kt index 20ba2a7e55..ac6732fd19 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensListState.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensListState.kt @@ -7,10 +7,12 @@ import com.tangem.domain.wallets.models.UserWalletId import com.tangem.features.managetokens.entity.CurrencyItemUM import com.tangem.pagination.Batch import com.tangem.pagination.BatchAction +import com.tangem.pagination.PaginationStatus internal typealias ManageTokensBatchAction = BatchAction internal data class ManageTokensListState( + val status: PaginationStatus<*> = PaginationStatus.None, val userWalletId: UserWalletId? = null, val uiBatches: List>> = mutableListOf(), val currencyBatches: List>> = mutableListOf(), diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensUiActions.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensUiActions.kt new file mode 100644 index 0000000000..9d73ea1e76 --- /dev/null +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensUiActions.kt @@ -0,0 +1,16 @@ +package com.tangem.features.managetokens.utils.list + +import com.tangem.domain.managetokens.model.ManagedCryptoCurrency +import com.tangem.domain.tokens.model.Network +import com.tangem.domain.wallets.models.UserWalletId + +internal interface ManageTokensUiActions { + + fun addCurrency(batchKey: Int, currencyId: ManagedCryptoCurrency.ID, networkId: Network.ID) + + fun removeCurrency(batchKey: Int, currencyId: ManagedCryptoCurrency.ID, networkId: Network.ID) + + fun checkNeedToShowRemoveNetworkWarning(currencyId: ManagedCryptoCurrency.ID, networkId: Network.ID): Boolean + + suspend fun checkHasLinkedTokens(userWalletId: UserWalletId, network: Network): Boolean +} \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensUiManager.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensUiManager.kt index f52bd4970d..ffb78bccf2 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensUiManager.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensUiManager.kt @@ -4,7 +4,6 @@ import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.ui.message.ContentMessage import com.tangem.domain.managetokens.model.ManagedCryptoCurrency import com.tangem.domain.tokens.model.Network -import com.tangem.domain.wallets.models.UserWalletId import com.tangem.features.managetokens.entity.CurrencyItemUM import com.tangem.features.managetokens.ui.dialog.HasLinkedTokensWarning import com.tangem.features.managetokens.ui.dialog.HideTokenWarning @@ -12,21 +11,36 @@ import com.tangem.features.managetokens.utils.mapper.toUiModel import com.tangem.features.managetokens.utils.ui.toggleExpanded import com.tangem.features.managetokens.utils.ui.update import com.tangem.pagination.Batch +import com.tangem.utils.Provider import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.update +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch -internal abstract class ManageTokensUiManager( +internal class ManageTokensUiManager( + private val state: MutableStateFlow, private val messageSender: UiMessageSender, private val dispatchers: CoroutineDispatcherProvider, + private val scopeProvider: Provider, + private val actions: ManageTokensUiActions, ) { - abstract val scope: CoroutineScope - abstract val state: MutableStateFlow + private val scope: CoroutineScope + get() = scopeProvider() - protected fun getUiBatches( + @OptIn(ExperimentalCoroutinesApi::class) + val items: Flow> = state + .mapLatest { state -> + state.uiBatches.asSequence() + .flatMap { it.data } + .toImmutableList() + } + .distinctUntilChanged() + + fun createOrUpdateUiBatches( newCurrencyBatches: List>>, canEditItems: Boolean, ): List>> { @@ -128,32 +142,23 @@ internal abstract class ManageTokensUiManager( if (currency !is ManagedCryptoCurrency.Token) return@launch if (isSelected) { - addCurrency(batchKey, currency.id, source.id) + actions.addCurrency(batchKey, currency.id, source.id) } else { - if (checkNeedToShowRemoveNetworkWarning(currency.id, source.id)) { + if (actions.checkNeedToShowRemoveNetworkWarning(currency.id, source.id)) { showRemoveNetworkWarning( currency = currency, network = source.network, isCoin = source is ManagedCryptoCurrency.SourceNetwork.Main, onConfirm = { - removeCurrency(batchKey, currency.id, source.id) + actions.removeCurrency(batchKey, currency.id, source.id) }, ) } else { - removeCurrency(batchKey, currency.id, source.id) + actions.removeCurrency(batchKey, currency.id, source.id) } } } - protected abstract fun addCurrency(batchKey: Int, currencyId: ManagedCryptoCurrency.ID, networkId: Network.ID) - - protected abstract fun removeCurrency(batchKey: Int, currencyId: ManagedCryptoCurrency.ID, networkId: Network.ID) - - protected abstract fun checkNeedToShowRemoveNetworkWarning( - currencyId: ManagedCryptoCurrency.ID, - networkId: Network.ID, - ): Boolean - private suspend fun showRemoveNetworkWarning( currency: ManagedCryptoCurrency, network: Network, @@ -164,7 +169,7 @@ internal abstract class ManageTokensUiManager( val hasLinkedTokens = if (userWalletId == null || !isCoin) { false } else { - checkHasLinkedTokens(userWalletId, network) + actions.checkHasLinkedTokens(userWalletId, network) } val message = ContentMessage { onDismiss -> @@ -189,8 +194,6 @@ internal abstract class ManageTokensUiManager( messageSender.send(message) } - protected abstract suspend fun checkHasLinkedTokens(userWalletId: UserWalletId, network: Network): Boolean - private fun Batch>.currencyIndexById(id: ManagedCryptoCurrency.ID): Int { return data .indexOfFirst { it.id == id }