diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index 2baace2c4d..636d609568 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -46,6 +46,7 @@
Чат
Принять
Добавить
+ Применить
Внимание
Баланс: %s
биометрическую аутентификацию
@@ -255,6 +256,9 @@
Восстановление кода доступа
Идентичные карты
Код доступа
+ Группировка
+ По балансу
+ Сортировка токенов
Участвовать
Не удалось загрузить информацию по реферальной программе. Пожалуйста, попробуйте позже.
Не удалось загрузить информацию по реферальной программе. Код ошибки: %s. Пожалуйста, попробуйте позже.
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index ea3c2c3c93..af04c2fd30 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -44,6 +44,7 @@
Support
Accept
Add
+ Apply
Attention
Balance: %s
biometric authentication
@@ -253,6 +254,9 @@
Access code restore
Identical cards
Access code
+ Group
+ By balance
+ Organize tokens
Participate
Failed to load the information about the referral program. Please try again later.
Failed to load the information about the referral program. Error code: %s. Please try again later.
diff --git a/core/ui/src/main/res/drawable/ic_group_24.xml b/core/ui/src/main/res/drawable/ic_group_24.xml
new file mode 100644
index 0000000000..0f5f5f4487
--- /dev/null
+++ b/core/ui/src/main/res/drawable/ic_group_24.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/core/ui/src/main/res/drawable/ic_sort_24.xml b/core/ui/src/main/res/drawable/ic_sort_24.xml
new file mode 100644
index 0000000000..538a24d8e0
--- /dev/null
+++ b/core/ui/src/main/res/drawable/ic_sort_24.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt
index 1bc2cd04fd..d62c79855f 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt
@@ -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()
+ }
}
}
}
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt
index 40817e750e..cdd8d70132 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt
@@ -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 = 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()
+ .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,
+ ),
)
}
\ No newline at end of file
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/NetworkGroupItem.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/NetworkGroupItem.kt
index ee2d924e85..70e1eeb242 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/NetworkGroupItem.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/NetworkGroupItem.kt
@@ -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)
}
}
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt
index 78c5b5f322..b2cb67a6d1 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt
@@ -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,
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/NetworkGroupState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/NetworkGroupState.kt
index c34fdd126d..854730e663 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/NetworkGroupState.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/NetworkGroupState.kt
@@ -7,17 +7,15 @@ import kotlinx.collections.immutable.ImmutableList
internal sealed interface NetworkGroupState {
val id: String
val networkName: String
- val tokens: ImmutableList
data class Draggable(
override val id: String,
override val networkName: String,
- override val tokens: ImmutableList,
) : NetworkGroupState
data class Content(
override val id: String,
override val networkName: String,
- override val tokens: ImmutableList,
+ val tokens: ImmutableList,
) : NetworkGroupState
}
\ No newline at end of file
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenListState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenListState.kt
deleted file mode 100644
index fe3b7fba46..0000000000
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenListState.kt
+++ /dev/null
@@ -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,
- ) : TokenListState
-
- data class Ungrouped(
- val tokens: ImmutableList,
- ) : TokenListState
-}
\ No newline at end of file
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt
index e626eb0029..cea7c1211a 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt
@@ -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(
+ collection = listOf(
+ WalletPreviewData.organizeTokensState,
+ WalletPreviewData.groupedOrganizeTokensState,
+ ),
+)
// endregion Preview
\ No newline at end of file
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt
index b9a8e797cb..9e76b66248 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt
@@ -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
+
+ data class GroupedByNetwork(
+ override val items: ImmutableList,
+ ) : OrganizeTokensListState()
+
+ data class Ungrouped(
+ override val items: ImmutableList,
+ ) : OrganizeTokensListState()
+
+ @Suppress("UNCHECKED_CAST")
+ inline fun updateItems(update: (List) -> List): OrganizeTokensListState {
+ val updatedItems = update(this.items).toPersistentList()
+
+ return when (this) {
+ is GroupedByNetwork -> this.copy(items = updatedItems)
+ is Ungrouped -> this.copy(items = updatedItems as ImmutableList)
+ }
+ }
+}
+
+@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
+ }
}
\ No newline at end of file
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt
index 16f726eab1..cbdd25e47e 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt
@@ -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().toPersistentList(),
+ )
+ is OrganizeTokensListState.Ungrouped -> OrganizeTokensListState.GroupedByNetwork(
+ items = WalletPreviewData.draggableItems,
+ )
+ }
+
+ uiState = uiState.copy(itemsState = newListState)
+ }
}
\ No newline at end of file
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt
index f2768ed116..b06a719e1f 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt
@@ -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().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().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()
- .apply { router = this@DefaultWalletRouter }
+ val viewModel: OrganizeTokensViewModel = hiltViewModel()
+ .apply { router = this@DefaultWalletRouter }
- OrganizeTokensScreen(state = viewModel.uiState)
- }
+ OrganizeTokensScreen(state = viewModel.uiState)
}
}
}