Updated on 2026-08-14

This commit is contained in:
Tangem 2023-06-06 14:25:38 +03:00
parent ae3d68a8d5
commit 99745c2ee4
14 changed files with 442 additions and 130 deletions

View file

@ -46,6 +46,7 @@
<string name="chat_button_title">Чат</string>
<string name="common_accept">Принять</string>
<string name="common_add">Добавить</string>
<string name="common_apply">Применить</string>
<string name="common_attention">Внимание</string>
<string name="common_balance">Баланс: %s</string>
<string name="common_biometric_authentication">биометрическую аутентификацию</string>
@ -255,6 +256,9 @@
<string name="onboarding_wallet_info_title_fourth">Восстановление кода доступа</string>
<string name="onboarding_wallet_info_title_second">Идентичные карты</string>
<string name="onboarding_wallet_info_title_third">Код доступа</string>
<string name="organize_tokens_group">Группировка</string>
<string name="organize_tokens_sort_by_balance">По балансу</string>
<string name="organize_tokens_title">Сортировка токенов</string>
<string name="referral_button_participate">Участвовать</string>
<string name="referral_error_failed_to_load_info">Не удалось загрузить информацию по реферальной программе. Пожалуйста, попробуйте позже.</string>
<string name="referral_error_failed_to_load_info_with_reason">Не удалось загрузить информацию по реферальной программе. Код ошибки: %s. Пожалуйста, попробуйте позже.</string>

View file

@ -44,6 +44,7 @@
<string name="chat_button_title">Support</string>
<string name="common_accept">Accept</string>
<string name="common_add">Add</string>
<string name="common_apply">Apply</string>
<string name="common_attention">Attention</string>
<string name="common_balance">Balance: %s</string>
<string name="common_biometric_authentication">biometric authentication</string>
@ -253,6 +254,9 @@
<string name="onboarding_wallet_info_title_fourth">Access code restore</string>
<string name="onboarding_wallet_info_title_second">Identical cards</string>
<string name="onboarding_wallet_info_title_third">Access code</string>
<string name="organize_tokens_group">Group</string>
<string name="organize_tokens_sort_by_balance">By balance</string>
<string name="organize_tokens_title">Organize tokens</string>
<string name="referral_button_participate">Participate</string>
<string name="referral_error_failed_to_load_info">Failed to load the information about the referral program. Please try again later.</string>
<string name="referral_error_failed_to_load_info_with_reason">Failed to load the information about the referral program. Error code: %s. Please try again later.</string>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000000"
android:pathData="M5,4V20H7V22H3V2H7V4H5ZM20,6H7V8H20V6ZM20,11H7V13H20V11ZM20,16H7V18H20V16Z" />
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000000"
android:pathData="M19,17H22L18,21L14,17H17V3H19M3,6H13V8H3M7,16V18H3V16M3,11H10V13H3V11Z" />
</vector>

View file

@ -8,6 +8,8 @@ import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import androidx.core.view.WindowCompat
import androidx.fragment.app.Fragment
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
import com.tangem.features.wallet.navigation.WalletRouter
@ -41,8 +43,15 @@ internal class WalletFragment : Fragment() {
return ComposeView(inflater.context).apply {
setContent {
isTransitionGroup = true
_walletRouter.Initialize()
TangemTheme {
val systemBarsColor = TangemTheme.colors.background.secondary
SystemBarsEffect {
setSystemBarsColor(systemBarsColor)
}
isTransitionGroup = true
_walletRouter.Initialize()
}
}
}
}

View file

@ -4,11 +4,13 @@ import com.tangem.core.ui.R
import com.tangem.feature.wallet.presentation.common.state.NetworkGroupState
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TokenOptionsState
import com.tangem.feature.wallet.presentation.common.state.TokenListState
import com.tangem.feature.wallet.presentation.organizetokens.DraggableItem
import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensListState
import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensStateHolder
import com.tangem.feature.wallet.presentation.wallet.state.WalletCardState
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
import java.util.UUID
internal object WalletPreviewData {
@ -107,42 +109,6 @@ internal object WalletPreviewData {
val loadingTokenItemState = TokenItemState.Loading(id = UUID.randomUUID().toString())
val organizeTokensState = OrganizeTokensStateHolder(
tokens = TokenListState.GroupedByNetwork(
groups = persistentListOf(),
),
header = OrganizeTokensStateHolder.HeaderConfig(
onSortByBalanceClick = {},
onGroupByNetworkClick = {},
),
actions = OrganizeTokensStateHolder.ActionsConfig(
onApplyClick = {},
onCancelClick = {},
),
)
private val draggableTokenList = persistentListOf(
tokenItemDragState.copy(
id = "token_1",
name = "Ethereum",
tokenIconResId = R.drawable.img_eth_22,
networkIconResId = null,
fiatAmount = "3 172,14 $",
),
tokenItemDragState.copy(
id = "token_2",
networkIconResId = R.drawable.img_eth_22,
fiatAmount = "803,65 $",
),
tokenItemDragState.copy(
id = "token_3",
name = "USDT",
tokenIconResId = R.drawable.img_arbitrum_22,
networkIconResId = R.drawable.img_eth_22,
fiatAmount = "88,01 $",
),
)
val networkGroup = NetworkGroupState.Content(
id = UUID.randomUUID().toString(),
networkName = "Ethereum",
@ -170,8 +136,66 @@ internal object WalletPreviewData {
)
val draggableNetworkGroup = NetworkGroupState.Draggable(
id = UUID.randomUUID().toString(),
id = "group_1",
networkName = "Ethereum",
tokens = draggableTokenList,
)
private const val networksSize = 10
private const val tokensSize = 3
val draggableItems = List(networksSize) { it }
.flatMap { index ->
val n = index + 1
val group = DraggableItem.GroupHeader(
groupState = NetworkGroupState.Draggable(
id = "group_$n",
networkName = "$n",
),
)
val tokens: MutableList<DraggableItem.Token> = mutableListOf()
repeat(times = tokensSize) { i ->
val nt = i + 1
tokens.add(
DraggableItem.Token(
tokenItemState = tokenItemDragState.copy(
id = "${group.id}_token_$nt",
name = "Token $nt",
networkIconResId = R.drawable.img_eth_22.takeIf { i != 0 },
),
groupId = group.id,
),
)
}
buildList {
add(group)
addAll(tokens)
}
}
.toPersistentList()
val draggableTokens = draggableItems
.filterIsInstance<DraggableItem.Token>()
.toPersistentList()
val groupedOrganizeTokensState = OrganizeTokensStateHolder(
itemsState = OrganizeTokensListState.GroupedByNetwork(
items = draggableItems,
),
header = OrganizeTokensStateHolder.HeaderConfig(
onSortByBalanceClick = {},
onGroupByNetworkClick = {},
),
actions = OrganizeTokensStateHolder.ActionsConfig(
onApplyClick = {},
onCancelClick = {},
),
)
val organizeTokensState = groupedOrganizeTokensState.copy(
itemsState = OrganizeTokensListState.Ungrouped(
items = draggableTokens,
),
)
}

View file

@ -19,7 +19,42 @@ import com.tangem.feature.wallet.presentation.common.WalletPreviewData
import com.tangem.feature.wallet.presentation.common.state.NetworkGroupState
@Composable
internal fun NetworkGroupItem(state: NetworkGroupState, modifier: Modifier = Modifier) {
internal fun NetworkGroupItem(state: NetworkGroupState.Content, modifier: Modifier = Modifier) {
InternalNetworkGroupItem(
modifier = modifier,
networkName = state.networkName,
tokens = {
state.tokens.forEach { token ->
key(token.id) {
TokenItem(state = token)
}
}
},
)
}
@Composable
internal fun DraggableNetworkGroupItem(state: NetworkGroupState.Draggable, modifier: Modifier = Modifier) {
InternalNetworkGroupItem(
modifier = modifier,
networkName = state.networkName,
endIcon = {
Icon(
painter = painterResource(id = R.drawable.ic_group_drop_24),
tint = TangemTheme.colors.icon.informative,
contentDescription = null,
)
},
)
}
@Composable
private fun InternalNetworkGroupItem(
networkName: String,
modifier: Modifier = Modifier,
tokens: @Composable ColumnScope.() -> Unit = {},
endIcon: @Composable RowScope.() -> Unit = {},
) {
Column(modifier = modifier) {
Row(
modifier = Modifier
@ -31,34 +66,30 @@ internal fun NetworkGroupItem(state: NetworkGroupState, modifier: Modifier = Mod
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(
text = stringResource(id = R.string.wallet_network_group_title, state.networkName),
text = stringResource(id = R.string.wallet_network_group_title, networkName),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.tertiary,
)
if (state is NetworkGroupState.Draggable) {
Icon(
painter = painterResource(id = R.drawable.ic_group_drop_24),
tint = TangemTheme.colors.icon.informative,
contentDescription = null,
)
}
}
Column {
state.tokens.forEach { token ->
key(token.id) {
TokenItem(state = token)
}
}
endIcon()
}
tokens()
}
}
// region Preview
@Composable
private fun NetworkGroupItemSample(group: NetworkGroupState) {
when (group) {
is NetworkGroupState.Content -> NetworkGroupItem(group)
is NetworkGroupState.Draggable -> DraggableNetworkGroupItem(group)
}
}
@Preview(showBackground = true, widthDp = 360)
@Composable
private fun NetworkGroupItemPreview_Light(@PreviewParameter(NetworkGroupProvider::class) group: NetworkGroupState) {
TangemTheme {
NetworkGroupItem(group)
NetworkGroupItemSample(group)
}
}
@ -66,7 +97,7 @@ private fun NetworkGroupItemPreview_Light(@PreviewParameter(NetworkGroupProvider
@Composable
private fun NetworkGroupItemPreview_Dark(@PreviewParameter(NetworkGroupProvider::class) group: NetworkGroupState) {
TangemTheme(isDark = true) {
NetworkGroupItem(group)
NetworkGroupItemSample(group)
}
}

View file

@ -9,6 +9,7 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.Stable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -18,6 +19,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstrainedLayoutReference
import androidx.constraintlayout.compose.ConstraintLayout
@ -34,6 +36,10 @@ import com.tangem.feature.wallet.presentation.common.state.TokenItemState
import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TokenOptionsState
private const val DOTS = "•••"
val TOKEN_ITEM_HEIGHT: Dp
@Composable
@ReadOnlyComposable
get() = TangemTheme.dimens.size68
@Composable
internal fun TokenItem(state: TokenItemState, modifier: Modifier = Modifier) {
@ -76,7 +82,8 @@ internal fun DraggableTokenItem(state: TokenItemState.Draggable, modifier: Modif
hasPending = false,
options = { ref ->
Icon(
modifier = Modifier.constrainAsOptionsItem(scope = this, ref),
modifier = Modifier
.constrainAsOptionsItem(scope = this, ref),
painter = painterResource(id = R.drawable.ic_drag_24),
tint = TangemTheme.colors.icon.informative,
contentDescription = null,
@ -251,7 +258,7 @@ private fun BaseSurface(
content: @Composable () -> Unit,
) {
Surface(
modifier = modifier.defaultMinSize(minHeight = TangemTheme.dimens.size68),
modifier = modifier.defaultMinSize(minHeight = TOKEN_ITEM_HEIGHT),
color = TangemTheme.colors.background.primary,
onClick = onClick ?: {},
enabled = onClick != null,

View file

@ -7,17 +7,15 @@ import kotlinx.collections.immutable.ImmutableList
internal sealed interface NetworkGroupState {
val id: String
val networkName: String
val tokens: ImmutableList<TokenItemState>
data class Draggable(
override val id: String,
override val networkName: String,
override val tokens: ImmutableList<TokenItemState.Draggable>,
) : NetworkGroupState
data class Content(
override val id: String,
override val networkName: String,
override val tokens: ImmutableList<TokenItemState>,
val tokens: ImmutableList<TokenItemState>,
) : NetworkGroupState
}

View file

@ -1,16 +0,0 @@
package com.tangem.feature.wallet.presentation.common.state
import androidx.compose.runtime.Immutable
import kotlinx.collections.immutable.ImmutableList
@Immutable
internal sealed interface TokenListState {
data class GroupedByNetwork(
val groups: ImmutableList<NetworkGroupState>,
) : TokenListState
data class Ungrouped(
val tokens: ImmutableList<TokenItemState>,
) : TokenListState
}

View file

@ -1,88 +1,255 @@
// TODO: Remove after components implementation
@file:Suppress("UNUSED_PARAMETER")
package com.tangem.feature.wallet.presentation.organizetokens
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.foundation.lazy.*
import androidx.compose.foundation.shape.RoundedCornerShape
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.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import com.tangem.core.ui.components.BackgroundActionButton
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.components.SecondaryButton
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
import com.tangem.feature.wallet.presentation.common.state.TokenListState
import com.tangem.feature.wallet.presentation.common.component.DraggableNetworkGroupItem
import com.tangem.feature.wallet.presentation.common.component.DraggableTokenItem
@Composable
internal fun OrganizeTokensScreen(state: OrganizeTokensStateHolder) {
val tokensListState = rememberLazyListState()
Scaffold(
topBar = {
TopBar(state.header)
TopBar(state.header, tokensListState)
},
content = { paddingValues ->
TokenList(
state = state.tokens,
modifier = Modifier.padding(paddingValues),
listState = tokensListState,
state = state.itemsState,
)
},
floatingActionButtonPosition = FabPosition.Center,
floatingActionButton = {
Actions(state.actions)
},
contentColor = TangemTheme.colors.background.primary,
containerColor = TangemTheme.colors.background.secondary,
)
}
@Composable
private fun TokenList(state: TokenListState, modifier: Modifier = Modifier) {
when (state) {
is TokenListState.GroupedByNetwork -> {
/* [REDACTED_TODO_COMMENT] */
}
is TokenListState.Ungrouped -> {
/* [REDACTED_TODO_COMMENT] */
private fun TokenList(listState: LazyListState, state: OrganizeTokensListState, modifier: Modifier = Modifier) {
Box(modifier = modifier) {
LazyColumn(
modifier = Modifier
.align(Alignment.TopCenter)
.padding(horizontal = TangemTheme.dimens.spacing16),
state = listState,
contentPadding = PaddingValues(
top = TangemTheme.dimens.spacing12,
bottom = TangemTheme.dimens.spacing92,
),
) {
itemsIndexed(
items = state.items,
key = { _, item -> item.id },
) { index, item ->
DraggableItem(
item = item,
index = index,
lastItemIndex = state.items.lastIndex,
)
}
}
BottomGradient(modifier = Modifier.align(Alignment.BottomCenter))
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun TopBar(config: OrganizeTokensStateHolder.HeaderConfig, modifier: Modifier = Modifier) {
TopAppBar(
title = {
private fun DraggableItem(index: Int, item: DraggableItem, lastItemIndex: Int) {
val itemModifier = Modifier
.clipFirstAndLastItems(index, lastItemIndex)
when (item) {
is DraggableItem.GroupHeader -> DraggableNetworkGroupItem(
modifier = itemModifier,
state = item.groupState,
)
is DraggableItem.Token -> DraggableTokenItem(
modifier = itemModifier,
state = item.tokenItemState,
)
}
}
@Composable
private fun BottomGradient(modifier: Modifier = Modifier) {
Box(
modifier = modifier
.fillMaxWidth()
.height(TangemTheme.dimens.size116)
.background(
brush = Brush.verticalGradient(
colors = listOf(
Color.Transparent,
TangemTheme.colors.background.secondary,
),
),
),
)
}
@Composable
private fun TopBar(
config: OrganizeTokensStateHolder.HeaderConfig,
tokensListState: LazyListState,
modifier: Modifier = Modifier,
) {
val isElevationEnabled by remember {
derivedStateOf {
tokensListState.firstVisibleItemScrollOffset > 0
}
}
val elevation by animateDpAsState(
targetValue = if (isElevationEnabled) AppBarDefaults.TopAppBarElevation else TangemTheme.dimens.elevation0,
label = "top_bar_shadow_elevation",
)
Column(
modifier = modifier
.shadow(elevation)
.background(TangemTheme.colors.background.secondary)
.padding(horizontal = TangemTheme.dimens.spacing16)
.fillMaxWidth(),
) {
Box(
modifier = Modifier
.fillMaxWidth()
.heightIn(min = TangemTheme.dimens.size56),
contentAlignment = Alignment.CenterStart,
) {
Text(
text = "Organize tokens", // TODO: Move to resources
text = stringResource(id = R.string.organize_tokens_title),
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
maxLines = 1,
)
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = TangemTheme.colors.background.secondary,
titleContentColor = TangemTheme.colors.text.primary1,
),
)
}
Row(
modifier = Modifier
.fillMaxWidth()
.heightIn(min = TangemTheme.dimens.size56),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
) {
BackgroundActionButton(
modifier = Modifier.weight(1f),
text = stringResource(id = R.string.organize_tokens_sort_by_balance),
iconResId = R.drawable.ic_sort_24,
onClick = config.onSortByBalanceClick,
)
BackgroundActionButton(
modifier = Modifier.weight(1f),
text = stringResource(id = R.string.organize_tokens_group),
iconResId = R.drawable.ic_group_24,
onClick = config.onGroupByNetworkClick,
)
}
}
}
@Composable
private fun Actions(config: OrganizeTokensStateHolder.ActionsConfig, modifier: Modifier = Modifier) {
// TODO: Implement list apply and cancel actions
Row(
modifier = modifier
.padding(horizontal = TangemTheme.dimens.spacing16)
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
) {
SecondaryButton(
modifier = Modifier.weight(1f),
text = stringResource(id = R.string.common_cancel),
onClick = config.onCancelClick,
)
PrimaryButton(
modifier = Modifier.weight(1f),
text = stringResource(id = R.string.common_apply),
onClick = config.onApplyClick,
)
}
}
private fun Modifier.clipFirstAndLastItems(index: Int, lastItemIndex: Int): Modifier = composed {
when (index) {
0 -> {
this
.clip(
RoundedCornerShape(
topStart = TangemTheme.dimens.radius16,
topEnd = TangemTheme.dimens.radius16,
),
)
}
lastItemIndex -> {
this
.clip(
RoundedCornerShape(
bottomStart = TangemTheme.dimens.radius16,
bottomEnd = TangemTheme.dimens.radius16,
),
)
}
else -> this
}
}
// region Preview
@Preview(showBackground = true, widthDp = 360)
@Composable
private fun OrganizeTokensScreenPreview_Light() {
private fun OrganizeTokensScreenPreview_Light(
@PreviewParameter(OrganizeTokensStateProvider::class) state: OrganizeTokensStateHolder,
) {
TangemTheme {
OrganizeTokensScreen(state = WalletPreviewData.organizeTokensState)
OrganizeTokensScreen(state)
}
}
@Preview(showBackground = true, widthDp = 360)
@Composable
private fun OrganizeTokensScreenPreview_Dark() {
private fun OrganizeTokensScreenPreview_Dark(
@PreviewParameter(OrganizeTokensStateProvider::class) state: OrganizeTokensStateHolder,
) {
TangemTheme(isDark = true) {
OrganizeTokensScreen(state = WalletPreviewData.organizeTokensState)
OrganizeTokensScreen(state)
}
}
private class OrganizeTokensStateProvider : CollectionPreviewParameterProvider<OrganizeTokensStateHolder>(
collection = listOf(
WalletPreviewData.organizeTokensState,
WalletPreviewData.groupedOrganizeTokensState,
),
)
// endregion Preview

View file

@ -1,10 +1,14 @@
package com.tangem.feature.wallet.presentation.organizetokens
import com.tangem.feature.wallet.presentation.common.state.TokenListState
import androidx.compose.runtime.Immutable
import com.tangem.feature.wallet.presentation.common.state.NetworkGroupState
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toPersistentList
internal data class OrganizeTokensStateHolder(
val tokens: TokenListState,
val header: HeaderConfig,
val itemsState: OrganizeTokensListState,
val actions: ActionsConfig,
) {
@ -17,4 +21,45 @@ internal data class OrganizeTokensStateHolder(
val onApplyClick: () -> Unit,
val onCancelClick: () -> Unit,
)
}
@Immutable
internal sealed class OrganizeTokensListState {
abstract val items: ImmutableList<DraggableItem>
data class GroupedByNetwork(
override val items: ImmutableList<DraggableItem>,
) : OrganizeTokensListState()
data class Ungrouped(
override val items: ImmutableList<DraggableItem.Token>,
) : OrganizeTokensListState()
@Suppress("UNCHECKED_CAST")
inline fun updateItems(update: (List<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>)
}
}
}
@Immutable
internal sealed interface DraggableItem {
val id: String
data class GroupHeader(
val groupState: NetworkGroupState.Draggable,
) : DraggableItem {
override val id: String = groupState.id
}
data class Token(
val tokenItemState: TokenItemState.Draggable,
val groupId: String,
) : DraggableItem {
override val id: String = tokenItemState.id
}
}

View file

@ -5,11 +5,14 @@ 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.HeaderConfig
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.collections.immutable.toPersistentList
import javax.inject.Inject
import kotlin.properties.Delegates
// FIXME: Implemented with preview data
@HiltViewModel
internal class OrganizeTokensViewModel @Inject constructor() : ViewModel() {
@ -18,5 +21,26 @@ internal class OrganizeTokensViewModel @Inject constructor() : ViewModel() {
var uiState: OrganizeTokensStateHolder by mutableStateOf(getInitialState())
private set
private fun getInitialState(): OrganizeTokensStateHolder = WalletPreviewData.organizeTokensState
private fun getInitialState(): OrganizeTokensStateHolder = WalletPreviewData.organizeTokensState.copy(
itemsState = OrganizeTokensListState.Ungrouped(
items = WalletPreviewData.draggableTokens,
),
header = HeaderConfig(
onSortByBalanceClick = { /* no-op */ },
onGroupByNetworkClick = this::toggleTokensByNetworkGrouping,
),
)
private fun toggleTokensByNetworkGrouping() {
val newListState = when (val itemsState = uiState.itemsState) {
is OrganizeTokensListState.GroupedByNetwork -> OrganizeTokensListState.Ungrouped(
items = itemsState.items.filterIsInstance<DraggableItem.Token>().toPersistentList(),
)
is OrganizeTokensListState.Ungrouped -> OrganizeTokensListState.GroupedByNetwork(
items = WalletPreviewData.draggableItems,
)
}
uiState = uiState.copy(itemsState = newListState)
}
}

View file

@ -8,7 +8,6 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.presentation.WalletFragment
import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensScreen
import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensViewModel
@ -25,24 +24,22 @@ internal class DefaultWalletRouter : InnerWalletRouter {
@Composable
override fun Initialize() {
TangemTheme {
NavHost(
navController = rememberNavController().apply { navController = this },
startDestination = WalletScreens.WALLET.name,
) {
composable(WalletScreens.WALLET.name) {
val viewModel = hiltViewModel<WalletViewModel>().apply { router = this@DefaultWalletRouter }
WalletScreen(state = viewModel.uiState)
}
NavHost(
navController = rememberNavController().apply { navController = this },
startDestination = WalletScreens.WALLET.name,
) {
composable(WalletScreens.WALLET.name) {
val viewModel = hiltViewModel<WalletViewModel>().apply { router = this@DefaultWalletRouter }
WalletScreen(state = viewModel.uiState)
}
composable(WalletScreens.ORGANIZE_TOKENS.name) {
BackHandler(onBack = ::popBackStack)
composable(WalletScreens.ORGANIZE_TOKENS.name) {
BackHandler(onBack = ::popBackStack)
val viewModel: OrganizeTokensViewModel = hiltViewModel<OrganizeTokensViewModel>()
.apply { router = this@DefaultWalletRouter }
val viewModel: OrganizeTokensViewModel = hiltViewModel<OrganizeTokensViewModel>()
.apply { router = this@DefaultWalletRouter }
OrganizeTokensScreen(state = viewModel.uiState)
}
OrganizeTokensScreen(state = viewModel.uiState)
}
}
}