Updated on 2026-08-14

This commit is contained in:
Tangem 2023-06-22 19:09:09 +08:00
parent aa19739725
commit 6c396362e4
12 changed files with 284 additions and 69 deletions

View file

@ -1,6 +1,7 @@
package com.tangem.feature.wallet.presentation.common
import com.tangem.core.ui.R
import com.tangem.core.ui.components.transactions.TransactionState
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.organizetokens.DraggableItem
@ -9,13 +10,14 @@ import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensState
import com.tangem.feature.wallet.presentation.wallet.state.WalletCardState
import com.tangem.feature.wallet.presentation.wallet.state.WalletContentItemState
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder
import com.tangem.feature.wallet.presentation.wallet.state.WalletTopBarConfig
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
import java.util.UUID
internal object WalletPreviewData {
val walletTopBarConfig = WalletStateHolder.TopBarConfig(onScanCardClick = {}, onMoreClick = {})
val walletTopBarConfig = WalletTopBarConfig(onScanCardClick = {}, onMoreClick = {})
val walletCardContentState = WalletCardState.Content(
id = UUID.randomUUID().toString(),
@ -159,7 +161,7 @@ internal object WalletPreviewData {
),
)
val groupedWalletScreenState = WalletStateHolder(
val multicurrencyWalletScreenState = WalletStateHolder.MultiCurrencyContent(
onBackClick = {},
topBarConfig = walletTopBarConfig,
selectedWallet = walletCardContentState,
@ -170,8 +172,8 @@ internal object WalletPreviewData {
walletCardErrorState,
),
contentItems = persistentListOf(
WalletContentItemState.NetworkGroupTitle("Bitcoin"),
WalletContentItemState.Token(
WalletContentItemState.MultiCurrencyItem.NetworkGroupTitle("Bitcoin"),
WalletContentItemState.MultiCurrencyItem.Token(
tokenItemVisibleState.copy(
id = "token_1",
name = "Ethereum",
@ -180,7 +182,7 @@ internal object WalletPreviewData {
amount = "1,89340821 ETH",
),
),
WalletContentItemState.Token(
WalletContentItemState.MultiCurrencyItem.Token(
tokenItemVisibleState.copy(
id = "token_2",
name = "Ethereum",
@ -189,7 +191,7 @@ internal object WalletPreviewData {
amount = "1,89340821 ETH",
),
),
WalletContentItemState.Token(
WalletContentItemState.MultiCurrencyItem.Token(
tokenItemVisibleState.copy(
id = "token_3",
name = "Ethereum",
@ -198,7 +200,7 @@ internal object WalletPreviewData {
amount = "1,89340821 ETH",
),
),
WalletContentItemState.Token(
WalletContentItemState.MultiCurrencyItem.Token(
tokenItemVisibleState.copy(
id = "token_4",
name = "Ethereum",
@ -207,8 +209,8 @@ internal object WalletPreviewData {
amount = "1,89340821 ETH",
),
),
WalletContentItemState.NetworkGroupTitle("Ethereum"),
WalletContentItemState.Token(
WalletContentItemState.MultiCurrencyItem.NetworkGroupTitle("Ethereum"),
WalletContentItemState.MultiCurrencyItem.Token(
tokenItemVisibleState.copy(
id = "token_5",
name = "Ethereum",
@ -220,4 +222,35 @@ internal object WalletPreviewData {
),
onOrganizeTokensClick = {},
)
val singleWalletScreenState = WalletStateHolder.SingleCurrencyContent(
onBackClick = {},
topBarConfig = walletTopBarConfig,
selectedWallet = walletCardContentState,
wallets = persistentListOf(
walletCardContentState,
walletCardLoadingState,
walletCardHiddenContentState,
walletCardErrorState,
),
contentItems = persistentListOf(
WalletContentItemState.SingleCurrencyItem.Title(onExploreClick = {}),
WalletContentItemState.SingleCurrencyItem.TransactionGroupTitle("Today"),
WalletContentItemState.SingleCurrencyItem.Transaction(
TransactionState.Sending(
address = "33BddS...ga2B",
amount = "-0.500913 BTC",
timestamp = "8:41",
),
),
WalletContentItemState.SingleCurrencyItem.TransactionGroupTitle("Yesterday"),
WalletContentItemState.SingleCurrencyItem.Transaction(
TransactionState.Sending(
address = "33BddS...ga2B",
amount = "-0.500913 BTC",
timestamp = "8:41",
),
),
),
)
}

View file

@ -1,13 +1,55 @@
package com.tangem.feature.wallet.presentation.wallet.state
import com.tangem.core.ui.components.transactions.TransactionState
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
/**
* Wallet screen content item state
*
[REDACTED_AUTHOR]
*/
internal sealed interface WalletContentItemState {
data class NetworkGroupTitle(val networkName: String) : WalletContentItemState
/** Multi currency wallet content state */
sealed interface MultiCurrencyItem : WalletContentItemState {
data class Token(val tokenItemState: TokenItemState) : WalletContentItemState
/**
* Network group title item
*
* @property networkName network name
*/
data class NetworkGroupTitle(val networkName: String) : MultiCurrencyItem
/**
* Token item
*
* @property state token item state
*/
data class Token(val state: TokenItemState) : MultiCurrencyItem
}
/** Single currency wallet content state */
sealed interface SingleCurrencyItem : WalletContentItemState {
/**
* Title item
*
* @property onExploreClick lambda be invoke when explore button was clicked
*/
data class Title(val onExploreClick: () -> Unit) : SingleCurrencyItem
/**
* Transaction group title item
*
* @property title title
*/
data class TransactionGroupTitle(val title: String) : SingleCurrencyItem
/**
* Transaction item
*
* @property state transaction state
*/
data class Transaction(val state: TransactionState) : SingleCurrencyItem
}
}

View file

@ -3,7 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.state
import kotlinx.collections.immutable.ImmutableList
/**
* Wallet state holder
* Wallet screen state holder
*
* @property onBackClick lambda be invoked when back button is clicked
* @property topBarConfig top bar config
@ -13,20 +13,46 @@ import kotlinx.collections.immutable.ImmutableList
*
[REDACTED_AUTHOR]
*/
internal data class WalletStateHolder(
val onBackClick: () -> Unit,
val topBarConfig: TopBarConfig,
val selectedWallet: WalletCardState,
val wallets: ImmutableList<WalletCardState>,
val contentItems: ImmutableList<WalletContentItemState>,
val onOrganizeTokensClick: () -> Unit,
internal sealed class WalletStateHolder(
open val onBackClick: () -> Unit,
open val topBarConfig: WalletTopBarConfig,
open val selectedWallet: WalletCardState,
open val wallets: ImmutableList<WalletCardState>,
open val contentItems: ImmutableList<WalletContentItemState>,
) {
/**
* Top bar config
* Multi currency wallet content state
*
* @property onScanCardClick lambda be invoked when scan card button is clicked
* @property onMoreClick lambda be invoked when more button is clicked
* @property onBackClick lambda be invoked when back button is clicked
* @property topBarConfig top bar config
* @property selectedWallet selected wallet
* @property wallets list of wallets states
* @property contentItems content items
*/
data class TopBarConfig(val onScanCardClick: () -> Unit, val onMoreClick: () -> Unit)
data class MultiCurrencyContent(
override val onBackClick: () -> Unit,
override val topBarConfig: WalletTopBarConfig,
override val selectedWallet: WalletCardState,
override val wallets: ImmutableList<WalletCardState>,
override val contentItems: ImmutableList<WalletContentItemState.MultiCurrencyItem>,
val onOrganizeTokensClick: () -> Unit,
) : WalletStateHolder(onBackClick, topBarConfig, selectedWallet, wallets, contentItems)
/**
* Single currency wallet content state
*
* @property onBackClick lambda be invoked when back button is clicked
* @property topBarConfig top bar config
* @property selectedWallet selected wallet
* @property wallets list of wallets states
* @property contentItems content items
*/
data class SingleCurrencyContent(
override val onBackClick: () -> Unit,
override val topBarConfig: WalletTopBarConfig,
override val selectedWallet: WalletCardState,
override val wallets: ImmutableList<WalletCardState>,
override val contentItems: ImmutableList<WalletContentItemState.SingleCurrencyItem>,
) : WalletStateHolder(onBackClick, topBarConfig, selectedWallet, wallets, contentItems)
}

View file

@ -0,0 +1,9 @@
package com.tangem.feature.wallet.presentation.wallet.state
/**
* Wallet screen top bar config
*
* @property onScanCardClick lambda be invoked when scan card button is clicked
* @property onMoreClick lambda be invoked when more button is clicked
*/
data class WalletTopBarConfig(val onScanCardClick: () -> Unit, val onMoreClick: () -> Unit)

View file

@ -1,23 +1,29 @@
package com.tangem.feature.wallet.presentation.wallet.ui
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
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.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
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.RoundedActionButton
import com.tangem.core.ui.components.transactions.Transaction
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
@ -43,9 +49,7 @@ internal fun WalletScreen(state: WalletStateHolder, modifier: Modifier = Modifie
topBar = { WalletTopBar(config = state.topBarConfig) },
containerColor = TangemTheme.colors.background.secondary,
) { scaffoldPaddings ->
val lastContentItemIndex = remember(state.contentItems) {
state.contentItems.lastIndex
}
val lastContentItemIndex = remember(state.contentItems) { state.contentItems.lastIndex }
LazyColumn(
modifier = modifier
@ -57,10 +61,13 @@ internal fun WalletScreen(state: WalletStateHolder, modifier: Modifier = Modifie
itemsIndexed(
items = state.contentItems,
key = { _, item ->
key = { index, item ->
when (item) {
is WalletContentItemState.NetworkGroupTitle -> item.networkName
is WalletContentItemState.Token -> item.tokenItemState.id
is WalletContentItemState.MultiCurrencyItem.NetworkGroupTitle -> item.networkName
is WalletContentItemState.MultiCurrencyItem.Token -> item.state.id
is WalletContentItemState.SingleCurrencyItem.Title -> index
is WalletContentItemState.SingleCurrencyItem.TransactionGroupTitle -> item.title
is WalletContentItemState.SingleCurrencyItem.Transaction -> index
}
},
) { index, item ->
@ -69,33 +76,96 @@ internal fun WalletScreen(state: WalletStateHolder, modifier: Modifier = Modifie
.clipFirstAndLastItems(index, lastContentItemIndex)
when (item) {
is WalletContentItemState.NetworkGroupTitle -> {
NetworkGroupItem(
networkName = item.networkName,
modifier = itemModifier,
)
is WalletContentItemState.MultiCurrencyItem.NetworkGroupTitle -> {
NetworkGroupItem(networkName = item.networkName, modifier = itemModifier)
}
is WalletContentItemState.Token -> {
TokenItem(
state = item.tokenItemState,
modifier = itemModifier,
)
is WalletContentItemState.MultiCurrencyItem.Token -> {
TokenItem(state = item.state, modifier = itemModifier)
}
is WalletContentItemState.SingleCurrencyItem.Title -> {
SingleCurrencyTitle(config = item, modifier = itemModifier)
}
is WalletContentItemState.SingleCurrencyItem.TransactionGroupTitle -> {
TransactionGroupTitle(config = item, modifier = itemModifier)
}
is WalletContentItemState.SingleCurrencyItem.Transaction -> {
Transaction(state = item.state, modifier = itemModifier)
}
}
}
item {
RoundedActionButton(
text = stringResource(id = R.string.organize_tokens_title),
iconResId = R.drawable.ic_filter_24,
onClick = state.onOrganizeTokensClick,
modifier = Modifier.padding(top = TangemTheme.dimens.spacing14),
)
if (state is WalletStateHolder.MultiCurrencyContent) {
item {
RoundedActionButton(
text = stringResource(id = R.string.organize_tokens_title),
iconResId = R.drawable.ic_filter_24,
onClick = state.onOrganizeTokensClick,
modifier = Modifier.padding(top = TangemTheme.dimens.spacing14),
)
}
}
}
}
}
@Composable
private fun SingleCurrencyTitle(
config: WalletContentItemState.SingleCurrencyItem.Title,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier
.background(TangemTheme.colors.background.primary)
.fillMaxWidth()
.padding(top = TangemTheme.dimens.spacing12)
.padding(horizontal = TangemTheme.dimens.spacing16),
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(
text = stringResource(id = R.string.common_transactions),
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.subtitle2,
)
Row(
modifier = Modifier.clickable(onClick = config.onExploreClick),
horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing4),
) {
Icon(
painter = painterResource(id = R.drawable.ic_compass_24),
contentDescription = null,
modifier = Modifier.size(size = TangemTheme.dimens.size18),
tint = TangemTheme.colors.icon.informative,
)
Text(
text = stringResource(id = R.string.common_explorer),
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.subtitle2,
)
}
}
}
@Composable
private fun TransactionGroupTitle(
config: WalletContentItemState.SingleCurrencyItem.TransactionGroupTitle,
modifier: Modifier = Modifier,
) {
Text(
text = config.title,
modifier = modifier
.background(TangemTheme.colors.background.primary)
.fillMaxWidth()
.padding(
horizontal = TangemTheme.dimens.spacing16,
vertical = TangemTheme.dimens.spacing14,
),
color = TangemTheme.colors.text.tertiary,
textAlign = TextAlign.Start,
style = TangemTheme.typography.body2,
)
}
private fun Modifier.clipFirstAndLastItems(index: Int, lastItemIndex: Int): Modifier = composed {
when (index) {
0 -> {
@ -143,6 +213,9 @@ private fun WalletScreenPreview_Dark(
}
private class WalletScreenParameterProvider : CollectionPreviewParameterProvider<WalletStateHolder>(
collection = listOf(WalletPreviewData.groupedWalletScreenState),
collection = listOf(
WalletPreviewData.multicurrencyWalletScreenState,
WalletPreviewData.singleWalletScreenState,
),
)
// endregion Preview

View file

@ -7,7 +7,7 @@ import androidx.compose.ui.tooling.preview.Preview
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.wallet.state.WalletStateHolder
import com.tangem.feature.wallet.presentation.wallet.state.WalletTopBarConfig
/**
* Wallet screen top bar
@ -16,7 +16,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
internal fun WalletTopBar(config: WalletStateHolder.TopBarConfig) {
internal fun WalletTopBar(config: WalletTopBarConfig) {
TopAppBar(
title = {
Icon(painter = painterResource(id = R.drawable.img_tangem_logo_90_24), contentDescription = null)

View file

@ -27,9 +27,9 @@ internal class WalletViewModel @Inject constructor() : ViewModel() {
private set
// TODO: [REDACTED_TASK_KEY] Use production data instead of WalletPreviewData
private fun getInitialState(): WalletStateHolder = WalletPreviewData.groupedWalletScreenState.copy(
private fun getInitialState(): WalletStateHolder = WalletPreviewData.multicurrencyWalletScreenState.copy(
onBackClick = { router.popBackStack() },
topBarConfig = WalletPreviewData.groupedWalletScreenState.topBarConfig.copy(
topBarConfig = WalletPreviewData.multicurrencyWalletScreenState.topBarConfig.copy(
onScanCardClick = { router.openOrganizeTokensScreen() },
),
)