Updated on 2026-08-14
This commit is contained in:
parent
5b4f0214d2
commit
fe09323d64
7 changed files with 103 additions and 69 deletions
|
|
@ -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]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<CurrencyItemUM>,
|
||||
|
|
@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,12 +7,25 @@ import kotlinx.coroutines.flow.update
|
|||
|
||||
internal typealias ChangedCurrencies = Map<ManagedCryptoCurrency.ID, Set<Network.ID>>
|
||||
|
||||
internal interface ChangedCurrenciesManager {
|
||||
internal class ChangedCurrenciesManager {
|
||||
|
||||
val currenciesToAdd: MutableStateFlow<ChangedCurrencies>
|
||||
val currenciesToRemove: MutableStateFlow<ChangedCurrencies>
|
||||
val currenciesToAdd: MutableStateFlow<ChangedCurrencies> = MutableStateFlow(emptyMap())
|
||||
val currenciesToRemove: MutableStateFlow<ChangedCurrencies> = 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<ChangedCurrencies>,
|
||||
|
|
|
|||
|
|
@ -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<ManageTokensBatchAction>(
|
||||
private val actionsFlow: MutableSharedFlow<ManageTokensBatchAction> = MutableSharedFlow(
|
||||
replay = 1,
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST,
|
||||
)
|
||||
|
||||
override val state: MutableStateFlow<ManageTokensListState> = MutableStateFlow(ManageTokensListState())
|
||||
private val state: MutableStateFlow<ManageTokensListState> = MutableStateFlow(ManageTokensListState())
|
||||
|
||||
val paginationStatus: MutableStateFlow<PaginationStatus<*>> = 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<ChangedCurrencies> = MutableStateFlow(emptyMap())
|
||||
override val currenciesToRemove: MutableStateFlow<ChangedCurrencies> = MutableStateFlow(emptyMap())
|
||||
val currenciesToAdd: StateFlow<ChangedCurrencies> = changedCurrenciesManager.currenciesToAdd
|
||||
val currenciesToRemove: StateFlow<ChangedCurrencies> = changedCurrenciesManager.currenciesToRemove
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val uiItems: Flow<ImmutableList<CurrencyItemUM>> = state
|
||||
.mapLatest { state ->
|
||||
state.uiBatches.asSequence()
|
||||
.flatMap { it.data }
|
||||
.toImmutableList()
|
||||
}
|
||||
val paginationStatus: Flow<PaginationStatus<*>> = state
|
||||
.mapLatest { it.status }
|
||||
.distinctUntilChanged()
|
||||
val uiItems: Flow<ImmutableList<CurrencyItemUM>> = 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<Int, List<ManagedCryptoCurrency>>,
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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<Int, ManageTokensListConfig, ManageTokensUpdateAction>
|
||||
|
||||
internal data class ManageTokensListState(
|
||||
val status: PaginationStatus<*> = PaginationStatus.None,
|
||||
val userWalletId: UserWalletId? = null,
|
||||
val uiBatches: List<Batch<Int, List<CurrencyItemUM>>> = mutableListOf(),
|
||||
val currencyBatches: List<Batch<Int, List<ManagedCryptoCurrency>>> = mutableListOf(),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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<ManageTokensListState>,
|
||||
private val messageSender: UiMessageSender,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val scopeProvider: Provider<CoroutineScope>,
|
||||
private val actions: ManageTokensUiActions,
|
||||
) {
|
||||
|
||||
abstract val scope: CoroutineScope
|
||||
abstract val state: MutableStateFlow<ManageTokensListState>
|
||||
private val scope: CoroutineScope
|
||||
get() = scopeProvider()
|
||||
|
||||
protected fun getUiBatches(
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val items: Flow<ImmutableList<CurrencyItemUM>> = state
|
||||
.mapLatest { state ->
|
||||
state.uiBatches.asSequence()
|
||||
.flatMap { it.data }
|
||||
.toImmutableList()
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
|
||||
fun createOrUpdateUiBatches(
|
||||
newCurrencyBatches: List<Batch<Int, List<ManagedCryptoCurrency>>>,
|
||||
canEditItems: Boolean,
|
||||
): List<Batch<Int, List<CurrencyItemUM>>> {
|
||||
|
|
@ -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<Int, List<ManagedCryptoCurrency>>.currencyIndexById(id: ManagedCryptoCurrency.ID): Int {
|
||||
return data
|
||||
.indexOfFirst { it.id == id }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue