Updated on 2026-08-14
This commit is contained in:
parent
6386cb1ea4
commit
b97ea5faf8
10 changed files with 342 additions and 60 deletions
|
|
@ -22,6 +22,7 @@ dependencies {
|
|||
implementation(deps.compose.ui.tooling)
|
||||
implementation(deps.compose.shimmer)
|
||||
implementation(deps.compose.accompanist.systemUiController)
|
||||
implementation(deps.compose.reorderable)
|
||||
|
||||
/** Other libraries */
|
||||
implementation(deps.kotlin.immutable.collections)
|
||||
|
|
|
|||
|
|
@ -105,21 +105,22 @@ internal object WalletPreviewData {
|
|||
private const val tokensSize = 3
|
||||
val draggableItems = List(networksSize) { it }
|
||||
.flatMap { index ->
|
||||
val n = index + 1
|
||||
val lastNetworkIndex = networksSize - 1
|
||||
val networkNumber = index + 1
|
||||
|
||||
val group = DraggableItem.GroupHeader(
|
||||
id = "group_$n",
|
||||
networkName = "$n",
|
||||
id = "group_$networkNumber",
|
||||
networkName = "$networkNumber",
|
||||
)
|
||||
|
||||
val tokens: MutableList<DraggableItem.Token> = mutableListOf()
|
||||
repeat(times = tokensSize) { i ->
|
||||
val nt = i + 1
|
||||
val tokenNumber = i + 1
|
||||
tokens.add(
|
||||
DraggableItem.Token(
|
||||
tokenItemState = tokenItemDragState.copy(
|
||||
id = "${group.id}_token_$nt",
|
||||
name = "Token $nt",
|
||||
id = "${group.id}_token_$tokenNumber",
|
||||
name = "Token $tokenNumber",
|
||||
networkIconResId = R.drawable.img_eth_22.takeIf { i != 0 },
|
||||
),
|
||||
groupId = group.id,
|
||||
|
|
@ -127,9 +128,14 @@ internal object WalletPreviewData {
|
|||
)
|
||||
}
|
||||
|
||||
val divider = DraggableItem.GroupPlaceholder(id = "divider_$networkNumber")
|
||||
|
||||
buildList {
|
||||
add(group)
|
||||
addAll(tokens)
|
||||
if (index != lastNetworkIndex) {
|
||||
add(divider)
|
||||
}
|
||||
}
|
||||
}
|
||||
.toPersistentList()
|
||||
|
|
@ -146,6 +152,12 @@ internal object WalletPreviewData {
|
|||
onSortByBalanceClick = {},
|
||||
onGroupByNetworkClick = {},
|
||||
),
|
||||
dragConfig = OrganizeTokensStateHolder.DragConfig(
|
||||
onItemDragged = { _, _ -> },
|
||||
onDragStart = {},
|
||||
canDragItemOver = { _, _ -> false },
|
||||
onItemDragEnd = {},
|
||||
),
|
||||
actions = OrganizeTokensStateHolder.ActionsConfig(
|
||||
onApplyClick = {},
|
||||
onCancelClick = {},
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
|||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import org.burnoutcrew.reorderable.ReorderableLazyListState
|
||||
import org.burnoutcrew.reorderable.detectReorder
|
||||
|
||||
@Composable
|
||||
internal fun NetworkGroupItem(networkName: String, modifier: Modifier = Modifier) {
|
||||
|
|
@ -24,16 +26,33 @@ internal fun NetworkGroupItem(networkName: String, modifier: Modifier = Modifier
|
|||
}
|
||||
|
||||
@Composable
|
||||
internal fun DraggableNetworkGroupItem(networkName: String, modifier: Modifier = Modifier) {
|
||||
internal fun DraggableNetworkGroupItem(
|
||||
networkName: String,
|
||||
modifier: Modifier = Modifier,
|
||||
reorderableTokenListState: ReorderableLazyListState? = null,
|
||||
) {
|
||||
InternalNetworkGroupItem(
|
||||
modifier = modifier,
|
||||
networkName = networkName,
|
||||
endIcon = {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_group_drop_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size32)
|
||||
.let {
|
||||
if (reorderableTokenListState != null) {
|
||||
it.detectReorder(reorderableTokenListState)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
},
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_group_drop_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ import com.tangem.feature.wallet.impl.R
|
|||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TokenOptionsState
|
||||
import org.burnoutcrew.reorderable.ReorderableLazyListState
|
||||
import org.burnoutcrew.reorderable.detectReorder
|
||||
|
||||
private const val DOTS = "•••"
|
||||
val TOKEN_ITEM_HEIGHT: Dp
|
||||
|
|
@ -46,7 +48,7 @@ internal fun TokenItem(state: TokenItemState, modifier: Modifier = Modifier) {
|
|||
when (state) {
|
||||
is TokenItemState.Content -> ContentTokenItem(state, modifier)
|
||||
is TokenItemState.Loading -> LoadingTokenItem(modifier)
|
||||
is TokenItemState.Draggable -> DraggableTokenItem(state, modifier)
|
||||
is TokenItemState.Draggable -> DraggableTokenItem(state, modifier, reorderableTokenListState = null)
|
||||
is TokenItemState.Unreachable -> UnreachableTokenItem(state, modifier)
|
||||
}
|
||||
}
|
||||
|
|
@ -71,7 +73,11 @@ private fun ContentTokenItem(content: TokenItemState.Content, modifier: Modifier
|
|||
}
|
||||
|
||||
@Composable
|
||||
internal fun DraggableTokenItem(state: TokenItemState.Draggable, modifier: Modifier = Modifier) {
|
||||
internal fun DraggableTokenItem(
|
||||
state: TokenItemState.Draggable,
|
||||
modifier: Modifier = Modifier,
|
||||
reorderableTokenListState: ReorderableLazyListState? = null,
|
||||
) {
|
||||
InternalTokenItem(
|
||||
modifier = modifier,
|
||||
name = state.name,
|
||||
|
|
@ -81,13 +87,25 @@ internal fun DraggableTokenItem(state: TokenItemState.Draggable, modifier: Modif
|
|||
amount = state.fiatAmount,
|
||||
hasPending = false,
|
||||
options = { ref ->
|
||||
Icon(
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.constrainAsOptionsItem(scope = this, ref),
|
||||
painter = painterResource(id = R.drawable.ic_drag_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
.size(TangemTheme.dimens.size32)
|
||||
.constrainAsOptionsItem(scope = this, ref)
|
||||
.let {
|
||||
if (reorderableTokenListState != null) {
|
||||
it.detectReorder(reorderableTokenListState)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
},
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_drag_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,10 +9,7 @@ import androidx.compose.material.AppBarDefaults
|
|||
import androidx.compose.material3.FabPosition
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
|
|
@ -32,6 +29,10 @@ import com.tangem.feature.wallet.impl.R
|
|||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.common.component.DraggableNetworkGroupItem
|
||||
import com.tangem.feature.wallet.presentation.common.component.DraggableTokenItem
|
||||
import org.burnoutcrew.reorderable.ReorderableItem
|
||||
import org.burnoutcrew.reorderable.ReorderableLazyListState
|
||||
import org.burnoutcrew.reorderable.rememberReorderableLazyListState
|
||||
import org.burnoutcrew.reorderable.reorderable
|
||||
|
||||
@Composable
|
||||
internal fun OrganizeTokensScreen(state: OrganizeTokensStateHolder, modifier: Modifier = Modifier) {
|
||||
|
|
@ -47,6 +48,7 @@ internal fun OrganizeTokensScreen(state: OrganizeTokensStateHolder, modifier: Mo
|
|||
modifier = Modifier.padding(paddingValues),
|
||||
listState = tokensListState,
|
||||
state = state.itemsState,
|
||||
dragConfig = state.dragConfig,
|
||||
)
|
||||
},
|
||||
floatingActionButtonPosition = FabPosition.Center,
|
||||
|
|
@ -57,28 +59,56 @@ internal fun OrganizeTokensScreen(state: OrganizeTokensStateHolder, modifier: Mo
|
|||
)
|
||||
}
|
||||
|
||||
// TODO: Fix list animations
|
||||
@Composable
|
||||
private fun TokenList(listState: LazyListState, state: OrganizeTokensListState, modifier: Modifier = Modifier) {
|
||||
private fun TokenList(
|
||||
listState: LazyListState,
|
||||
state: OrganizeTokensListState,
|
||||
dragConfig: OrganizeTokensStateHolder.DragConfig,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(modifier = modifier) {
|
||||
val reorderableListState = rememberReorderableLazyListState(
|
||||
onMove = dragConfig.onItemDragged,
|
||||
listState = listState,
|
||||
canDragOver = dragConfig.canDragItemOver,
|
||||
onDragEnd = { _, _ -> dragConfig.onItemDragEnd() },
|
||||
)
|
||||
val items = state.items
|
||||
val lastItemIndex = items.lastIndex
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.reorderable(reorderableListState)
|
||||
.align(Alignment.TopCenter)
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
state = listState,
|
||||
state = reorderableListState.listState,
|
||||
contentPadding = PaddingValues(
|
||||
top = TangemTheme.dimens.spacing12,
|
||||
bottom = TangemTheme.dimens.spacing92,
|
||||
),
|
||||
) {
|
||||
itemsIndexed(
|
||||
items = state.items,
|
||||
items = items,
|
||||
key = { _, item -> item.id },
|
||||
) { index, item ->
|
||||
|
||||
val onDragStart = remember(item) {
|
||||
{ dragConfig.onDragStart(item) }
|
||||
}
|
||||
|
||||
DraggableItem(
|
||||
item = item,
|
||||
index = index,
|
||||
lastItemIndex = state.items.lastIndex,
|
||||
lastItemIndex = lastItemIndex,
|
||||
reorderableState = reorderableListState,
|
||||
onDragStart = onDragStart,
|
||||
)
|
||||
|
||||
if (item is DraggableItem.GroupPlaceholder) {
|
||||
// This item should be displayed in the list but remain invisible
|
||||
Box(modifier = Modifier.fillMaxWidth())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -87,19 +117,39 @@ private fun TokenList(listState: LazyListState, state: OrganizeTokensListState,
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun DraggableItem(index: Int, item: DraggableItem, lastItemIndex: Int) {
|
||||
val itemModifier = Modifier
|
||||
.clipFirstAndLastItems(index, lastItemIndex)
|
||||
private fun LazyItemScope.DraggableItem(
|
||||
index: Int,
|
||||
item: DraggableItem,
|
||||
lastItemIndex: Int,
|
||||
reorderableState: ReorderableLazyListState,
|
||||
onDragStart: () -> Unit,
|
||||
) {
|
||||
ReorderableItem(
|
||||
reorderableState = reorderableState,
|
||||
index = index,
|
||||
key = item.id,
|
||||
) { isDragging ->
|
||||
|
||||
when (item) {
|
||||
is DraggableItem.GroupHeader -> DraggableNetworkGroupItem(
|
||||
modifier = itemModifier,
|
||||
networkName = item.networkName,
|
||||
)
|
||||
is DraggableItem.Token -> DraggableTokenItem(
|
||||
modifier = itemModifier,
|
||||
state = item.tokenItemState,
|
||||
)
|
||||
if (isDragging) {
|
||||
onDragStart()
|
||||
}
|
||||
|
||||
val itemModifier = Modifier
|
||||
.clipFirstLastAndDraggingItems(index, lastItemIndex, isDragging)
|
||||
|
||||
when (item) {
|
||||
is DraggableItem.GroupHeader -> DraggableNetworkGroupItem(
|
||||
modifier = itemModifier,
|
||||
networkName = item.networkName,
|
||||
reorderableTokenListState = reorderableState,
|
||||
)
|
||||
is DraggableItem.Token -> DraggableTokenItem(
|
||||
modifier = itemModifier,
|
||||
state = item.tokenItemState,
|
||||
reorderableTokenListState = reorderableState,
|
||||
)
|
||||
is DraggableItem.GroupPlaceholder -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -201,29 +251,36 @@ private fun Actions(config: OrganizeTokensStateHolder.ActionsConfig, modifier: M
|
|||
}
|
||||
}
|
||||
|
||||
private fun Modifier.clipFirstAndLastItems(index: Int, lastItemIndex: Int): Modifier = composed {
|
||||
when (index) {
|
||||
0 -> {
|
||||
this
|
||||
.clip(
|
||||
private fun Modifier.clipFirstLastAndDraggingItems(index: Int, lastItemIndex: Int, isDragging: Boolean): Modifier =
|
||||
composed {
|
||||
when {
|
||||
isDragging -> {
|
||||
val elevation by animateDpAsState(
|
||||
targetValue = TangemTheme.dimens.elevation12,
|
||||
label = "dragging_item_shadow_elevation",
|
||||
)
|
||||
|
||||
this.shadow(elevation, shape = TangemTheme.shapes.roundedCornersXMedium)
|
||||
}
|
||||
index == 0 -> {
|
||||
this.clip(
|
||||
RoundedCornerShape(
|
||||
topStart = TangemTheme.dimens.radius16,
|
||||
topEnd = TangemTheme.dimens.radius16,
|
||||
),
|
||||
)
|
||||
}
|
||||
lastItemIndex -> {
|
||||
this
|
||||
.clip(
|
||||
}
|
||||
index == lastItemIndex -> {
|
||||
this.clip(
|
||||
RoundedCornerShape(
|
||||
bottomStart = TangemTheme.dimens.radius16,
|
||||
bottomEnd = TangemTheme.dimens.radius16,
|
||||
),
|
||||
)
|
||||
}
|
||||
else -> this
|
||||
}
|
||||
else -> this
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
|
||||
|
|
|
|||
|
|
@ -2,12 +2,14 @@ package com.tangem.feature.wallet.presentation.organizetokens
|
|||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import org.burnoutcrew.reorderable.ItemPosition
|
||||
|
||||
internal data class OrganizeTokensStateHolder(
|
||||
val header: HeaderConfig,
|
||||
val itemsState: OrganizeTokensListState,
|
||||
val dragConfig: DragConfig,
|
||||
val actions: ActionsConfig,
|
||||
) {
|
||||
|
||||
|
|
@ -20,44 +22,79 @@ internal data class OrganizeTokensStateHolder(
|
|||
val onApplyClick: () -> Unit,
|
||||
val onCancelClick: () -> Unit,
|
||||
)
|
||||
|
||||
data class DragConfig(
|
||||
val onItemDragged: (from: ItemPosition, to: ItemPosition) -> Unit,
|
||||
val canDragItemOver: (dragOver: ItemPosition, dragging: ItemPosition) -> Boolean,
|
||||
val onItemDragEnd: () -> Unit,
|
||||
val onDragStart: (item: DraggableItem) -> Unit,
|
||||
)
|
||||
}
|
||||
|
||||
@Immutable
|
||||
internal sealed class OrganizeTokensListState {
|
||||
abstract val items: ImmutableList<DraggableItem>
|
||||
internal sealed interface OrganizeTokensListState {
|
||||
val items: PersistentList<DraggableItem>
|
||||
|
||||
data class GroupedByNetwork(
|
||||
override val items: ImmutableList<DraggableItem>,
|
||||
) : OrganizeTokensListState()
|
||||
override val items: PersistentList<DraggableItem>,
|
||||
) : OrganizeTokensListState
|
||||
|
||||
data class Ungrouped(
|
||||
override val items: ImmutableList<DraggableItem.Token>,
|
||||
) : OrganizeTokensListState()
|
||||
override val items: PersistentList<DraggableItem.Token>,
|
||||
) : OrganizeTokensListState
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
inline fun updateItems(update: (List<DraggableItem>) -> List<DraggableItem>): OrganizeTokensListState {
|
||||
fun updateItems(update: (PersistentList<DraggableItem>) -> List<DraggableItem>): OrganizeTokensListState {
|
||||
val updatedItems = update(this.items).toPersistentList()
|
||||
|
||||
return when (this) {
|
||||
is GroupedByNetwork -> this.copy(items = updatedItems)
|
||||
is Ungrouped -> this.copy(items = updatedItems as ImmutableList<DraggableItem.Token>)
|
||||
is Ungrouped -> this.copy(items = updatedItems as PersistentList<DraggableItem.Token>)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for the DND list items
|
||||
*
|
||||
* @property id ID of the item
|
||||
* */
|
||||
@Immutable
|
||||
internal sealed interface DraggableItem {
|
||||
val id: String
|
||||
|
||||
/**
|
||||
* Item for network group header.
|
||||
*
|
||||
* @property id ID of the network group
|
||||
* @property networkName network group name
|
||||
* */
|
||||
data class GroupHeader(
|
||||
override val id: String,
|
||||
val networkName: String,
|
||||
) : DraggableItem
|
||||
|
||||
/**
|
||||
* Item for token.
|
||||
*
|
||||
* @property tokenItemState state of the token item
|
||||
* @property groupId ID of the network group which contains this token
|
||||
* @property id ID of the token
|
||||
* */
|
||||
data class Token(
|
||||
val tokenItemState: TokenItemState.Draggable,
|
||||
val groupId: String,
|
||||
) : DraggableItem {
|
||||
override val id: String = tokenItemState.id
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper item used to detect possible positions where a network group can be placed.
|
||||
* Used only on [OrganizeTokensListState.GroupedByNetwork] and placed between network groups.
|
||||
*
|
||||
* @property id ID of the placeholder
|
||||
* */
|
||||
data class GroupPlaceholder(
|
||||
override val id: String,
|
||||
) : DraggableItem
|
||||
}
|
||||
|
|
@ -5,10 +5,16 @@ import androidx.compose.runtime.mutableStateOf
|
|||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensStateHolder.DragConfig
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensStateHolder.HeaderConfig
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.checkCanMoveHeaderOver
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.checkCanMoveTokenOver
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.findItemsToMove
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.moveItem
|
||||
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import org.burnoutcrew.reorderable.ItemPosition
|
||||
import javax.inject.Inject
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
|
|
@ -16,6 +22,10 @@ import kotlin.properties.Delegates
|
|||
@HiltViewModel
|
||||
internal class OrganizeTokensViewModel @Inject constructor() : ViewModel() {
|
||||
|
||||
// TODO: Move to domain
|
||||
@Volatile
|
||||
private var groupIdToTokens: Map<String, List<DraggableItem.Token>>? = null
|
||||
|
||||
var router: InnerWalletRouter by Delegates.notNull()
|
||||
|
||||
var uiState: OrganizeTokensStateHolder by mutableStateOf(getInitialState())
|
||||
|
|
@ -25,6 +35,12 @@ internal class OrganizeTokensViewModel @Inject constructor() : ViewModel() {
|
|||
itemsState = OrganizeTokensListState.Ungrouped(
|
||||
items = WalletPreviewData.draggableTokens,
|
||||
),
|
||||
dragConfig = DragConfig(
|
||||
onItemDragged = this::moveItem,
|
||||
canDragItemOver = this::checkCanMoveItemOver,
|
||||
onItemDragEnd = this::expandGroups,
|
||||
onDragStart = this::collapseGroup,
|
||||
),
|
||||
header = HeaderConfig(
|
||||
onSortByBalanceClick = { /* no-op */ },
|
||||
onGroupByNetworkClick = this::toggleTokensByNetworkGrouping,
|
||||
|
|
@ -43,4 +59,69 @@ internal class OrganizeTokensViewModel @Inject constructor() : ViewModel() {
|
|||
|
||||
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 collapseGroup(item: DraggableItem) {
|
||||
if (!groupIdToTokens.isNullOrEmpty() || item is DraggableItem.Token) return
|
||||
|
||||
val itemsState = uiState.itemsState as? OrganizeTokensListState.GroupedByNetwork ?: return
|
||||
groupIdToTokens = itemsState.items
|
||||
.asSequence()
|
||||
.filterIsInstance<DraggableItem.Token>()
|
||||
.groupBy(DraggableItem.Token::groupId)
|
||||
|
||||
uiState = uiState.copy(
|
||||
itemsState = itemsState.updateItems { items ->
|
||||
items.filterNot { it is DraggableItem.Token && it.groupId == item.id }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun expandGroups() {
|
||||
if (groupIdToTokens.isNullOrEmpty()) return
|
||||
|
||||
val itemsState = uiState.itemsState as? OrganizeTokensListState.GroupedByNetwork ?: return
|
||||
val currentGroups = itemsState.items.filterIsInstance<DraggableItem.GroupHeader>()
|
||||
val newItems = currentGroups
|
||||
.flatMapIndexed { index, group ->
|
||||
mutableListOf<DraggableItem>(group)
|
||||
.also { it.addAll(groupIdToTokens?.get(group.id).orEmpty()) }
|
||||
.also {
|
||||
if (index != currentGroups.lastIndex) {
|
||||
it.add(DraggableItem.GroupPlaceholder(id = "group_divider_$index"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uiState = uiState.copy(
|
||||
itemsState = itemsState.updateItems { newItems },
|
||||
)
|
||||
|
||||
groupIdToTokens = null
|
||||
}
|
||||
|
||||
private fun moveItem(from: ItemPosition, to: ItemPosition) {
|
||||
uiState = uiState.copy(
|
||||
itemsState = uiState.itemsState.updateItems {
|
||||
it.moveItem(from.index, to.index)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package com.tangem.feature.wallet.presentation.organizetokens.utils
|
||||
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.DraggableItem
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import org.burnoutcrew.reorderable.ItemPosition
|
||||
|
||||
internal fun List<DraggableItem>.findItemsToMove(
|
||||
moveOverItemKey: Any?,
|
||||
movedItemKey: Any?,
|
||||
): Pair<DraggableItem?, DraggableItem?> {
|
||||
var moveOverItem: DraggableItem? = null
|
||||
var movedItem: DraggableItem? = null
|
||||
|
||||
for (item in this) {
|
||||
if (item.id == moveOverItemKey) {
|
||||
moveOverItem = item
|
||||
}
|
||||
if (item.id == movedItemKey) {
|
||||
movedItem = item
|
||||
}
|
||||
if (moveOverItem != null && movedItem != null) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return Pair(moveOverItem, movedItem)
|
||||
}
|
||||
|
||||
internal fun checkCanMoveHeaderOver(
|
||||
moveOverItemPosition: ItemPosition,
|
||||
moveOverItem: DraggableItem,
|
||||
lastItemIndex: Int,
|
||||
): Boolean {
|
||||
return when {
|
||||
moveOverItemPosition.index == 0 -> true
|
||||
moveOverItemPosition.index == lastItemIndex -> true
|
||||
moveOverItem is DraggableItem.GroupPlaceholder -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
internal fun checkCanMoveTokenOver(item: DraggableItem.Token, moveOverItem: DraggableItem): Boolean {
|
||||
return when (moveOverItem) {
|
||||
is DraggableItem.GroupHeader -> false // Token item can not be moved to group item
|
||||
is DraggableItem.Token -> item.groupId == moveOverItem.groupId // Token item can not be moved over its group
|
||||
is DraggableItem.GroupPlaceholder -> false
|
||||
}
|
||||
}
|
||||
|
||||
internal fun PersistentList<DraggableItem>.moveItem(fromIndex: Int, toIndex: Int): PersistentList<DraggableItem> {
|
||||
val fromItem = this[fromIndex]
|
||||
return this
|
||||
.removeAt(fromIndex)
|
||||
.add(toIndex, fromItem)
|
||||
}
|
||||
|
|
@ -31,6 +31,7 @@ compose-constraint = "1.0.1"
|
|||
compose-navigation = "2.5.3"
|
||||
compose-accompanist = "0.30.1"
|
||||
compose-paging = "1.0.0-alpha18"
|
||||
compose-reorderable = "0.9.6"
|
||||
# endregion Compose
|
||||
|
||||
# region Other libraries
|
||||
|
|
@ -147,6 +148,7 @@ compose-accompanist-appCompatTheme = { module = "com.google.accompanist:accompan
|
|||
compose-accompanist-systemUiController = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "compose-accompanist" }
|
||||
compose-accompanist-webView = { module = "com.google.accompanist:accompanist-webview", version.ref = "compose-accompanist" }
|
||||
compose-paging = { module = "androidx.paging:paging-compose", version.ref = "compose-paging" }
|
||||
compose-reorderable = { module = "org.burnoutcrew.composereorderable:reorderable", version.ref = "compose-reorderable" }
|
||||
# endregion Compose
|
||||
|
||||
# region Firebase
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue