Updated on 2026-08-14
This commit is contained in:
parent
95d45c5a19
commit
53100fe46a
23 changed files with 495 additions and 400 deletions
|
|
@ -1,12 +1,16 @@
|
|||
package com.tangem.tap.features.tokens.impl.presentation.states
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
/**
|
||||
* Network item state
|
||||
* Token item state.
|
||||
* All subclasses is stable, but @Immutable annotation is required to use this sealed class like as
|
||||
* field of TokensListStateHolder.
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Immutable
|
||||
sealed interface TokenItemState {
|
||||
|
||||
/** Token id */
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.features.tokens.impl.presentation.states
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.paging.LoadState
|
||||
import androidx.paging.PagingData
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -9,6 +10,7 @@ import kotlinx.coroutines.flow.Flow
|
|||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Immutable
|
||||
internal sealed interface TokensListStateHolder {
|
||||
|
||||
/** Toolbar state */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
package com.tangem.tap.features.tokens.impl.presentation.states
|
||||
|
||||
/** Toolbar state */
|
||||
import androidx.compose.runtime.Immutable
|
||||
|
||||
/**
|
||||
* Toolbar state.
|
||||
* All subclasses is stable, but @Immutable annotation is required to use this sealed class like as
|
||||
* field of TokensListStateHolder.
|
||||
*/
|
||||
@Immutable
|
||||
sealed interface TokensListToolbarState {
|
||||
|
||||
/** Callback to be invoked when BackButton is being clicked */
|
||||
|
|
|
|||
|
|
@ -312,14 +312,8 @@ private fun TangemButton(
|
|||
textStyle: TextStyle = TangemTheme.typography.button,
|
||||
) {
|
||||
Button(
|
||||
modifier = modifier
|
||||
.width(IntrinsicSize.Min)
|
||||
.heightIn(min = size.toHeightDp()),
|
||||
onClick = {
|
||||
if (!showProgress) {
|
||||
onClick()
|
||||
}
|
||||
},
|
||||
modifier = modifier.heightIn(min = size.toHeightDp()),
|
||||
onClick = { if (!showProgress) onClick() },
|
||||
enabled = enabled,
|
||||
elevation = elevation,
|
||||
shape = size.toShape(),
|
||||
|
|
@ -588,59 +582,44 @@ private open class TangemButtonColors(
|
|||
|
||||
// region Preview
|
||||
@Composable
|
||||
private fun PrimaryButtonSample(modifier: Modifier = Modifier) {
|
||||
private fun PrimaryButtonSample() {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary),
|
||||
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||
) {
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
showProgress = true,
|
||||
text = "Manage tokens",
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
PrimaryButton(modifier = Modifier.fillMaxWidth(), text = "Manage tokens", onClick = { })
|
||||
PrimaryButton(modifier = Modifier.fillMaxWidth(), showProgress = true, text = "Manage tokens", onClick = { })
|
||||
PrimaryButtonIconEnd(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
onClick = { /* no-op */ },
|
||||
onClick = { },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
PrimaryButtonIconStart(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
onClick = { /* no-op */ },
|
||||
onClick = { },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
enabled = false,
|
||||
onClick = { /* no-op */ },
|
||||
onClick = { },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
PrimaryButtonIconEnd(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
enabled = false,
|
||||
onClick = { /* no-op */ },
|
||||
onClick = { },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
PrimaryButtonIconStart(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
enabled = false,
|
||||
onClick = { /* no-op */ },
|
||||
onClick = { },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -662,59 +641,44 @@ private fun PrimaryButtonPreview_Dark() {
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun SecondaryButtonSample(modifier: Modifier = Modifier) {
|
||||
private fun SecondaryButtonSample() {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary),
|
||||
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||
) {
|
||||
SecondaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
SecondaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
showProgress = true,
|
||||
text = "Manage tokens",
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
SecondaryButton(modifier = Modifier.fillMaxWidth(), text = "Manage tokens", onClick = { })
|
||||
SecondaryButton(modifier = Modifier.fillMaxWidth(), showProgress = true, text = "Manage tokens", onClick = { })
|
||||
SecondaryButtonIconEnd(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
onClick = { /* no-op */ },
|
||||
onClick = { },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
SecondaryButtonIconStart(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
onClick = { /* no-op */ },
|
||||
onClick = { },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
SecondaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
enabled = false,
|
||||
onClick = { /* no-op */ },
|
||||
onClick = { },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
SecondaryButtonIconEnd(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
enabled = false,
|
||||
onClick = { /* no-op */ },
|
||||
onClick = { },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
SecondaryButtonIconStart(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Manage tokens",
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
enabled = false,
|
||||
onClick = { /* no-op */ },
|
||||
onClick = { },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -736,50 +700,23 @@ private fun SecondaryButtonPreview_Dark() {
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun TextButtonSample(modifier: Modifier = Modifier) {
|
||||
private fun TextButtonSample() {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary),
|
||||
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||
) {
|
||||
TextButton(
|
||||
text = "Enabled",
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
TextButtonIconStart(
|
||||
text = "Enabled",
|
||||
iconResId = R.drawable.ic_plus_24,
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
TextButton(
|
||||
text = "Enabled",
|
||||
enabled = false,
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
TextButtonIconStart(
|
||||
text = "Enabled",
|
||||
iconResId = R.drawable.ic_plus_24,
|
||||
enabled = false,
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
WarningTextButton(
|
||||
text = "Delete",
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
SelectorButton(
|
||||
text = "USD",
|
||||
onClick = { /* no-op */ },
|
||||
)
|
||||
TextButton(text = "Enabled", onClick = { })
|
||||
TextButtonIconStart(text = "Enabled", iconResId = R.drawable.ic_plus_24, onClick = { })
|
||||
TextButton(text = "Enabled", enabled = false, onClick = { })
|
||||
TextButtonIconStart(text = "Enabled", iconResId = R.drawable.ic_plus_24, enabled = false, onClick = { })
|
||||
WarningTextButton(text = "Delete", onClick = { })
|
||||
SelectorButton(text = "USD", onClick = { })
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun TextButtonPreview_Light() {
|
||||
private fun TextButtonPreview_LightTheme() {
|
||||
TangemTheme {
|
||||
TextButtonSample()
|
||||
}
|
||||
|
|
@ -787,62 +724,30 @@ private fun TextButtonPreview_Light() {
|
|||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun TextButtonPreview_Dark() {
|
||||
private fun TextButtonPreview_DarkTheme() {
|
||||
TangemTheme(isDark = true) {
|
||||
TextButtonSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ActionButtonSample(modifier: Modifier = Modifier) {
|
||||
private fun ActionButtonSample() {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary),
|
||||
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||
) {
|
||||
RoundedActionButton(
|
||||
text = "Send",
|
||||
iconResId = R.drawable.ic_arrow_up_24,
|
||||
onClick = { /* [REDACTED_TODO_COMMENT]*/ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
ActionButton(
|
||||
text = "Send",
|
||||
iconResId = R.drawable.ic_arrow_up_24,
|
||||
onClick = { /* [REDACTED_TODO_COMMENT]*/ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
BackgroundActionButton(
|
||||
text = "Send",
|
||||
iconResId = R.drawable.ic_arrow_up_24,
|
||||
onClick = { /* [REDACTED_TODO_COMMENT]*/ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
RoundedActionButton(
|
||||
text = "Send",
|
||||
iconResId = R.drawable.ic_arrow_up_24,
|
||||
enabled = false,
|
||||
onClick = { /* [REDACTED_TODO_COMMENT]*/ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
ActionButton(
|
||||
text = "Send",
|
||||
iconResId = R.drawable.ic_arrow_up_24,
|
||||
enabled = false,
|
||||
onClick = { /* [REDACTED_TODO_COMMENT]*/ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
BackgroundActionButton(
|
||||
text = "Send",
|
||||
iconResId = R.drawable.ic_arrow_up_24,
|
||||
enabled = false,
|
||||
onClick = { /* [REDACTED_TODO_COMMENT]*/ },
|
||||
)
|
||||
RoundedActionButton(text = "Send", iconResId = R.drawable.ic_arrow_up_24, onClick = { })
|
||||
ActionButton(text = "Send", iconResId = R.drawable.ic_arrow_up_24, onClick = { })
|
||||
BackgroundActionButton(text = "Send", iconResId = R.drawable.ic_arrow_up_24, onClick = { })
|
||||
RoundedActionButton(text = "Send", iconResId = R.drawable.ic_arrow_up_24, enabled = false, onClick = { })
|
||||
ActionButton(text = "Send", iconResId = R.drawable.ic_arrow_up_24, enabled = false, onClick = { })
|
||||
BackgroundActionButton(text = "Send", iconResId = R.drawable.ic_arrow_up_24, enabled = false, onClick = { })
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun ActionButtonPreview_Light() {
|
||||
private fun ActionButtonPreview_LightTheme() {
|
||||
TangemTheme {
|
||||
ActionButtonSample()
|
||||
}
|
||||
|
|
@ -850,7 +755,7 @@ private fun ActionButtonPreview_Light() {
|
|||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun ActionButtonPreview_Dark() {
|
||||
private fun ActionButtonPreview_DarkTheme() {
|
||||
TangemTheme(isDark = true) {
|
||||
ActionButtonSample()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,22 +2,42 @@ package com.tangem.core.ui.extensions
|
|||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
||||
/**
|
||||
* Utility class for creating text as [String] or [StringRes].
|
||||
* It necessary to use [Immutable] annotation because all sealed interface has runtime stability.
|
||||
* All subclasses are stable.
|
||||
*/
|
||||
@Immutable
|
||||
sealed interface TextReference {
|
||||
class Res(@StringRes val id: Int, val formatArgs: List<Any> = emptyList()) : TextReference {
|
||||
constructor(@StringRes id: Int, vararg formatArgs: Any) : this(id, formatArgs.toList())
|
||||
}
|
||||
|
||||
class Str(val value: String) : TextReference
|
||||
/**
|
||||
* Text resource id
|
||||
*
|
||||
* @property id resource id
|
||||
* @property formatArgs arguments
|
||||
*
|
||||
* Impossible to use [kotlinx.collections.immutable.ImmutableList] because [Any] is unstable.
|
||||
*/
|
||||
data class Res(@StringRes val id: Int, val formatArgs: WrappedList<Any> = WrappedList(emptyList())) : TextReference
|
||||
|
||||
/**
|
||||
* Text string
|
||||
*
|
||||
* @property value value
|
||||
*/
|
||||
data class Str(val value: String) : TextReference
|
||||
}
|
||||
|
||||
/** Get text */
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
fun TextReference.resolveReference(): String {
|
||||
return when (this) {
|
||||
is TextReference.Res -> stringResource(this.id, *this.formatArgs.toTypedArray())
|
||||
is TextReference.Str -> this.value
|
||||
is TextReference.Res -> stringResource(id, *formatArgs.toTypedArray())
|
||||
is TextReference.Str -> value
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.core.ui.extensions
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@JvmInline
|
||||
@Immutable
|
||||
value class WrappedList<T>(val data: List<T>) : List<T> by data
|
||||
9
core/ui/src/main/res/drawable/ic_filter_24.xml
Normal file
9
core/ui/src/main/res/drawable/ic_filter_24.xml
Normal 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:pathData="M6,13H18V11H6M3,6V8H21V6M10,18H14V16H10V18Z"
|
||||
android:fillColor="#000000" />
|
||||
</vector>
|
||||
|
|
@ -22,6 +22,8 @@ dependencies {
|
|||
implementation(deps.compose.ui.tooling)
|
||||
implementation(deps.compose.shimmer)
|
||||
implementation(deps.compose.accompanist.systemUiController)
|
||||
|
||||
/** Other libraries */
|
||||
implementation(deps.kotlin.immutable.collections)
|
||||
|
||||
/** DI */
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
package com.tangem.feature.wallet.presentation.common
|
||||
|
||||
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.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.WalletContentItemState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
|
@ -15,7 +15,9 @@ import java.util.UUID
|
|||
|
||||
internal object WalletPreviewData {
|
||||
|
||||
val walletCardContent = WalletCardState.Content(
|
||||
val walletTopBarConfig = WalletStateHolder.TopBarConfig(onScanCardClick = {}, onMoreClick = {})
|
||||
|
||||
val walletCardContentState = WalletCardState.Content(
|
||||
id = UUID.randomUUID().toString(),
|
||||
title = "Wallet 1",
|
||||
balance = "8923,05 $",
|
||||
|
|
@ -24,7 +26,7 @@ internal object WalletPreviewData {
|
|||
onClick = {},
|
||||
)
|
||||
|
||||
val walletCardLoading = WalletCardState.Loading(
|
||||
val walletCardLoadingState = WalletCardState.Loading(
|
||||
id = UUID.randomUUID().toString(),
|
||||
title = "Wallet 1",
|
||||
additionalInfo = "3 cards • Seed enabled",
|
||||
|
|
@ -32,7 +34,7 @@ internal object WalletPreviewData {
|
|||
onClick = {},
|
||||
)
|
||||
|
||||
val walletCardHiddenContent = WalletCardState.HiddenContent(
|
||||
val walletCardHiddenContentState = WalletCardState.HiddenContent(
|
||||
id = UUID.randomUUID().toString(),
|
||||
title = "Wallet 1",
|
||||
additionalInfo = "3 cards • Seed enabled",
|
||||
|
|
@ -40,7 +42,7 @@ internal object WalletPreviewData {
|
|||
onClick = {},
|
||||
)
|
||||
|
||||
val walletCardError = WalletCardState.Error(
|
||||
val walletCardErrorState = WalletCardState.Error(
|
||||
id = UUID.randomUUID().toString(),
|
||||
title = "Wallet 1",
|
||||
additionalInfo = "3 cards • Seed enabled",
|
||||
|
|
@ -48,15 +50,6 @@ internal object WalletPreviewData {
|
|||
onClick = {},
|
||||
)
|
||||
|
||||
val walletScreenState = WalletStateHolder(
|
||||
onBackClick = {},
|
||||
headerConfig = WalletStateHolder.HeaderConfig(
|
||||
wallets = persistentListOf(walletCardContent, walletCardLoading, walletCardHiddenContent, walletCardError),
|
||||
onScanCardClick = {},
|
||||
onMoreClick = {},
|
||||
),
|
||||
)
|
||||
|
||||
val tokenItemVisibleState = TokenItemState.Content(
|
||||
id = UUID.randomUUID().toString(),
|
||||
tokenIconUrl = null,
|
||||
|
|
@ -109,37 +102,6 @@ internal object WalletPreviewData {
|
|||
|
||||
val loadingTokenItemState = TokenItemState.Loading(id = UUID.randomUUID().toString())
|
||||
|
||||
val networkGroup = NetworkGroupState.Content(
|
||||
id = UUID.randomUUID().toString(),
|
||||
networkName = "Ethereum",
|
||||
tokens = persistentListOf(
|
||||
tokenItemVisibleState.copy(
|
||||
id = "token_1",
|
||||
name = "Ethereum",
|
||||
tokenIconResId = R.drawable.img_eth_22,
|
||||
networkIconResId = null,
|
||||
amount = "1,89340821 ETH",
|
||||
),
|
||||
tokenItemVisibleState.copy(
|
||||
id = "token_2",
|
||||
networkIconResId = R.drawable.img_eth_22,
|
||||
amount = "733,71097 MATIC",
|
||||
),
|
||||
tokenItemVisibleState.copy(
|
||||
id = "token_3",
|
||||
name = "USDT",
|
||||
tokenIconResId = R.drawable.img_arbitrum_22,
|
||||
networkIconResId = R.drawable.img_eth_22,
|
||||
amount = "0,25404523 ARB",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
val draggableNetworkGroup = NetworkGroupState.Draggable(
|
||||
id = "group_1",
|
||||
networkName = "Ethereum",
|
||||
)
|
||||
|
||||
private const val networksSize = 10
|
||||
private const val tokensSize = 3
|
||||
val draggableItems = List(networksSize) { it }
|
||||
|
|
@ -147,10 +109,8 @@ internal object WalletPreviewData {
|
|||
val n = index + 1
|
||||
|
||||
val group = DraggableItem.GroupHeader(
|
||||
groupState = NetworkGroupState.Draggable(
|
||||
id = "group_$n",
|
||||
networkName = "$n",
|
||||
),
|
||||
id = "group_$n",
|
||||
networkName = "$n",
|
||||
)
|
||||
|
||||
val tokens: MutableList<DraggableItem.Token> = mutableListOf()
|
||||
|
|
@ -198,4 +158,66 @@ internal object WalletPreviewData {
|
|||
items = draggableTokens,
|
||||
),
|
||||
)
|
||||
|
||||
val groupedWalletScreenState = WalletStateHolder(
|
||||
onBackClick = {},
|
||||
topBarConfig = walletTopBarConfig,
|
||||
selectedWallet = walletCardContentState,
|
||||
wallets = persistentListOf(
|
||||
walletCardContentState,
|
||||
walletCardLoadingState,
|
||||
walletCardHiddenContentState,
|
||||
walletCardErrorState,
|
||||
),
|
||||
contentItems = persistentListOf(
|
||||
WalletContentItemState.NetworkGroupTitle("Bitcoin"),
|
||||
WalletContentItemState.Token(
|
||||
tokenItemVisibleState.copy(
|
||||
id = "token_1",
|
||||
name = "Ethereum",
|
||||
tokenIconResId = R.drawable.img_eth_22,
|
||||
networkIconResId = null,
|
||||
amount = "1,89340821 ETH",
|
||||
),
|
||||
),
|
||||
WalletContentItemState.Token(
|
||||
tokenItemVisibleState.copy(
|
||||
id = "token_2",
|
||||
name = "Ethereum",
|
||||
tokenIconResId = R.drawable.img_eth_22,
|
||||
networkIconResId = null,
|
||||
amount = "1,89340821 ETH",
|
||||
),
|
||||
),
|
||||
WalletContentItemState.Token(
|
||||
tokenItemVisibleState.copy(
|
||||
id = "token_3",
|
||||
name = "Ethereum",
|
||||
tokenIconResId = R.drawable.img_eth_22,
|
||||
networkIconResId = null,
|
||||
amount = "1,89340821 ETH",
|
||||
),
|
||||
),
|
||||
WalletContentItemState.Token(
|
||||
tokenItemVisibleState.copy(
|
||||
id = "token_4",
|
||||
name = "Ethereum",
|
||||
tokenIconResId = R.drawable.img_eth_22,
|
||||
networkIconResId = null,
|
||||
amount = "1,89340821 ETH",
|
||||
),
|
||||
),
|
||||
WalletContentItemState.NetworkGroupTitle("Ethereum"),
|
||||
WalletContentItemState.Token(
|
||||
tokenItemVisibleState.copy(
|
||||
id = "token_5",
|
||||
name = "Ethereum",
|
||||
tokenIconResId = R.drawable.img_eth_22,
|
||||
networkIconResId = null,
|
||||
amount = "1,89340821 ETH",
|
||||
),
|
||||
),
|
||||
),
|
||||
onOrganizeTokensClick = {},
|
||||
)
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.*
|
|||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
|
|
@ -15,29 +14,20 @@ 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 com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.common.state.NetworkGroupState
|
||||
|
||||
@Composable
|
||||
internal fun NetworkGroupItem(state: NetworkGroupState.Content, modifier: Modifier = Modifier) {
|
||||
internal fun NetworkGroupItem(networkName: String, modifier: Modifier = Modifier) {
|
||||
InternalNetworkGroupItem(
|
||||
modifier = modifier,
|
||||
networkName = state.networkName,
|
||||
tokens = {
|
||||
state.tokens.forEach { token ->
|
||||
key(token.id) {
|
||||
TokenItem(state = token)
|
||||
}
|
||||
}
|
||||
},
|
||||
networkName = networkName,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun DraggableNetworkGroupItem(state: NetworkGroupState.Draggable, modifier: Modifier = Modifier) {
|
||||
internal fun DraggableNetworkGroupItem(networkName: String, modifier: Modifier = Modifier) {
|
||||
InternalNetworkGroupItem(
|
||||
modifier = modifier,
|
||||
networkName = state.networkName,
|
||||
networkName = networkName,
|
||||
endIcon = {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_group_drop_24),
|
||||
|
|
@ -52,14 +42,13 @@ internal fun DraggableNetworkGroupItem(state: NetworkGroupState.Draggable, modif
|
|||
private fun InternalNetworkGroupItem(
|
||||
networkName: String,
|
||||
modifier: Modifier = Modifier,
|
||||
tokens: @Composable ColumnScope.() -> Unit = {},
|
||||
endIcon: @Composable RowScope.() -> Unit = {},
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.padding(horizontal = TangemTheme.dimens.spacing12)
|
||||
.padding(horizontal = TangemTheme.dimens.spacing14)
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = TangemTheme.dimens.size48),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
|
@ -72,39 +61,36 @@ private fun InternalNetworkGroupItem(
|
|||
)
|
||||
endIcon()
|
||||
}
|
||||
tokens()
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
private fun NetworkGroupItemSample(group: NetworkGroupState) {
|
||||
when (group) {
|
||||
is NetworkGroupState.Content -> NetworkGroupItem(group)
|
||||
is NetworkGroupState.Draggable -> DraggableNetworkGroupItem(group)
|
||||
private fun NetworkGroupItemSample(isDraggable: Boolean) {
|
||||
if (isDraggable) {
|
||||
DraggableNetworkGroupItem(networkName = "Ethereum")
|
||||
} else {
|
||||
NetworkGroupItem(networkName = "Ethereum")
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun NetworkGroupItemPreview_Light(@PreviewParameter(NetworkGroupProvider::class) group: NetworkGroupState) {
|
||||
private fun NetworkGroupItemPreview_Light(@PreviewParameter(NetworkGroupProvider::class) isDraggable: Boolean) {
|
||||
TangemTheme {
|
||||
NetworkGroupItemSample(group)
|
||||
NetworkGroupItemSample(isDraggable)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun NetworkGroupItemPreview_Dark(@PreviewParameter(NetworkGroupProvider::class) group: NetworkGroupState) {
|
||||
private fun NetworkGroupItemPreview_Dark(@PreviewParameter(NetworkGroupProvider::class) isDraggable: Boolean) {
|
||||
TangemTheme(isDark = true) {
|
||||
NetworkGroupItemSample(group)
|
||||
NetworkGroupItemSample(isDraggable)
|
||||
}
|
||||
}
|
||||
|
||||
private class NetworkGroupProvider : CollectionPreviewParameterProvider<NetworkGroupState>(
|
||||
collection = listOf(
|
||||
WalletPreviewData.networkGroup,
|
||||
WalletPreviewData.draggableNetworkGroup,
|
||||
),
|
||||
private class NetworkGroupProvider : CollectionPreviewParameterProvider<Boolean>(
|
||||
collection = listOf(true, false),
|
||||
)
|
||||
// endregion Preview
|
||||
|
|
@ -206,7 +206,7 @@ private fun InternalTokenItem(
|
|||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing12,
|
||||
horizontal = TangemTheme.dimens.spacing14,
|
||||
vertical = TangemTheme.dimens.spacing4,
|
||||
),
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.common.state
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Immutable
|
||||
internal sealed interface NetworkGroupState {
|
||||
val id: String
|
||||
val networkName: String
|
||||
|
||||
data class Draggable(
|
||||
override val id: String,
|
||||
override val networkName: String,
|
||||
) : NetworkGroupState
|
||||
|
||||
data class Content(
|
||||
override val id: String,
|
||||
override val networkName: String,
|
||||
val tokens: ImmutableList<TokenItemState>,
|
||||
) : NetworkGroupState
|
||||
}
|
||||
|
|
@ -93,7 +93,7 @@ private fun DraggableItem(index: Int, item: DraggableItem, lastItemIndex: Int) {
|
|||
when (item) {
|
||||
is DraggableItem.GroupHeader -> DraggableNetworkGroupItem(
|
||||
modifier = itemModifier,
|
||||
state = item.groupState,
|
||||
networkName = item.networkName,
|
||||
)
|
||||
is DraggableItem.Token -> DraggableTokenItem(
|
||||
modifier = itemModifier,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.organizetokens
|
||||
|
||||
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
|
||||
|
|
@ -51,10 +50,9 @@ internal sealed interface DraggableItem {
|
|||
val id: String
|
||||
|
||||
data class GroupHeader(
|
||||
val groupState: NetworkGroupState.Draggable,
|
||||
) : DraggableItem {
|
||||
override val id: String = groupState.id
|
||||
}
|
||||
override val id: String,
|
||||
val networkName: String,
|
||||
) : DraggableItem
|
||||
|
||||
data class Token(
|
||||
val tokenItemState: TokenItemState.Draggable,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.runtime.Immutable
|
||||
|
||||
/** Wallet card state */
|
||||
@Immutable
|
||||
internal sealed interface WalletCardState {
|
||||
|
||||
/** Id */
|
||||
val id: String
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state
|
||||
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal sealed interface WalletContentItemState {
|
||||
|
||||
data class NetworkGroupTitle(val networkName: String) : WalletContentItemState
|
||||
|
||||
data class Token(val tokenItemState: TokenItemState) : WalletContentItemState
|
||||
}
|
||||
|
|
@ -5,26 +5,28 @@ import kotlinx.collections.immutable.ImmutableList
|
|||
/**
|
||||
* Wallet state holder
|
||||
*
|
||||
* @property onBackClick lambda be invoked when back button is clicked
|
||||
* @property headerConfig header config
|
||||
* @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
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal data class WalletStateHolder(
|
||||
val onBackClick: () -> Unit,
|
||||
val headerConfig: HeaderConfig,
|
||||
val topBarConfig: TopBarConfig,
|
||||
val selectedWallet: WalletCardState,
|
||||
val wallets: ImmutableList<WalletCardState>,
|
||||
val contentItems: ImmutableList<WalletContentItemState>,
|
||||
val onOrganizeTokensClick: () -> Unit,
|
||||
) {
|
||||
|
||||
/**
|
||||
* Header config
|
||||
* Top bar config
|
||||
*
|
||||
* @property wallets list of wallets states
|
||||
* @property onScanCardClick lambda be invoked when scan card button is clicked
|
||||
* @property onMoreClick lambda be invoked when more button is clicked
|
||||
*/
|
||||
data class HeaderConfig(
|
||||
val wallets: ImmutableList<WalletCardState>,
|
||||
val onScanCardClick: () -> Unit,
|
||||
val onMoreClick: () -> Unit,
|
||||
)
|
||||
data class TopBarConfig(val onScanCardClick: () -> Unit, val onMoreClick: () -> Unit)
|
||||
}
|
||||
|
|
@ -1,10 +1,32 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Scaffold
|
||||
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.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.RoundedActionButton
|
||||
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.component.NetworkGroupItem
|
||||
import com.tangem.feature.wallet.presentation.common.component.TokenItem
|
||||
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.ui.components.WalletHeader
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletCardsList
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletTopBar
|
||||
|
||||
/**
|
||||
* Wallet screen
|
||||
|
|
@ -18,8 +40,109 @@ internal fun WalletScreen(state: WalletStateHolder) {
|
|||
BackHandler(onBack = state.onBackClick)
|
||||
|
||||
Scaffold(
|
||||
topBar = { WalletHeader(config = state.headerConfig) },
|
||||
) {
|
||||
// TODO: [REDACTED_TASK_KEY] Design a body with tokens and transactions
|
||||
topBar = { WalletTopBar(config = state.topBarConfig) },
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
) { scaffoldPaddings ->
|
||||
val lastContentItemIndex = remember(state.contentItems) {
|
||||
state.contentItems.lastIndex
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(scaffoldPaddings)
|
||||
.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
item { WalletCardsList(wallets = state.wallets) }
|
||||
|
||||
itemsIndexed(
|
||||
items = state.contentItems,
|
||||
key = { _, item ->
|
||||
when (item) {
|
||||
is WalletContentItemState.NetworkGroupTitle -> item.networkName
|
||||
is WalletContentItemState.Token -> item.tokenItemState.id
|
||||
}
|
||||
},
|
||||
) { index, item ->
|
||||
val itemModifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.clipFirstAndLastItems(index, lastContentItemIndex)
|
||||
|
||||
when (item) {
|
||||
is WalletContentItemState.NetworkGroupTitle -> {
|
||||
NetworkGroupItem(
|
||||
networkName = item.networkName,
|
||||
modifier = itemModifier,
|
||||
)
|
||||
}
|
||||
is WalletContentItemState.Token -> {
|
||||
TokenItem(
|
||||
state = item.tokenItemState,
|
||||
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),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Modifier.clipFirstAndLastItems(index: Int, lastItemIndex: Int): Modifier = composed {
|
||||
when (index) {
|
||||
0 -> {
|
||||
this
|
||||
.padding(top = TangemTheme.dimens.spacing14)
|
||||
.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 WalletScreenPreview_Light(
|
||||
@PreviewParameter(WalletScreenParameterProvider::class) state: WalletStateHolder,
|
||||
) {
|
||||
TangemTheme {
|
||||
WalletScreen(state)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun WalletScreenPreview_Dark(
|
||||
@PreviewParameter(WalletScreenParameterProvider::class) state: WalletStateHolder,
|
||||
) {
|
||||
TangemTheme(isDark = true) {
|
||||
WalletScreen(state)
|
||||
}
|
||||
}
|
||||
|
||||
private class WalletScreenParameterProvider : CollectionPreviewParameterProvider<WalletStateHolder>(
|
||||
collection = listOf(WalletPreviewData.groupedWalletScreenState),
|
||||
)
|
||||
// endregion Preview
|
||||
|
|
@ -44,7 +44,7 @@ internal fun WalletCard(state: WalletCardState, modifier: Modifier = Modifier) {
|
|||
ConstraintLayout(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = TangemTheme.dimens.spacing12),
|
||||
.padding(horizontal = TangemTheme.dimens.spacing14),
|
||||
) {
|
||||
val (balanceBlock, imageItem) = createRefs()
|
||||
Column(
|
||||
|
|
@ -182,10 +182,10 @@ private fun Preview_WalletCard_DarkTheme(@PreviewParameter(WalletCardStateProvid
|
|||
|
||||
private class WalletCardStateProvider : CollectionPreviewParameterProvider<WalletCardState>(
|
||||
collection = listOf(
|
||||
WalletPreviewData.walletCardContent,
|
||||
WalletPreviewData.walletCardLoading,
|
||||
WalletPreviewData.walletCardHiddenContent,
|
||||
WalletPreviewData.walletCardError,
|
||||
WalletPreviewData.walletCardContentState,
|
||||
WalletPreviewData.walletCardLoadingState,
|
||||
WalletPreviewData.walletCardHiddenContentState,
|
||||
WalletPreviewData.walletCardErrorState,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components
|
||||
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletCardState
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
/**
|
||||
* Wallets list
|
||||
*
|
||||
* @param wallets list of wallet state
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
internal fun WalletCardsList(wallets: ImmutableList<WalletCardState>) {
|
||||
val horizontalCardPadding = TangemTheme.dimens.spacing16
|
||||
val itemWidth = LocalConfiguration.current.screenWidthDp.dp - horizontalCardPadding * 2
|
||||
|
||||
val lazyListState = rememberLazyListState()
|
||||
LazyRow(
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.secondary),
|
||||
state = lazyListState,
|
||||
contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing16),
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||
flingBehavior = rememberSnapFlingBehavior(lazyListState = lazyListState),
|
||||
) {
|
||||
items(items = wallets, key = WalletCardState::id) { state ->
|
||||
WalletCard(state = state, modifier = Modifier.width(itemWidth))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun Preview_WalletHeader_LightTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
WalletCardsList(
|
||||
wallets = persistentListOf(
|
||||
WalletPreviewData.walletCardContentState,
|
||||
WalletPreviewData.walletCardLoadingState,
|
||||
WalletPreviewData.walletCardHiddenContentState,
|
||||
WalletPreviewData.walletCardErrorState,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun Preview_WalletHeader_DarkTheme() {
|
||||
TangemTheme(isDark = true) {
|
||||
WalletCardsList(
|
||||
wallets = persistentListOf(
|
||||
WalletPreviewData.walletCardContentState,
|
||||
WalletPreviewData.walletCardLoadingState,
|
||||
WalletPreviewData.walletCardHiddenContentState,
|
||||
WalletPreviewData.walletCardErrorState,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components
|
||||
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.LocalOverscrollConfiguration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
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 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.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
/**
|
||||
* Wallet screen header
|
||||
*
|
||||
* @param config config
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
internal fun WalletHeader(config: WalletStateHolder.HeaderConfig) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(color = TangemTheme.colors.background.secondary)
|
||||
.padding(bottom = TangemTheme.dimens.spacing14),
|
||||
) {
|
||||
TopBar(onScanCardClick = config.onScanCardClick, onMoreClick = config.onMoreClick)
|
||||
|
||||
CompositionLocalProvider(LocalOverscrollConfiguration provides null) {
|
||||
val lazyListState = rememberLazyListState()
|
||||
LazyRow(
|
||||
state = lazyListState,
|
||||
contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing16),
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||
flingBehavior = rememberSnapFlingBehavior(lazyListState = lazyListState),
|
||||
) {
|
||||
items(items = config.wallets, key = WalletCardState::id) { state ->
|
||||
WalletCard(
|
||||
state = state,
|
||||
modifier = Modifier.width(
|
||||
LocalConfiguration.current.screenWidthDp.dp - TangemTheme.dimens.size32,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(onScanCardClick: () -> Unit, onMoreClick: () -> Unit) {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Icon(painter = painterResource(id = R.drawable.img_tangem_logo_90_24), contentDescription = null)
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = onScanCardClick) {
|
||||
Icon(painter = painterResource(id = R.drawable.ic_tap_card_24), contentDescription = "Scan card")
|
||||
}
|
||||
IconButton(onClick = onMoreClick) {
|
||||
Icon(painter = painterResource(id = R.drawable.ic_more_vertical_24), contentDescription = "More")
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
titleContentColor = TangemTheme.colors.icon.primary1,
|
||||
actionIconContentColor = TangemTheme.colors.icon.primary1,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun Preview_WalletHeader_LightTheme(
|
||||
@PreviewParameter(WalletHeaderProvider::class) state: WalletStateHolder.HeaderConfig,
|
||||
) {
|
||||
TangemTheme(isDark = false) {
|
||||
WalletHeader(state)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun Preview_WalletHeader_DarkTheme(
|
||||
@PreviewParameter(WalletHeaderProvider::class) state: WalletStateHolder.HeaderConfig,
|
||||
) {
|
||||
TangemTheme(isDark = true) {
|
||||
WalletHeader(state)
|
||||
}
|
||||
}
|
||||
|
||||
private class WalletHeaderProvider : CollectionPreviewParameterProvider<WalletStateHolder.HeaderConfig>(
|
||||
collection = listOf(
|
||||
WalletStateHolder.HeaderConfig(
|
||||
wallets = persistentListOf(
|
||||
WalletPreviewData.walletCardContent,
|
||||
WalletPreviewData.walletCardLoading,
|
||||
WalletPreviewData.walletCardHiddenContent,
|
||||
WalletPreviewData.walletCardError,
|
||||
),
|
||||
onScanCardClick = {},
|
||||
onMoreClick = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components
|
||||
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.painterResource
|
||||
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
|
||||
|
||||
/**
|
||||
* Wallet screen top bar
|
||||
*
|
||||
* @param config top bar config
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
internal fun WalletTopBar(config: WalletStateHolder.TopBarConfig) {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Icon(painter = painterResource(id = R.drawable.img_tangem_logo_90_24), contentDescription = null)
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = config.onScanCardClick) {
|
||||
Icon(painter = painterResource(id = R.drawable.ic_tap_card_24), contentDescription = "Scan card")
|
||||
}
|
||||
IconButton(onClick = config.onMoreClick) {
|
||||
Icon(painter = painterResource(id = R.drawable.ic_more_vertical_24), contentDescription = "More")
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
titleContentColor = TangemTheme.colors.icon.primary1,
|
||||
actionIconContentColor = TangemTheme.colors.icon.primary1,
|
||||
),
|
||||
scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(),
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun Preview_WalletTopBar_LightTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
WalletTopBar(config = WalletPreviewData.walletTopBarConfig)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun Preview_WalletTopBar_DarkTheme() {
|
||||
TangemTheme(isDark = true) {
|
||||
WalletTopBar(config = WalletPreviewData.walletTopBarConfig)
|
||||
}
|
||||
}
|
||||
|
|
@ -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.walletScreenState.copy(
|
||||
private fun getInitialState(): WalletStateHolder = WalletPreviewData.groupedWalletScreenState.copy(
|
||||
onBackClick = { router.popBackStack() },
|
||||
headerConfig = WalletPreviewData.walletScreenState.headerConfig.copy(
|
||||
topBarConfig = WalletPreviewData.groupedWalletScreenState.topBarConfig.copy(
|
||||
onScanCardClick = { router.openOrganizeTokensScreen() },
|
||||
),
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue