Updated on 2026-08-14

This commit is contained in:
Tangem 2023-11-14 15:44:02 +04:00
parent a2b09c6d37
commit 51540c732a
20 changed files with 915 additions and 34 deletions

View file

@ -63,6 +63,7 @@ internal fun SettingsSwitchItem(item: Item.Switch, modifier: Modifier = Modifier
checked = item.isChecked,
enabled = item.isEnabled,
onCheckedChange = item.onCheckedChange,
checkedColor = TangemTheme.colors.icon.accent,
)
}
}

View file

@ -13,6 +13,7 @@ import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
@ -162,11 +163,12 @@ fun CardWithIcon(
* >Figma component</a>
*/
@Composable
fun IconWithTitleAndDescription(
internal fun IconWithTitleAndDescription(
title: String,
description: String,
description: String?,
icon: @Composable () -> Unit,
additionalContent: @Composable () -> Unit = {},
iconBackground: Color = TangemTheme.colors.background.secondary,
) {
Row(
modifier = Modifier
@ -182,7 +184,7 @@ fun IconWithTitleAndDescription(
Box(
modifier = Modifier
.background(
color = TangemTheme.colors.background.secondary,
color = iconBackground,
shape = CircleShape,
)
.height(TangemTheme.dimens.size40)
@ -205,12 +207,14 @@ fun IconWithTitleAndDescription(
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography.subtitle1,
)
SpacerH4()
Text(
text = description,
color = TangemTheme.colors.text.secondary,
style = TangemTheme.typography.body2,
)
if (description != null) {
SpacerH4()
Text(
text = description,
color = TangemTheme.colors.text.secondary,
style = TangemTheme.typography.body2,
)
}
}
additionalContent()

View file

@ -23,7 +23,7 @@ import com.tangem.core.ui.res.TangemTheme
@Composable
fun TangemSwitch(
onCheckedChange: (Boolean) -> Unit,
checkedColor: Color = TangemTheme.colors.icon.accent,
checkedColor: Color = TangemTheme.colors.control.checked,
uncheckedColor: Color = TangemTheme.colors.icon.informative,
size: Dp = 48.dp,
checked: Boolean = false,

View file

@ -9,6 +9,7 @@ import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.R
@ -36,6 +37,28 @@ fun WarningCard(title: String, description: String, icon: @Composable (() -> Uni
)
}
/**
* A card with a warning icon to the left and title without description shown to the right of it.
*
* @param title title of the warning in bold
*
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=290%3A217&t=yMepJZTRh5bLkOoJ-1"
* >Figma component</a>
*/
@Composable
fun WarningCardTitleOnly(title: String, icon: @Composable (() -> Unit)? = null) {
WarningCardMaterial3Style(
content = {
WarningBody(
title = title,
description = null,
icon = icon,
iconBackground = TangemTheme.colors.button.disabled,
)
},
)
}
/**
* [WarningCard], but clickable (with an 'greater then' icon to the left)
*
@ -105,7 +128,8 @@ fun RefreshableWaringCard(
@Composable
private fun WarningBody(
title: String,
description: String,
description: String?,
iconBackground: Color = TangemTheme.colors.background.secondary,
icon: @Composable (() -> Unit)? = null,
additionalContent: @Composable () -> Unit = {},
) {
@ -119,6 +143,7 @@ private fun WarningBody(
contentDescription = null,
)
},
iconBackground = iconBackground,
)
}
@ -136,6 +161,20 @@ private fun WarningCardSurface(onClick: (() -> Unit)? = null, content: @Composab
}
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
private fun WarningCardMaterial3Style(onClick: (() -> Unit)? = null, content: @Composable () -> Unit) {
Card(
shape = RoundedCornerShape(TangemTheme.dimens.radius16),
backgroundColor = TangemTheme.colors.button.disabled,
elevation = TangemTheme.dimens.elevation0,
onClick = onClick ?: {},
enabled = onClick != null,
) {
content()
}
}
// endregion elements
// region Preview

View file

@ -7,18 +7,34 @@ import androidx.compose.ui.composed
import androidx.compose.ui.draw.clip
import com.tangem.core.ui.res.TangemTheme
fun Modifier.roundedShapeItemDecoration(currentIndex: Int, lastIndex: Int): Modifier = composed {
val modifierWithHorizontalPadding = this.padding(horizontal = TangemTheme.dimens.spacing16)
fun Modifier.roundedShapeItemDecoration(
currentIndex: Int,
lastIndex: Int,
addDefaultPadding: Boolean = true,
): Modifier = composed {
val modifier = if (addDefaultPadding) this.padding(horizontal = TangemTheme.dimens.spacing16) else this
val isSingleItem = currentIndex == 0 && lastIndex == 0
when {
isSingleItem -> {
modifierWithHorizontalPadding
.padding(top = TangemTheme.dimens.spacing14)
modifier
.then(
if (addDefaultPadding) {
Modifier.padding(top = TangemTheme.dimens.spacing14)
} else {
Modifier
},
)
.clip(shape = TangemTheme.shapes.roundedCornersXMedium)
}
currentIndex == 0 -> {
modifierWithHorizontalPadding
.padding(top = TangemTheme.dimens.spacing14)
modifier
.then(
if (addDefaultPadding) {
Modifier.padding(top = TangemTheme.dimens.spacing14)
} else {
Modifier
},
)
.clip(
shape = RoundedCornerShape(
topStart = TangemTheme.dimens.radius16,
@ -27,7 +43,7 @@ fun Modifier.roundedShapeItemDecoration(currentIndex: Int, lastIndex: Int): Modi
)
}
currentIndex == lastIndex -> {
modifierWithHorizontalPadding
modifier
.clip(
shape = RoundedCornerShape(
bottomStart = TangemTheme.dimens.radius16,
@ -35,6 +51,6 @@ fun Modifier.roundedShapeItemDecoration(currentIndex: Int, lastIndex: Int): Modi
),
)
}
else -> modifierWithHorizontalPadding
else -> modifier
}
}

View file

@ -0,0 +1,31 @@
package com.tangem.managetokens.presentation.common.state
import com.tangem.core.ui.extensions.TextReference
import com.tangem.features.managetokens.impl.R
import kotlinx.collections.immutable.ImmutableList
internal sealed class ChooseWalletState {
data class Choose(
val wallets: ImmutableList<WalletState>,
val selectedWallet: WalletState?,
val onChooseWalletClick: () -> Unit,
val onCloseChoosingWalletClick: () -> Unit,
) : ChooseWalletState()
object NoSelection : ChooseWalletState()
class Warning(val type: ChooseWalletWarning) : ChooseWalletState() {
val message: TextReference
get() = when (type) {
ChooseWalletWarning.SINGLE_CURRENCY ->
TextReference.Res(R.string.manage_tokens_wallet_support_only_one_network_title)
ChooseWalletWarning.WALLET_INCOMPATIBLE ->
TextReference.Res(R.string.manage_tokens_wallet_does_not_supported_blockchain)
}
}
}
enum class ChooseWalletWarning {
SINGLE_CURRENCY,
WALLET_INCOMPATIBLE,
}

View file

@ -0,0 +1,90 @@
package com.tangem.managetokens.presentation.common.state
import androidx.compose.runtime.MutableState
import com.tangem.blockchain.common.Blockchain
import com.tangem.core.ui.extensions.getActiveIconRes
import com.tangem.core.ui.extensions.getGreyedOutIconRes
import com.tangem.managetokens.presentation.managetokens.state.TokenItemState
/**
* Network item state
*
* @property name network name
* @property protocolName network protocol name
* @property id network id
* @property blockchain blockchain
* @property iconRes network icon id from resources
*/
internal sealed interface NetworkItemState {
val name: String
val protocolName: String
val id: String
val blockchain: Blockchain
val iconRes: Int
get() = when (this) {
is Selectable -> this.iconResId
is Toggleable -> this.iconResId.value
}
/**
* Network item state that can be added and deleted
*
* @property name network name
* @property protocolName network protocol name
* @property id network id
* @property blockchain blockchain
* @property iconResId network icon id from resources
* @property isMainNetwork flag that determines if the network is the main network for the token
* @property isAdded flag that determines if the user has saved the network
* @property address contract address
* @property decimals decimal count
* @property onToggleClick lambda be invoked when switch is been toggled
*/
@Suppress("LongParameterList")
class Toggleable(
override val name: String,
override val protocolName: String,
override val id: String,
override val blockchain: Blockchain,
val iconResId: MutableState<Int>,
val isMainNetwork: Boolean,
val isAdded: MutableState<Boolean>,
val address: String?,
val decimals: Int?,
val onToggleClick: (TokenItemState.Loaded, Toggleable) -> Unit,
) : NetworkItemState {
/**
* Change toggle state [isAdded].
*
* It is a hack that helps us to change element of flow
*/
fun changeToggleState() {
val reverseState = !isAdded.value
isAdded.value = reverseState
iconResId.value = if (reverseState) getActiveIconRes(blockchain.id) else getGreyedOutIconRes(blockchain.id)
}
}
/**
* Network item state that can be selected
*
* @property name network name
* @property protocolName network protocol name
* @property iconResId network icon id from resources
* @property id network id
* @property blockchain blockchain
* @property onNetworkClick lambda be invoked when network item is been clicked
*
*/
class Selectable(
override val name: String,
override val protocolName: String,
val iconResId: Int,
override val id: String,
override val blockchain: Blockchain,
val onNetworkClick: (NetworkItemState) -> Unit,
) : NetworkItemState
}

View file

@ -0,0 +1,8 @@
package com.tangem.managetokens.presentation.common.state
internal data class WalletState(
val walletId: String,
val artworkUrl: String?,
val walletName: String,
val onSelected: (String) -> Unit,
)

View file

@ -0,0 +1,27 @@
package com.tangem.managetokens.presentation.common.state.previewdata
import com.tangem.managetokens.presentation.common.state.ChooseWalletState
import com.tangem.managetokens.presentation.common.state.WalletState
import kotlinx.collections.immutable.persistentListOf
internal object ChooseWalletStatePreviewData {
val state: ChooseWalletState.Choose
get() = ChooseWalletState.Choose(
wallets = persistentListOf(
walletState,
walletState.copy(walletId = "2"),
),
selectedWallet = walletState,
onChooseWalletClick = {},
onCloseChoosingWalletClick = {},
)
private val walletState: WalletState
get() = WalletState(
walletName = "My wallet",
walletId = "1",
artworkUrl = "",
onSelected = {},
)
}

View file

@ -0,0 +1,151 @@
package com.tangem.managetokens.presentation.common.ui
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
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 coil.compose.SubcomposeAsyncImage
import coil.request.ImageRequest
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.SpacerW12
import com.tangem.core.ui.components.SpacerWMax
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.managetokens.impl.R
import com.tangem.managetokens.presentation.common.state.ChooseWalletState
import com.tangem.managetokens.presentation.common.state.WalletState
import com.tangem.managetokens.presentation.common.state.previewdata.ChooseWalletStatePreviewData
@Composable
internal fun ChooseWalletScreen(state: ChooseWalletState.Choose, modifier: Modifier = Modifier) {
LazyColumn(
modifier = modifier
.background(TangemTheme.colors.background.tertiary)
.padding(TangemTheme.dimens.spacing16),
) {
item {
Box(
modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size44),
) {
IconButton(
onClick = state.onCloseChoosingWalletClick,
modifier = Modifier.align(Alignment.CenterStart),
) {
Icon(
painterResource(id = R.drawable.ic_back_24),
contentDescription = null,
tint = TangemTheme.colors.icon.primary1,
)
}
Text(
text = stringResource(id = R.string.manage_tokens_wallet_selector_title),
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography.subtitle1,
textAlign = TextAlign.Center,
maxLines = 1,
modifier = Modifier
.fillMaxWidth()
.align(Alignment.Center),
)
}
}
items(
count = state.wallets.count(),
key = { index -> state.wallets[index].walletId },
) { index ->
WalletItem(
wallet = state.wallets[index],
selectedWallet = state.selectedWallet,
modifier = Modifier
.roundedShapeItemDecoration(
currentIndex = index,
lastIndex = state.wallets.lastIndex,
addDefaultPadding = false,
),
)
}
item {
SpacerH(height = TangemTheme.dimens.spacing16)
}
}
}
@Composable
private fun WalletItem(wallet: WalletState, selectedWallet: WalletState?, modifier: Modifier = Modifier) {
Row(
modifier = modifier
.clickable { wallet.onSelected(wallet.walletId) }
.background(TangemTheme.colors.background.action)
.defaultMinSize(minHeight = TangemTheme.dimens.size72)
.padding(horizontal = TangemTheme.dimens.spacing16),
verticalAlignment = Alignment.CenterVertically,
) {
SubcomposeAsyncImage(
modifier = Modifier.size(height = TangemTheme.dimens.size30, width = TangemTheme.dimens.size50),
model = ImageRequest.Builder(context = LocalContext.current)
.data(wallet.artworkUrl)
.crossfade(enable = true)
.build(),
loading = {
Image(
painter = painterResource(R.drawable.card_placeholder_primary),
contentDescription = null,
)
},
error = {
Image(
painter = painterResource(R.drawable.card_placeholder_primary),
contentDescription = null,
)
},
contentDescription = null,
)
SpacerW12()
Text(
text = wallet.walletName,
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
)
SpacerWMax()
if (selectedWallet == wallet) {
Icon(
painter = painterResource(id = R.drawable.ic_check_24),
contentDescription = null,
tint = TangemTheme.colors.icon.accent,
)
}
}
}
@Preview
@Composable
private fun Preview_ChooseWalletScreen_Light() {
TangemTheme(isDark = false) {
ChooseWalletScreen(
state = ChooseWalletStatePreviewData.state,
)
}
}
@Preview
@Composable
private fun Preview_ChooseWalletScreen_Dark() {
TangemTheme(isDark = false) {
ChooseWalletScreen(
state = ChooseWalletStatePreviewData.state,
)
}
}

View file

@ -0,0 +1,157 @@
package com.tangem.managetokens.presentation.common.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
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 com.tangem.blockchain.common.Blockchain
import com.tangem.core.ui.components.SpacerW
import com.tangem.core.ui.components.TangemSwitch
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.managetokens.impl.R
import com.tangem.managetokens.presentation.common.state.NetworkItemState
import com.tangem.managetokens.presentation.managetokens.state.TokenItemState
import com.tangem.managetokens.presentation.managetokens.state.previewdata.TokenItemStatePreviewData
@Composable
internal fun NetworkItem(state: NetworkItemState, tokenState: TokenItemState.Loaded?, modifier: Modifier = Modifier) {
Row(
modifier = modifier
.background(TangemTheme.colors.background.action)
.defaultMinSize(minHeight = TangemTheme.dimens.size68)
.padding(horizontal = TangemTheme.dimens.spacing16)
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
NetworkIcon(model = state)
SpacerW(width = TangemTheme.dimens.spacing12)
Text(
text = state.name,
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography.subtitle2,
)
SpacerW(width = TangemTheme.dimens.spacing6)
Text(
text = state.protocolName,
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.body2,
modifier = Modifier
.weight(1f),
)
if (state is NetworkItemState.Toggleable) {
TangemSwitch(
onCheckedChange = {
state.onToggleClick(tokenState!!, state)
},
checked = state.isAdded.value,
)
}
}
}
@Composable
internal fun NetworkIcon(model: NetworkItemState, modifier: Modifier = Modifier) {
Box(modifier = modifier.size(size = TangemTheme.dimens.size36)) {
val isAdded = when (model) {
is NetworkItemState.Selectable -> true
is NetworkItemState.Toggleable -> model.isAdded.value
}
if (!isAdded) {
Box(
modifier = Modifier
.size(TangemTheme.dimens.size36)
.clip(CircleShape)
.background(TangemTheme.colors.control.unchecked),
)
}
Icon(
painter = painterResource(id = model.iconRes),
contentDescription = null,
modifier = Modifier.size(size = TangemTheme.dimens.size36),
tint = if (isAdded) Color.Unspecified else TangemTheme.colors.text.tertiary,
)
if (model is NetworkItemState.Toggleable && model.isMainNetwork) {
Box(
modifier = Modifier
.align(Alignment.TopEnd)
.size(TangemTheme.dimens.size10)
.clip(CircleShape)
.background(TangemTheme.colors.stroke.transparency),
contentAlignment = Alignment.Center,
) {
Box(
modifier = Modifier
.size(TangemTheme.dimens.size8)
.clip(CircleShape)
.background(TangemTheme.colors.icon.accent),
)
}
}
}
}
@Preview
@Composable
private fun Preview_NetworkItem_Light(@PreviewParameter(NetworkItemStateProvider::class) state: NetworkItemState) {
TangemTheme(isDark = false) {
NetworkItem(state, tokenState = TokenItemStatePreviewData.loadedPriceDown as TokenItemState.Loaded)
}
}
@Preview
@Composable
private fun Preview_NetworkItem_Dark(@PreviewParameter(NetworkItemStateProvider::class) state: NetworkItemState) {
TangemTheme(isDark = true) {
NetworkItem(state, tokenState = TokenItemStatePreviewData.loadedPriceDown as TokenItemState.Loaded)
}
}
private class NetworkItemStateProvider : CollectionPreviewParameterProvider<NetworkItemState>(
collection = listOf(
NetworkItemState.Toggleable(
name = "Ethereum",
protocolName = "ETH",
iconResId = mutableStateOf(R.drawable.img_polygon_22),
isMainNetwork = true,
isAdded = mutableStateOf(true),
id = "",
address = "",
onToggleClick = { _, _ -> },
blockchain = Blockchain.Ethereum,
decimals = 0,
),
NetworkItemState.Toggleable(
name = "BNB SMART CHAIN",
protocolName = "BEP20",
iconResId = mutableStateOf(R.drawable.ic_bsc_16),
isMainNetwork = false,
isAdded = mutableStateOf(false),
id = "",
address = "",
onToggleClick = { _, _ -> },
blockchain = Blockchain.BSC,
decimals = 0,
),
NetworkItemState.Selectable(
name = "Ethereum",
protocolName = "ETH",
iconResId = R.drawable.img_polygon_22,
id = "",
onNetworkClick = { },
blockchain = Blockchain.Ethereum,
),
),
)

View file

@ -0,0 +1,70 @@
package com.tangem.managetokens.presentation.common.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.res.TangemTheme
@Composable
fun SimpleSelectionBlock(
title: String,
subtitle: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
roundedCorners: Boolean = true,
) {
Column(
modifier = modifier
.then(
if (roundedCorners) {
Modifier.clip(shape = RoundedCornerShape(TangemTheme.dimens.radius16))
} else {
Modifier
},
)
.background(color = TangemTheme.colors.background.action)
.clickable { onClick() }
.padding(
horizontal = TangemTheme.dimens.spacing20,
vertical = TangemTheme.dimens.spacing16,
)
.fillMaxWidth(),
) {
Text(
text = title,
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography.subtitle1,
)
SpacerH(height = TangemTheme.dimens.spacing4)
Text(
text = subtitle,
color = TangemTheme.colors.text.secondary,
style = TangemTheme.typography.body2,
)
}
}
@Preview
@Composable
private fun Preview_SimpleSelectionBlock_Light() {
TangemTheme(isDark = false) {
SimpleSelectionBlock(title = "Wallet", subtitle = "Family Wallet", onClick = { })
}
}
@Preview
@Composable
private fun Preview_SimpleSelectionBlock_Dark() {
TangemTheme(isDark = true) {
SimpleSelectionBlock(title = "Wallet", subtitle = "Family Wallet", onClick = { })
}
}

View file

@ -0,0 +1,11 @@
package com.tangem.managetokens.presentation.managetokens.state
import com.tangem.managetokens.presentation.common.state.NetworkItemState
import kotlinx.collections.immutable.ImmutableList
internal data class ChooseNetworkState(
val nativeNetworks: ImmutableList<NetworkItemState>,
val nonNativeNetworks: ImmutableList<NetworkItemState>,
val onNonNativeNetworkHintClick: () -> Unit,
val onCloseChooseNetworkScreen: () -> Unit,
)

View file

@ -8,7 +8,8 @@ import com.tangem.features.managetokens.impl.R
data class DerivationNotificationState(
val totalNeeded: Int,
val missingAddressesCount: Int,
val totalWallets: Int,
val walletsToDerive: Int,
val onGenerateClick: () -> Unit,
) {
val config = NotificationConfig(
@ -25,8 +26,8 @@ data class DerivationNotificationState(
onClick = onGenerateClick,
additionalText = pluralReference(
id = R.plurals.manage_tokens_number_of_wallets_android,
count = totalNeeded,
formatArgs = wrappedList(missingAddressesCount, totalNeeded),
count = totalWallets,
formatArgs = wrappedList(walletsToDerive, totalWallets),
),
),
)

View file

@ -1,5 +1,7 @@
package com.tangem.managetokens.presentation.managetokens.state
import androidx.compose.runtime.MutableState
internal sealed class TokenItemState {
abstract val id: String
@ -9,11 +11,13 @@ internal sealed class TokenItemState {
data class Loaded(
override val id: String,
val name: String,
val currencyId: String,
val currencySymbol: String,
val tokenId: String,
val tokenIcon: TokenIconState,
val quotes: QuotesState,
val rate: String?,
val availableAction: TokenButtonType,
val onButtonClick: (String) -> Unit,
val availableAction: MutableState<TokenButtonType>,
val chooseNetworkState: ChooseNetworkState,
val onButtonClick: (Loaded) -> Unit,
) : TokenItemState()
}

View file

@ -0,0 +1,60 @@
package com.tangem.managetokens.presentation.managetokens.state.previewdata
import androidx.compose.runtime.mutableStateOf
import com.tangem.blockchain.common.Blockchain
import com.tangem.features.managetokens.impl.R
import com.tangem.managetokens.presentation.common.state.NetworkItemState
import com.tangem.managetokens.presentation.managetokens.state.ChooseNetworkState
import kotlinx.collections.immutable.toImmutableList
internal object ChooseNetworkStatePreviewData {
val state = ChooseNetworkState(
nativeNetworks = nativeNetworks.toImmutableList(),
nonNativeNetworks = nonNativeNetworks.toImmutableList(),
onNonNativeNetworkHintClick = {},
onCloseChooseNetworkScreen = {},
)
}
internal val nativeNetworks = listOf(
NetworkItemState.Toggleable(
name = "Ethereum",
protocolName = "ETH",
iconResId = mutableStateOf(R.drawable.img_polygon_22),
isMainNetwork = true,
isAdded = mutableStateOf(true),
id = "",
onToggleClick = { _, _ -> },
blockchain = Blockchain.Ethereum,
address = "",
decimals = 0,
),
)
internal val nonNativeNetworks = listOf(
NetworkItemState.Toggleable(
name = "Ethereum",
protocolName = "ETH",
iconResId = mutableStateOf(R.drawable.img_kusama_22),
isMainNetwork = false,
isAdded = mutableStateOf(true),
id = "",
onToggleClick = { _, _ -> },
blockchain = Blockchain.Ethereum,
address = "",
decimals = 0,
),
NetworkItemState.Toggleable(
name = "BNB SMART CHAIN",
protocolName = "BEP20",
iconResId = mutableStateOf(R.drawable.ic_bsc_16),
isMainNetwork = false,
isAdded = mutableStateOf(false),
id = "",
onToggleClick = { _, _ -> },
blockchain = Blockchain.BSC,
address = "",
decimals = 0,
),
)

View file

@ -4,8 +4,9 @@ import com.tangem.managetokens.presentation.managetokens.state.DerivationNotific
object DerivationNotificationStatePreviewData {
val state = DerivationNotificationState(
totalNeeded = 3,
missingAddressesCount = 2,
totalNeeded = 5,
totalWallets = 3,
walletsToDerive = 2,
onGenerateClick = {},
)
}

View file

@ -1,5 +1,6 @@
package com.tangem.managetokens.presentation.managetokens.state.previewdata
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.graphics.Color
import com.tangem.managetokens.presentation.managetokens.state.*
import kotlinx.collections.immutable.persistentListOf
@ -13,7 +14,8 @@ internal object TokenItemStatePreviewData {
get() = TokenItemState.Loaded(
id = "BTC",
name = "Bitcoin Bitcoin Bitcoin Bitcoin Bitcoin Bitcoin Bitcoin Bitcoin",
currencyId = "BTC",
tokenId = "BTC",
currencySymbol = "BTC",
tokenIcon = tokenIconState,
quotes = QuotesState.Content(
priceChange = "0.43%",
@ -21,15 +23,17 @@ internal object TokenItemStatePreviewData {
chartData = persistentListOf(10f, 2f, 5f, 3f, 4f, 8f, 9f, 7f, 4f),
),
rate = "31 285.72$",
availableAction = TokenButtonType.ADD,
availableAction = mutableStateOf(TokenButtonType.ADD),
onButtonClick = {},
chooseNetworkState = ChooseNetworkStatePreviewData.state,
)
val loadedPriceUp: TokenItemState
get() = TokenItemState.Loaded(
id = "BTC",
name = "Bitcoin",
currencyId = "BTC",
tokenId = "BTC",
currencySymbol = "BTC",
tokenIcon = tokenIconState,
quotes = QuotesState.Content(
priceChange = "0.43%",
@ -37,8 +41,9 @@ internal object TokenItemStatePreviewData {
chartData = persistentListOf(1f, 3f, 4f, 8f, 12f, 10f, 8f, 3f, 5f, 7f),
),
rate = "31 285.72$",
availableAction = TokenButtonType.NOT_AVAILABLE,
availableAction = mutableStateOf(TokenButtonType.NOT_AVAILABLE),
onButtonClick = {},
chooseNetworkState = ChooseNetworkStatePreviewData.state,
)
private val tokenIconState: TokenIconState

View file

@ -0,0 +1,205 @@
package com.tangem.managetokens.presentation.managetokens.ui
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.LazyListScope
import androidx.compose.material.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
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 com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.SpacerW
import com.tangem.core.ui.components.WarningCardTitleOnly
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.managetokens.impl.R
import com.tangem.managetokens.presentation.common.state.ChooseWalletState
import com.tangem.managetokens.presentation.common.state.previewdata.ChooseWalletStatePreviewData
import com.tangem.managetokens.presentation.common.ui.components.NetworkItem
import com.tangem.managetokens.presentation.common.ui.components.SimpleSelectionBlock
import com.tangem.managetokens.presentation.managetokens.state.ChooseNetworkState
import com.tangem.managetokens.presentation.managetokens.state.TokenItemState
import com.tangem.managetokens.presentation.managetokens.state.previewdata.TokenItemStatePreviewData
@Composable
internal fun ChooseNetworkScreen(
state: TokenItemState.Loaded,
walletState: ChooseWalletState,
modifier: Modifier = Modifier,
) {
val networkState = state.chooseNetworkState
LazyColumn(
modifier = modifier
.background(TangemTheme.colors.background.tertiary)
.padding(TangemTheme.dimens.spacing16),
) {
item {
Text(
text = stringResource(id = R.string.manage_tokens_network_selector_title),
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography.subtitle1,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth(),
)
}
when (walletState) {
is ChooseWalletState.Choose -> {
item {
SpacerH(height = TangemTheme.dimens.spacing10)
}
item {
SimpleSelectionBlock(
title = stringResource(id = R.string.manage_tokens_network_selector_wallet),
subtitle = walletState.selectedWallet?.walletName ?: "",
onClick = walletState.onChooseWalletClick,
)
}
}
ChooseWalletState.NoSelection -> Unit
is ChooseWalletState.Warning -> {
item {
SpacerH(height = TangemTheme.dimens.spacing10)
}
item {
WarningCardTitleOnly(
title = stringResource(id = R.string.manage_tokens_wallet_support_only_one_network_title),
)
}
}
}
item {
SpacerH(height = TangemTheme.dimens.spacing16)
}
item {
if (networkState.nativeNetworks.isNotEmpty()) {
NativeNetworks(networkState = networkState, tokenState = state)
}
}
if (networkState.nonNativeNetworks.isNotEmpty()) {
item {
NonNativeNetworksHeader(networkState.onNonNativeNetworkHintClick)
}
item {
SpacerH(height = TangemTheme.dimens.spacing8)
}
item {
this@LazyColumn.NonNativeNetworks(networkState = networkState, tokenState = state)
}
}
}
}
@Composable
private fun NativeNetworks(networkState: ChooseNetworkState, tokenState: TokenItemState.Loaded) {
Column {
Text(
text = stringResource(id = R.string.manage_tokens_network_selector_native_title),
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.caption1,
)
SpacerH(height = TangemTheme.dimens.spacing2)
Text(
text = stringResource(id = R.string.manage_tokens_network_selector_native_subtitle),
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.caption2,
)
SpacerH(height = TangemTheme.dimens.spacing8)
networkState.nativeNetworks.forEachIndexed { index, network ->
NetworkItem(
state = network,
tokenState = tokenState,
modifier = Modifier
.roundedShapeItemDecoration(
currentIndex = index,
lastIndex = networkState.nativeNetworks.lastIndex,
addDefaultPadding = false,
),
)
}
SpacerH(height = TangemTheme.dimens.spacing16)
}
}
@Composable
private fun LazyListScope.NonNativeNetworks(networkState: ChooseNetworkState, tokenState: TokenItemState.Loaded) {
items(
count = networkState.nonNativeNetworks.count(),
key = { index -> networkState.nonNativeNetworks[index].id },
) { index ->
NetworkItem(
state = networkState.nonNativeNetworks[index],
tokenState = tokenState,
modifier = Modifier
.roundedShapeItemDecoration(
currentIndex = index,
lastIndex = networkState.nonNativeNetworks.lastIndex,
addDefaultPadding = false,
),
)
}
item {
SpacerH(height = TangemTheme.dimens.spacing16)
}
}
@Composable
private fun NonNativeNetworksHeader(onNonNativeNetworkHintClick: () -> Unit) {
Column {
Row {
Text(
text = stringResource(id = R.string.manage_tokens_network_selector_non_native_title),
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.caption1,
)
SpacerW(width = TangemTheme.dimens.spacing2)
Icon(
painter = painterResource(id = R.drawable.ic_information_24),
tint = TangemTheme.colors.icon.inactive,
contentDescription = null,
modifier = Modifier
.size(TangemTheme.dimens.size16)
.clickable { onNonNativeNetworkHintClick() },
)
}
SpacerH(height = TangemTheme.dimens.spacing2)
Text(
text = stringResource(id = R.string.manage_tokens_network_selector_non_native_subtitle),
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.caption2,
)
}
}
@Preview
@Composable
private fun Preview_ChooseNetworkScreen_Light() {
TangemTheme(isDark = false) {
ChooseNetworkScreen(
state = TokenItemStatePreviewData.loadedPriceDown as TokenItemState.Loaded,
walletState = ChooseWalletStatePreviewData.state,
)
}
}
@Preview
@Composable
private fun Preview_ChooseNetworkScreen_Dark() {
TangemTheme(isDark = true) {
ChooseNetworkScreen(
state = TokenItemStatePreviewData.loadedPriceDown as TokenItemState.Loaded,
walletState = ChooseWalletStatePreviewData.state,
)
}
}

View file

@ -54,7 +54,7 @@ private fun LoadedTokenItem(state: TokenItemState.Loaded, modifier: Modifier = M
modifier = Modifier
.weight(weight = 1f),
) {
TokenName(name = state.name, currencyId = state.currencyId)
TokenName(name = state.name, currencyId = state.currencySymbol)
TokenPriceData(price = state.rate, quotesState = state.quotes)
}
SpacerW24()
@ -67,8 +67,8 @@ private fun LoadedTokenItem(state: TokenItemState.Loaded, modifier: Modifier = M
}
TokenButton(
type = state.availableAction,
onClick = { state.onButtonClick(state.currencyId) },
type = state.availableAction.value,
onClick = { state.onButtonClick(state) },
)
}
}