Updated on 2026-08-14
This commit is contained in:
parent
7aa44c4da7
commit
8cb232cd36
11 changed files with 204 additions and 162 deletions
|
|
@ -9,7 +9,6 @@ import arrow.core.toNonEmptyListOrNull
|
|||
import arrow.core.toNonEmptySetOrNull
|
||||
import com.tangem.domain.tokens.error.TokenListSortingError
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
import com.tangem.domain.tokens.models.Network
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -23,7 +22,7 @@ class ApplyTokenListSortingUseCase(
|
|||
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
sortedTokensIds: List<Pair<Network.ID, CryptoCurrency.ID>>,
|
||||
sortedTokensIds: List<CryptoCurrency.ID>,
|
||||
isGroupedByNetwork: Boolean,
|
||||
isSortedByBalance: Boolean,
|
||||
): Either<TokenListSortingError, Unit> {
|
||||
|
|
@ -40,7 +39,7 @@ class ApplyTokenListSortingUseCase(
|
|||
}
|
||||
|
||||
private suspend fun Raise<TokenListSortingError>.sortTokens(
|
||||
sortedTokensIds: List<Pair<Network.ID, CryptoCurrency.ID>>,
|
||||
sortedTokensIds: List<CryptoCurrency.ID>,
|
||||
unsortedTokens: List<CryptoCurrency>,
|
||||
): List<CryptoCurrency> = withContext(dispatchers.default) {
|
||||
val nonEmptySortedTokensIds = ensureNotNull(sortedTokensIds.toNonEmptySetOrNull()) {
|
||||
|
|
@ -49,13 +48,13 @@ class ApplyTokenListSortingUseCase(
|
|||
|
||||
val sortedTokens = sortedMapOf<Int, CryptoCurrency>()
|
||||
|
||||
unsortedTokens.distinct().forEach { token ->
|
||||
val index = nonEmptySortedTokensIds.indexOfFirst { (networkId, tokenId) ->
|
||||
networkId == token.networkId && tokenId == token.id
|
||||
unsortedTokens.distinct().forEach { currency ->
|
||||
val index = nonEmptySortedTokensIds.indexOfFirst { currencyId ->
|
||||
currencyId == currency.id
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
sortedTokens[index] = token
|
||||
sortedTokens[index] = currency
|
||||
} else {
|
||||
raise(TokenListSortingError.UnableToSortTokenList)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ internal class ApplyTokenListSortingUseCaseTest {
|
|||
// When
|
||||
val result = useCase(
|
||||
userWalletId = userWalletId,
|
||||
sortedTokensIds = MockTokens.tokens.map { it.networkId to it.id },
|
||||
sortedTokensIds = MockTokens.tokens.map { it.id },
|
||||
isGroupedByNetwork = false,
|
||||
isSortedByBalance = false,
|
||||
)
|
||||
|
|
@ -76,7 +76,7 @@ internal class ApplyTokenListSortingUseCaseTest {
|
|||
// When
|
||||
useCase(
|
||||
userWalletId = userWalletId,
|
||||
sortedTokensIds = expectedTokens.map { it.networkId to it.id },
|
||||
sortedTokensIds = expectedTokens.map { it.id },
|
||||
isGroupedByNetwork = expectedIsGrouped,
|
||||
isSortedByBalance = expectedIsSorted,
|
||||
)
|
||||
|
|
@ -100,7 +100,7 @@ internal class ApplyTokenListSortingUseCaseTest {
|
|||
// When
|
||||
useCase(
|
||||
userWalletId = userWalletId,
|
||||
sortedTokensIds = expectedTokens.map { it.networkId to it.id },
|
||||
sortedTokensIds = expectedTokens.map { it.id },
|
||||
isGroupedByNetwork = expectedIsGrouped,
|
||||
isSortedByBalance = expectedIsSorted,
|
||||
)
|
||||
|
|
@ -124,7 +124,7 @@ internal class ApplyTokenListSortingUseCaseTest {
|
|||
// When
|
||||
useCase(
|
||||
userWalletId = userWalletId,
|
||||
sortedTokensIds = expectedTokens.map { it.networkId to it.id },
|
||||
sortedTokensIds = expectedTokens.map { it.id },
|
||||
isGroupedByNetwork = expectedIsGrouped,
|
||||
isSortedByBalance = expectedIsSorted,
|
||||
)
|
||||
|
|
@ -148,7 +148,7 @@ internal class ApplyTokenListSortingUseCaseTest {
|
|||
// When
|
||||
useCase(
|
||||
userWalletId = userWalletId,
|
||||
sortedTokensIds = expectedTokens.map { it.networkId to it.id },
|
||||
sortedTokensIds = expectedTokens.map { it.id },
|
||||
isGroupedByNetwork = expectedIsGrouped,
|
||||
isSortedByBalance = expectedIsSorted,
|
||||
)
|
||||
|
|
@ -170,7 +170,7 @@ internal class ApplyTokenListSortingUseCaseTest {
|
|||
// When
|
||||
val result = useCase(
|
||||
userWalletId = userWalletId,
|
||||
sortedTokensIds = getSortedTokens().drop(n = 3).map { it.networkId to it.id },
|
||||
sortedTokensIds = getSortedTokens().drop(n = 3).map { it.id },
|
||||
isGroupedByNetwork = false,
|
||||
isSortedByBalance = false,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ plugins {
|
|||
dependencies {
|
||||
/** AndroidX */
|
||||
implementation(deps.androidx.activity.compose)
|
||||
implementation(deps.lifecycle.compose)
|
||||
implementation(deps.material)
|
||||
|
||||
/** Compose */
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
package com.tangem.feature.wallet.presentation.organizetokens
|
||||
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
|
||||
import org.burnoutcrew.reorderable.ItemPosition
|
||||
|
||||
internal interface OrganizeTokensIntents {
|
||||
|
||||
fun onBackClick()
|
||||
|
|
@ -14,12 +11,4 @@ internal interface OrganizeTokensIntents {
|
|||
fun onApplyClick()
|
||||
|
||||
fun onCancelClick()
|
||||
|
||||
fun onItemDragged(from: ItemPosition, to: ItemPosition)
|
||||
|
||||
fun canDragItemOver(dragOver: ItemPosition, dragging: ItemPosition): Boolean
|
||||
|
||||
fun onItemDraggingStart(item: DraggableItem)
|
||||
|
||||
fun onItemDraggingEnd()
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.organizetokens
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
|
|
@ -38,6 +39,8 @@ import org.burnoutcrew.reorderable.*
|
|||
|
||||
@Composable
|
||||
internal fun OrganizeTokensScreen(state: OrganizeTokensState, modifier: Modifier = Modifier) {
|
||||
BackHandler(onBack = state.onBackClick)
|
||||
|
||||
val tokensListState = rememberLazyListState()
|
||||
|
||||
Scaffold(
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
package com.tangem.feature.wallet.presentation.organizetokens
|
||||
|
||||
import com.tangem.common.Provider
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.error.TokenListSortingError
|
||||
import com.tangem.domain.tokens.model.TokenList
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.updateSorting
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.InProgressStateConverter
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.TokenListToStateConverter
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.error.TokenListErrorConverter
|
||||
|
|
@ -17,22 +17,17 @@ import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.ite
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
@Suppress("unused", "MemberVisibilityCanBePrivate") // TODO: Will be used in next MR
|
||||
internal class OrganizeTokensStateHolder(
|
||||
private val intents: OrganizeTokensIntents,
|
||||
private val fiatCurrencyCode: String,
|
||||
private val fiatCurrencySymbol: String,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val onSubscription: () -> Unit,
|
||||
scope: CoroutineScope,
|
||||
stateFlowScope: CoroutineScope,
|
||||
) {
|
||||
|
||||
private val stateFlowInternal: MutableStateFlow<OrganizeTokensState> = MutableStateFlow(getInitialState())
|
||||
|
||||
private val tokenListConverter by lazy {
|
||||
val tokensConverter = CryptoCurrencyToDraggableItemConverter(
|
||||
fiatCurrencyCode = fiatCurrencyCode,
|
||||
fiatCurrencySymbol = fiatCurrencySymbol,
|
||||
)
|
||||
val tokensConverter = CryptoCurrencyToDraggableItemConverter(appCurrencyProvider)
|
||||
val itemsConverter = TokenListToListStateConverter(
|
||||
tokensConverter = tokensConverter,
|
||||
groupsConverter = NetworkGroupToDraggableItemsConverter(tokensConverter),
|
||||
|
|
@ -56,17 +51,13 @@ internal class OrganizeTokensStateHolder(
|
|||
val stateFlow: StateFlow<OrganizeTokensState> = stateFlowInternal
|
||||
.onSubscription { onSubscription() }
|
||||
.stateIn(
|
||||
scope = scope,
|
||||
scope = stateFlowScope,
|
||||
started = SharingStarted.WhileSubscribed(),
|
||||
initialValue = getInitialState(),
|
||||
)
|
||||
|
||||
var tokenList: TokenList? = null
|
||||
private set
|
||||
|
||||
fun updateStateWithTokenList(tokenList: TokenList) {
|
||||
updateState { tokenListConverter.convert(tokenList) }
|
||||
this.tokenList = tokenList
|
||||
}
|
||||
|
||||
fun updateStateToDisplayProgress() {
|
||||
|
|
@ -77,16 +68,6 @@ internal class OrganizeTokensStateHolder(
|
|||
updateState { inProgressStateConverter.convertBack(value = this) }
|
||||
}
|
||||
|
||||
fun updateStateWithManualSorting(itemsState: OrganizeTokensListState) {
|
||||
updateState {
|
||||
copy(
|
||||
header = header.copy(isSortedByBalance = false),
|
||||
itemsState = itemsState,
|
||||
)
|
||||
}
|
||||
tokenList = tokenList?.updateSorting(isSortedByBalance = false)
|
||||
}
|
||||
|
||||
fun updateStateWithError(error: TokenListError) {
|
||||
updateState { tokenListErrorConverter.convert(error) }
|
||||
}
|
||||
|
|
@ -107,11 +88,12 @@ internal class OrganizeTokensStateHolder(
|
|||
onApplyClick = intents::onApplyClick,
|
||||
onCancelClick = intents::onCancelClick,
|
||||
),
|
||||
// TODO: Will be added in next MR
|
||||
dndConfig = OrganizeTokensState.DragAndDropConfig(
|
||||
onItemDragged = intents::onItemDragged,
|
||||
onDragStart = intents::onItemDraggingStart,
|
||||
onItemDragEnd = intents::onItemDraggingEnd,
|
||||
canDragItemOver = intents::canDragItemOver,
|
||||
onItemDragged = { _, _ -> },
|
||||
onDragStart = { },
|
||||
onItemDragEnd = { },
|
||||
canDragItemOver = { _, _ -> false },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,133 +1,147 @@
|
|||
package com.tangem.feature.wallet.presentation.organizetokens
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.Provider
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
|
||||
import com.tangem.domain.tokens.GetTokenListUseCase
|
||||
import com.tangem.domain.tokens.ToggleTokenListGroupingUseCase
|
||||
import com.tangem.domain.tokens.ToggleTokenListSortingUseCase
|
||||
import com.tangem.domain.tokens.model.TokenList
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.*
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.CryptoCurrenciesIdsResolver
|
||||
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
|
||||
import com.tangem.feature.wallet.presentation.router.WalletRoute
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import org.burnoutcrew.reorderable.ItemPosition
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
// FIXME: Implemented with preview data
|
||||
@HiltViewModel
|
||||
internal class OrganizeTokensViewModel @Inject constructor(savedStateHandle: SavedStateHandle) : ViewModel() {
|
||||
internal class OrganizeTokensViewModel @Inject constructor(
|
||||
private val getTokenListUseCase: GetTokenListUseCase,
|
||||
private val toggleTokenListGroupingUseCase: ToggleTokenListGroupingUseCase,
|
||||
private val toggleTokenListSortingUseCase: ToggleTokenListSortingUseCase,
|
||||
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel(), OrganizeTokensIntents {
|
||||
|
||||
@Volatile
|
||||
private var movingItem: DraggableItem? = null
|
||||
lateinit var router: InnerWalletRouter
|
||||
|
||||
var router: InnerWalletRouter by Delegates.notNull()
|
||||
val userWalletId: UserWalletId by lazy {
|
||||
private val selectedAppCurrencyFlow = createSelectedAppCurrencyFlow()
|
||||
|
||||
private val stateHolder = OrganizeTokensStateHolder(
|
||||
stateFlowScope = viewModelScope,
|
||||
intents = this,
|
||||
appCurrencyProvider = Provider(selectedAppCurrencyFlow::value),
|
||||
onSubscription = {
|
||||
bootstrapTokenList()
|
||||
},
|
||||
)
|
||||
|
||||
private val userWalletId: UserWalletId by lazy {
|
||||
val userWalletIdValue: String = checkNotNull(savedStateHandle[WalletRoute.userWalletIdKey])
|
||||
|
||||
UserWalletId(userWalletIdValue)
|
||||
}
|
||||
|
||||
var uiState: OrganizeTokensState by mutableStateOf(getInitialState())
|
||||
private set
|
||||
private var tokenList: TokenList? = null
|
||||
|
||||
private fun getInitialState(): OrganizeTokensState = WalletPreviewData.organizeTokensState.copy(
|
||||
itemsState = OrganizeTokensListState.Ungrouped(
|
||||
items = WalletPreviewData.draggableTokens,
|
||||
),
|
||||
dndConfig = OrganizeTokensState.DragAndDropConfig(
|
||||
onItemDragged = this::moveItem,
|
||||
canDragItemOver = this::checkCanMoveItemOver,
|
||||
onItemDragEnd = this::endMoving,
|
||||
onDragStart = this::startMoving,
|
||||
),
|
||||
header = OrganizeTokensState.HeaderConfig(
|
||||
onSortClick = { /* no-op */ },
|
||||
onGroupClick = this::toggleTokensByNetworkGrouping,
|
||||
),
|
||||
)
|
||||
val uiState: StateFlow<OrganizeTokensState> = stateHolder.stateFlow
|
||||
|
||||
private fun toggleTokensByNetworkGrouping() {
|
||||
val newListState = when (val itemsState = uiState.itemsState) {
|
||||
is OrganizeTokensListState.GroupedByNetwork -> OrganizeTokensListState.Ungrouped(
|
||||
items = itemsState.items.filterIsInstance<DraggableItem.Token>().toPersistentList(),
|
||||
override fun onBackClick() {
|
||||
router.popBackStack()
|
||||
}
|
||||
|
||||
override fun onSortClick() {
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
val list = tokenList ?: return@launch
|
||||
|
||||
toggleTokenListSortingUseCase(list).fold(
|
||||
ifLeft = stateHolder::updateStateWithError,
|
||||
ifRight = {
|
||||
stateHolder.updateStateWithTokenList(it)
|
||||
tokenList = it
|
||||
},
|
||||
)
|
||||
is OrganizeTokensListState.Ungrouped -> OrganizeTokensListState.GroupedByNetwork(
|
||||
items = WalletPreviewData.draggableItems,
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGroupClick() {
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
val list = tokenList ?: return@launch
|
||||
|
||||
toggleTokenListGroupingUseCase(list).fold(
|
||||
ifLeft = stateHolder::updateStateWithError,
|
||||
ifRight = {
|
||||
stateHolder.updateStateWithTokenList(it)
|
||||
tokenList = it
|
||||
},
|
||||
)
|
||||
is OrganizeTokensListState.Empty -> itemsState
|
||||
}
|
||||
|
||||
uiState = uiState.copy(itemsState = newListState)
|
||||
}
|
||||
|
||||
private fun checkCanMoveItemOver(moveOverItemPosition: ItemPosition, movedItemPosition: ItemPosition): Boolean {
|
||||
val items = (uiState.itemsState as? OrganizeTokensListState.GroupedByNetwork)
|
||||
?.items
|
||||
?: return true // If ungrouped then item can be moved anywhere
|
||||
|
||||
val (moveOverItem, movedItem) = items.findItemsToMove(moveOverItemPosition.key, movedItemPosition.key)
|
||||
|
||||
if (moveOverItem == null || movedItem == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
return when (movedItem) {
|
||||
is DraggableItem.GroupHeader -> checkCanMoveHeaderOver(moveOverItemPosition, moveOverItem, items.lastIndex)
|
||||
is DraggableItem.Token -> checkCanMoveTokenOver(movedItem, moveOverItem)
|
||||
is DraggableItem.GroupPlaceholder -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun startMoving(movingItem: DraggableItem) = viewModelScope.launch(Dispatchers.Default) {
|
||||
if (this@OrganizeTokensViewModel.movingItem != null) return@launch
|
||||
this@OrganizeTokensViewModel.movingItem = movingItem
|
||||
override fun onApplyClick() {
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
stateHolder.updateStateToDisplayProgress()
|
||||
|
||||
val updatedItemsState = uiState.itemsState.updateItems { items ->
|
||||
when (movingItem) {
|
||||
is DraggableItem.GroupHeader -> items.collapseGroup(movingItem)
|
||||
is DraggableItem.Token -> when (uiState.itemsState) {
|
||||
is OrganizeTokensListState.GroupedByNetwork -> items.divideGroups(movingItem)
|
||||
is OrganizeTokensListState.Ungrouped -> items.divideItems(movingItem)
|
||||
is OrganizeTokensListState.Empty -> uiState.itemsState.items
|
||||
}
|
||||
is DraggableItem.GroupPlaceholder -> items
|
||||
val listState = uiState.value.itemsState
|
||||
val resolver = CryptoCurrenciesIdsResolver()
|
||||
|
||||
val result = applyTokenListSortingUseCase(
|
||||
userWalletId = userWalletId,
|
||||
sortedTokensIds = resolver.resolve(listState, tokenList),
|
||||
isGroupedByNetwork = listState is OrganizeTokensListState.GroupedByNetwork,
|
||||
isSortedByBalance = uiState.value.header.isSortedByBalance,
|
||||
)
|
||||
|
||||
result.fold(
|
||||
ifLeft = stateHolder::updateStateWithError,
|
||||
ifRight = {
|
||||
stateHolder.updateStateToHideProgress()
|
||||
withContext(Dispatchers.Main) { router.popBackStack() }
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCancelClick() {
|
||||
router.popBackStack()
|
||||
}
|
||||
|
||||
private fun bootstrapTokenList() {
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
val maybeTokenList = getTokenListUseCase(userWalletId)
|
||||
.first { it.getOrNull()?.totalFiatBalance is TokenList.FiatBalance.Loaded }
|
||||
|
||||
maybeTokenList.fold(
|
||||
ifLeft = stateHolder::updateStateWithError,
|
||||
ifRight = {
|
||||
stateHolder.updateStateWithTokenList(it)
|
||||
tokenList = it
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSelectedAppCurrencyFlow(): StateFlow<AppCurrency> {
|
||||
return getSelectedAppCurrencyUseCase()
|
||||
.map { maybeAppCurrency ->
|
||||
maybeAppCurrency.getOrElse { AppCurrency.Default }
|
||||
}
|
||||
}
|
||||
|
||||
uiState = uiState.copy(itemsState = updatedItemsState)
|
||||
}
|
||||
|
||||
private fun endMoving() = viewModelScope.launch(Dispatchers.Default) {
|
||||
if (movingItem == null) return@launch
|
||||
|
||||
val updatedItemsState = uiState.itemsState.updateItems { items ->
|
||||
when (movingItem) {
|
||||
is DraggableItem.GroupHeader -> items.expandGroups()
|
||||
is DraggableItem.Token -> items.uniteItems()
|
||||
is DraggableItem.GroupPlaceholder,
|
||||
null,
|
||||
-> items
|
||||
}
|
||||
}
|
||||
|
||||
uiState = uiState.copy(itemsState = updatedItemsState)
|
||||
movingItem = null
|
||||
}
|
||||
|
||||
private fun moveItem(from: ItemPosition, to: ItemPosition) = viewModelScope.launch(Dispatchers.Default) {
|
||||
uiState = uiState.copy(
|
||||
itemsState = uiState.itemsState.updateItems {
|
||||
it.moveItem(from.index, to.index)
|
||||
},
|
||||
)
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = AppCurrency.Default,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package com.tangem.feature.wallet.presentation.organizetokens.utils
|
||||
|
||||
import com.tangem.domain.tokens.model.TokenList
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState
|
||||
|
||||
internal class CryptoCurrenciesIdsResolver {
|
||||
|
||||
fun resolve(listState: OrganizeTokensListState, tokenList: TokenList?): List<CryptoCurrency.ID> {
|
||||
val draggableTokens = when (listState) {
|
||||
is OrganizeTokensListState.Empty -> return emptyList()
|
||||
is OrganizeTokensListState.GroupedByNetwork -> listState.items.filterIsInstance<DraggableItem.Token>()
|
||||
is OrganizeTokensListState.Ungrouped -> listState.items
|
||||
}
|
||||
val currenciesStatuses = when (tokenList) {
|
||||
is TokenList.GroupedByNetwork -> tokenList.groups.flatMap { it.currencies }
|
||||
is TokenList.Ungrouped -> tokenList.currencies
|
||||
is TokenList.NotInitialized,
|
||||
null,
|
||||
-> return emptyList()
|
||||
}
|
||||
|
||||
return draggableTokens.mapNotNull { draggableToken ->
|
||||
val currencyStatus = currenciesStatuses.firstOrNull {
|
||||
it.currency.id.value == draggableToken.id
|
||||
}
|
||||
|
||||
currencyStatus?.currency?.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
package com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.tangem.common.Provider
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
|
|
@ -12,8 +14,7 @@ import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getTok
|
|||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class CryptoCurrencyToDraggableItemConverter(
|
||||
private val fiatCurrencyCode: String,
|
||||
private val fiatCurrencySymbol: String,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
) : Converter<CryptoCurrencyStatus, DraggableItem.Token> {
|
||||
|
||||
private val CryptoCurrency.networkIconResId: Int?
|
||||
|
|
@ -29,13 +30,29 @@ internal class CryptoCurrencyToDraggableItemConverter(
|
|||
}
|
||||
|
||||
override fun convert(value: CryptoCurrencyStatus): DraggableItem.Token {
|
||||
return createDraggableToken(value, appCurrencyProvider())
|
||||
}
|
||||
|
||||
override fun convertList(input: Collection<CryptoCurrencyStatus>): List<DraggableItem.Token> {
|
||||
val appCurrency = appCurrencyProvider()
|
||||
|
||||
return input.map { createDraggableToken(it, appCurrency) }
|
||||
}
|
||||
|
||||
private fun createDraggableToken(
|
||||
currencyStatus: CryptoCurrencyStatus,
|
||||
appCurrency: AppCurrency,
|
||||
): DraggableItem.Token {
|
||||
return DraggableItem.Token(
|
||||
tokenItemState = createToTokenItemState(value),
|
||||
groupId = getGroupHeaderId(value.currency.networkId),
|
||||
tokenItemState = createTokenItemState(currencyStatus, appCurrency),
|
||||
groupId = getGroupHeaderId(currencyStatus.currency.networkId),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createToTokenItemState(currencyStatus: CryptoCurrencyStatus): TokenItemState.Draggable {
|
||||
private fun createTokenItemState(
|
||||
currencyStatus: CryptoCurrencyStatus,
|
||||
appCurrency: AppCurrency,
|
||||
): TokenItemState.Draggable {
|
||||
val currency = currencyStatus.currency
|
||||
|
||||
return TokenItemState.Draggable(
|
||||
|
|
@ -44,13 +61,13 @@ internal class CryptoCurrencyToDraggableItemConverter(
|
|||
tokenIconResId = currency.tokenIconResId,
|
||||
networkIconResId = currency.networkIconResId,
|
||||
name = currency.name,
|
||||
fiatAmount = getFormattedFiatAmount(currencyStatus),
|
||||
fiatAmount = getFormattedFiatAmount(currencyStatus, appCurrency),
|
||||
)
|
||||
}
|
||||
|
||||
private fun getFormattedFiatAmount(currency: CryptoCurrencyStatus): String {
|
||||
private fun getFormattedFiatAmount(currency: CryptoCurrencyStatus, appCurrency: AppCurrency): String {
|
||||
val fiatAmount = currency.value.fiatAmount ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN
|
||||
|
||||
return BigDecimalFormatter.formatFiatAmount(fiatAmount, fiatCurrencyCode, fiatCurrencySymbol)
|
||||
return BigDecimalFormatter.formatFiatAmount(fiatAmount, appCurrency.code, appCurrency.symbol)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,15 @@
|
|||
package com.tangem.feature.wallet.presentation.router
|
||||
|
||||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.compose.NavHost
|
||||
|
|
@ -59,9 +61,11 @@ internal class DefaultWalletRouter(private val navigationStateHolder: Navigation
|
|||
router = this@DefaultWalletRouter
|
||||
}
|
||||
|
||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
OrganizeTokensScreen(
|
||||
modifier = Modifier.systemBarsPadding(),
|
||||
state = viewModel.uiState,
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
state = uiState,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ androidx-paging-runtime = { module = "androidx.paging:paging-runtime", version.r
|
|||
lifecycle-common-java8 = { module = "androidx.lifecycle:lifecycle-common-java8", version.ref = "androidxLifecycle" }
|
||||
lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidxLifecycle" }
|
||||
lifecycle-viewModel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidxLifecycle" }
|
||||
lifecycle-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidxLifecycle" }
|
||||
# region AndroidX
|
||||
|
||||
# region Compose
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue