Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-13 20:22:15 +05:00
parent a31b747b71
commit 3cc83d3666
20 changed files with 184 additions and 68 deletions

View file

@ -217,6 +217,7 @@
<string name="common_add">Hinzufügen</string>
<string name="common_add_to_portfolio">Zum Portfolio hinzufügen</string>
<string name="common_add_token">Token hinzufügen</string>
<string name="common_add_tokens">Token hinzufügen</string>
<string name="common_added">Hinzugefügt</string>
<string name="common_address">Vertragsadresse</string>
<string name="common_all">Alle</string>
@ -1107,9 +1108,9 @@
<item quantity="one">für %d Wallet</item>
<item quantity="other">für %d Wallets</item>
</plurals>
<string name="referral_point_currencies_description">Du bekommst ^^%1$s^^ für jede von einem Freund gekaufte Wallet auf deine %2$s Netzwerkadresse %3$s ^^30 Tage nach^^ dem</string>
<string name="referral_point_currencies_title">Du</string>
<string name="referral_point_discount_description_prefix">Bekommt eine</string>
<string name="referral_point_currencies_description">Du erhältst ^^%1$s^^ für jede Wallet, die ein Freund kauft.\nDie Auszahlung erfolgt ^^30 Tage^^ nach dem Kauf an deine%2$s Adresse.%3$s</string>
<string name="referral_point_currencies_title">Du erhältst</string>
<string name="referral_point_discount_description_prefix">Er erhält</string>
<string name="referral_point_discount_description_suffix">beim Kauf einer Wallet auf tangem.com</string>
<string name="referral_point_discount_description_value">%s Rabatt</string>
<string name="referral_point_discount_title">Dein Freund</string>

View file

@ -217,6 +217,7 @@
<string name="common_add">Agregar</string>
<string name="common_add_to_portfolio">Añadir al portafolio</string>
<string name="common_add_token">Agregar token</string>
<string name="common_add_tokens">Añada tokens</string>
<string name="common_added">Agregado</string>
<string name="common_address">Dirección</string>
<string name="common_all">Todos</string>

View file

@ -217,6 +217,7 @@
<string name="common_add">Ajouter</string>
<string name="common_add_to_portfolio">Ajouter au portfolio</string>
<string name="common_add_token">Ajouter un jeton</string>
<string name="common_add_tokens">Ajouter des jetons</string>
<string name="common_added">Ajouté</string>
<string name="common_address">Adresse</string>
<string name="common_all">Tous</string>

View file

@ -214,6 +214,7 @@
<string name="common_add">追加</string>
<string name="common_add_to_portfolio">ポートフォリオに追加</string>
<string name="common_add_token">トークンを追加</string>
<string name="common_add_tokens">トークンの追加</string>
<string name="common_added">追加済み</string>
<string name="common_address">アドレス</string>
<string name="common_all">すべて</string>

View file

@ -223,6 +223,7 @@
<string name="common_add">Добавить</string>
<string name="common_add_to_portfolio">Добавить в портфель</string>
<string name="common_add_token">Добавить токен</string>
<string name="common_add_tokens">Добавьте токены</string>
<string name="common_added">Добавлен</string>
<string name="common_address">Адрес</string>
<string name="common_all">Все</string>
@ -408,6 +409,7 @@
<string name="common_understand">Я понял</string>
<string name="common_understand_continue">Я понимаю, продолжить</string>
<string name="common_unknown_error">Произошла ошибка. Пожалуйста, попробуйте снова.</string>
<string name="common_unlock">Разблокировать</string>
<string name="common_unreachable">Недоступно</string>
<string name="common_unstake">Завершить стейкинг</string>
<string name="common_utxo_validate_withdrawal_message_warning">Из-за ограничений %1$s в одну транзакцию может поместиться только %2$d UTXO. Это означает, что вы можете отправить только %3$s или меньше. Вам нужно уменьшить сумму.</string>

View file

@ -223,6 +223,7 @@
<string name="common_add">Додати</string>
<string name="common_add_to_portfolio">Додати у портфель</string>
<string name="common_add_token">Додати токен</string>
<string name="common_add_tokens">Додайте токени</string>
<string name="common_added">Додано</string>
<string name="common_address">Адреса</string>
<string name="common_all">Усе</string>

View file

@ -217,6 +217,7 @@
<string name="common_add">Add</string>
<string name="common_add_to_portfolio">Add to portfolio</string>
<string name="common_add_token">Add token</string>
<string name="common_add_tokens">Add tokens</string>
<string name="common_added">Added</string>
<string name="common_address">Address</string>
<string name="common_all">All</string>
@ -383,6 +384,7 @@
<string name="common_to">To</string>
<string name="common_to_wallet_name">To %s</string>
<string name="common_today">Today</string>
<string name="common_token_send">Token to send</string>
<plurals name="common_tokens_count">
<item quantity="one">%d token</item>
<item quantity="other">%d tokens</item>
@ -395,6 +397,7 @@
<string name="common_understand">I understand</string>
<string name="common_understand_continue">I understand, continue</string>
<string name="common_unknown_error">There was an error. Please try again.</string>
<string name="common_unlock">Unlock</string>
<string name="common_unreachable">Unreachable</string>
<string name="common_unstake">Unstake</string>
<string name="common_utxo_validate_withdrawal_message_warning">Due to %1$s limitations only %2$d UTXOs can fit in a single transaction. This means you can only send %3$s or less. You need to reduce the amount.</string>
@ -1207,6 +1210,10 @@
<string name="send_memo">Memo: %s</string>
<string name="send_memo_destination_tag_error">Invalid Memo</string>
<string name="send_network_fee_warning_title">Network fee coverage</string>
<plurals name="send_network_selection_hidden_tokens">
<item quantity="one">%d token isn\'t compatible with this address</item>
<item quantity="other">%d tokens aren\'t compatible with this address</item>
</plurals>
<string name="send_nonce">Nonce</string>
<string name="send_nonce_footer">Unique number for each transaction. Use it to resend or cancel a pending transaction.</string>
<string name="send_nonce_hint">Enter nonce…</string>

View file

@ -68,7 +68,7 @@ internal fun TangemButtonInternal(
modifier = modifier
.testTag(BaseButtonTestTags.BUTTON)
.clickableSingle(enabled = isEnabled, onClick = onClick, role = Role.Button)
.height(size.toHeightDp())
.heightIn(min = size.toHeightDp())
.conditionalCompose(text == null) {
width(size.toHeightDp())
}

View file

@ -2,6 +2,8 @@ package com.tangem.feature.wallet.presentation.common.preview
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.ds.button.TangemButtonShape
import com.tangem.core.ui.ds.button.TangemButtonSize
import com.tangem.core.ui.ds.button.TangemButtonType
import com.tangem.core.ui.ds.button.TangemButtonUM
import com.tangem.core.ui.ds.image.TangemIconUM
@ -50,14 +52,19 @@ internal object WalletScreenPreviewData {
text = resourceReference(R.string.organize_tokens_title),
type = TangemButtonType.Secondary,
onClick = {},
iconRes = R.drawable.ic_filter_default_24,
size = TangemButtonSize.X9,
shape = TangemButtonShape.Rounded,
),
)
private val walletLocked = WalletUM.Locked(
walletsBalanceUM = WalletBalancePreview.content,
buttons = WalletPreviewData.actionButtons,
walletsBalanceUM = WalletBalancePreview.empty,
buttons = WalletPreviewData.disabledActionButtons,
type = WalletType.Cold,
notifications = persistentListOf(),
notifications = persistentListOf(
WalletNotificationUM.UnlockWallets({}),
),
)
private val walletDefault = WalletUM.Content(
@ -82,13 +89,29 @@ internal object WalletScreenPreviewData {
tangemPayState = TangemPayState.Loading,
)
internal val defaultState = WalletScreenState(
private val walletEmpty = WalletUM.Content(
walletsBalanceUM = WalletBalancePreview.empty,
buttons = WalletPreviewData.disabledActionButtons,
type = WalletType.Cold,
pullToRefreshConfig = PullToRefreshConfig(
isRefreshing = false,
onRefresh = {},
),
notifications = persistentListOf(),
notificationsCarousel = persistentListOf(),
tokensListUM = WalletTokensListUM.Empty,
nftState = WalletNFTItemUM.Hidden,
tangemPayState = TangemPayState.Empty,
)
val defaultState = WalletScreenState(
topBarConfig = topBarConfig,
selectedWalletIndex = 0,
wallets = persistentListOf(),
wallets2 = persistentListOf(
walletLocked,
walletDefault,
walletEmpty,
walletLocked,
),
onWalletChange = { _, _ -> },
event = consumedEvent(),
@ -96,4 +119,12 @@ internal object WalletScreenPreviewData {
showMarketsOnboarding = false,
onDismissMarketsTooltip = {},
)
val emptyState = defaultState.copy(
selectedWalletIndex = 1,
)
val lockedState = defaultState.copy(
selectedWalletIndex = 2,
)
}

View file

@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.preview
import androidx.compose.ui.text.SpanStyle
import com.tangem.core.ui.ds.image.DeviceIconUM
import com.tangem.core.ui.extensions.combinedReference
import com.tangem.core.ui.extensions.orMaskWithStars
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.styledStringReference
import com.tangem.core.ui.res.TangemTheme
@ -37,6 +38,8 @@ internal object WalletBalancePreview {
isZeroBalance = false,
)
val hiddenBalanceContent = content.copy(balance = content.balance.orMaskWithStars(true))
val loading: WalletBalanceUM.Loading = WalletBalanceUM.Loading(
id = UserWalletId("1"),
name = "My Wallet",
@ -48,4 +51,10 @@ internal object WalletBalancePreview {
name = "My Wallet",
deviceIcon = DeviceIconUM.Stub(cardsCount = 3),
)
val empty: WalletBalanceUM.Empty = WalletBalanceUM.Empty(
id = UserWalletId("2"),
name = "My Wallet",
deviceIcon = DeviceIconUM.Stub(cardsCount = 3),
)
}

View file

@ -15,6 +15,12 @@ internal object WalletPreviewData {
}
val actionButtons = persistentListOf(
WalletActionButtons.Buy({}, true).buttonUM,
WalletActionButtons.Swap({}, true).buttonUM,
WalletActionButtons.Sell({}, true).buttonUM,
)
val disabledActionButtons = persistentListOf(
WalletActionButtons.Buy({}, false).buttonUM,
WalletActionButtons.Swap({}, false).buttonUM,
WalletActionButtons.Sell({}, false).buttonUM,

View file

@ -69,11 +69,24 @@ internal sealed interface WalletBalanceUM {
override val deviceIcon: DeviceIconUM,
) : WalletBalanceUM
/**
* Wallet card loading state
*
* @property id wallet id
* @property name wallet name
*/
data class Empty(
override val id: UserWalletId,
override val name: String,
override val deviceIcon: DeviceIconUM,
) : WalletBalanceUM
fun copySealed(name: String): WalletBalanceUM {
return when (this) {
is Content -> copy(name = name)
is Error -> copy(name = name)
is Loading -> copy(name = name)
is Empty -> copy(name = name)
}
}
}

View file

@ -343,7 +343,13 @@ internal sealed class WalletNotificationUM(val messageUM: TangemMessageUM, val t
resourceReference(R.string.common_biometrics),
),
),
onClick = onClick,
buttonsUM = persistentListOf(
TangemMessageButtonUM(
text = resourceReference(R.string.common_unlock),
onClick = onClick,
type = TangemButtonType.Primary,
),
),
messageEffect = TangemMessageEffect.Card,
isCentered = true,
),

View file

@ -25,6 +25,24 @@ internal sealed class WalletTokensListUM {
override val organizeButtonUM: TangemButtonUM? = null
}
data object Locked : WalletTokensListUM() {
override val tokenList: ImmutableList<TokensListItemUM2.Portfolio> = persistentListOf(
TokensListItemUM2.Portfolio(
tokenRowUM = TangemTokenRowUM.Empty(id = "0"),
tokenList = persistentListOf(),
isExpanded = false,
isCollapsable = true,
),
TokensListItemUM2.Portfolio(
tokenRowUM = TangemTokenRowUM.Empty(id = "1"),
tokenList = persistentListOf(),
isExpanded = false,
isCollapsable = true,
),
)
override val organizeButtonUM: TangemButtonUM? = null
}
data object Loading : WalletTokensListUM() {
override val tokenList: ImmutableList<TokensListItemUM2> = persistentListOf(
TokensListItemUM2.Portfolio(

View file

@ -45,7 +45,7 @@ internal sealed interface WalletUM {
) : WalletUM {
override val notificationsCarousel: ImmutableList<WalletNotificationUM> = persistentListOf()
override val pullToRefreshConfig = PullToRefreshConfig(false, {})
override val tokensListUM: WalletTokensListUM = WalletTokensListUM.Empty // todo redesign main locked state
override val tokensListUM: WalletTokensListUM = WalletTokensListUM.Locked
override val nftState: WalletNFTItemUM = WalletNFTItemUM.Hidden
override val tangemPayState: TangemPayState = TangemPayState.Empty
}

View file

@ -1,6 +1,5 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.core.ui.R as CoreUiR
import com.tangem.common.ui.userwallet.converter.WalletIconUMConverter
import com.tangem.core.ui.ds.button.TangemButtonUM
import com.tangem.core.ui.ds.topbar.TangemTopBarActionUM
@ -20,6 +19,7 @@ import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toPersistentList
import com.tangem.core.ui.R as CoreUiR
internal class InitializeWalletsTransformer(
private val selectedWalletIndex: Int,
@ -128,7 +128,7 @@ internal class InitializeWalletsTransformer(
private fun UserWallet.toLockedWalletUM(): WalletUM.Locked {
return WalletUM.Locked(
walletsBalanceUM = WalletBalanceUM.Loading(
walletsBalanceUM = WalletBalanceUM.Empty(
id = walletId,
name = name,
deviceIcon = getWalletIconUseCase.invoke(userWallet = this)

View file

@ -71,7 +71,7 @@ internal class WalletTokensListUMConverter(
override fun convert(value: AccountStatusList): WalletTokensListUM {
val promoCryptoCurrency = yieldSupplyPromoBannerConverter.convert2(value = value)
return if (value.accountStatuses.isEmpty()) {
return if (value.flattenCurrencies().isEmpty()) {
WalletTokensListUM.Empty
} else {
val isCollapsable = value.accountStatuses.count {
@ -163,7 +163,7 @@ internal class WalletTokensListUMConverter(
return if (accountList.flattenCurrencies().size > 1 && !selectedWallet.isSingleWalletWithToken()) {
TangemButtonUM(
text = resourceReference(R.string.organize_tokens_title),
isEnabled = accountList.totalFiatBalance !is TotalFiatBalance.Loading,
isEnabled = accountList.totalFiatBalance is TotalFiatBalance.Loading,
size = TangemButtonSize.X9,
shape = TangemButtonShape.Rounded,
type = TangemButtonType.PrimaryInverse,

View file

@ -575,7 +575,8 @@ private class WalletScreen2PreviewProvider : PreviewParameterProvider<WalletScre
override val values: Sequence<WalletScreenState>
get() = sequenceOf(
WalletScreenPreviewData.defaultState,
WalletScreenPreviewData.defaultState.copy(selectedWalletIndex = 1),
WalletScreenPreviewData.emptyState,
WalletScreenPreviewData.lockedState,
)
}
// endregion

View file

@ -29,6 +29,7 @@ import com.tangem.core.ui.ds.button.SecondaryTangemButton
import com.tangem.core.ui.ds.button.TangemButtonShape
import com.tangem.core.ui.ds.button.TangemButtonUM
import com.tangem.core.ui.ds.image.TangemDeviceIcon
import com.tangem.core.ui.ds.placeholder.TextPlaceholder
import com.tangem.core.ui.ds.topbar.collapsing.TangemCollapsingAppBarBehavior
import com.tangem.core.ui.ds.topbar.collapsing.rememberTangemExitUntilCollapsedScrollBehavior
import com.tangem.core.ui.ds.topbar.collapsing.snapToExitUntilCollapsed
@ -113,43 +114,40 @@ private fun Balance(walletBalanceUM: WalletBalanceUM, isBalanceHidden: Boolean,
},
) { balanceUM ->
when (balanceUM) {
is WalletBalanceUM.Content -> {
Text(
text = balanceUM.balance.orMaskWithStars(isBalanceHidden).resolveAnnotatedReference(),
style = TangemTheme.typography2.titleRegular44.applyBladeBrush(
isEnabled = balanceUM.isBalanceFlickering,
textColor = TangemTheme.colors2.text.neutral.primary,
),
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
autoSize = TextAutoSize.StepBased(
minFontSize = TangemTheme.typography2.bodySemibold15.fontSize,
maxFontSize = TangemTheme.typography2.titleRegular44.fontSize,
),
)
}
is WalletBalanceUM.Error -> {
Text(
text = StringsSigns.DASH_SIGN,
style = TangemTheme.typography2.titleRegular44,
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
autoSize = TextAutoSize.StepBased(
minFontSize = TangemTheme.typography2.bodySemibold15.fontSize,
maxFontSize = TangemTheme.typography2.titleRegular44.fontSize,
),
)
}
is WalletBalanceUM.Loading,
-> {
TextShimmer(
text = "123456",
style = TangemTheme.typography2.titleRegular44,
radius = TangemTheme.dimens2.x25,
)
}
is WalletBalanceUM.Content -> Text(
text = balanceUM.balance.orMaskWithStars(isBalanceHidden).resolveAnnotatedReference(),
style = TangemTheme.typography2.titleRegular44.applyBladeBrush(
isEnabled = balanceUM.isBalanceFlickering,
textColor = TangemTheme.colors2.text.neutral.primary,
),
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
autoSize = TextAutoSize.StepBased(
minFontSize = TangemTheme.typography2.bodySemibold15.fontSize,
maxFontSize = TangemTheme.typography2.titleRegular44.fontSize,
),
)
is WalletBalanceUM.Error -> Text(
text = StringsSigns.DASH_SIGN,
style = TangemTheme.typography2.titleRegular44,
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
autoSize = TextAutoSize.StepBased(
minFontSize = TangemTheme.typography2.bodySemibold15.fontSize,
maxFontSize = TangemTheme.typography2.titleRegular44.fontSize,
),
)
is WalletBalanceUM.Loading -> TextShimmer(
text = "123456",
style = TangemTheme.typography2.titleRegular44,
radius = TangemTheme.dimens2.x25,
)
is WalletBalanceUM.Empty -> TextPlaceholder(
textStyle = TangemTheme.typography2.titleRegular44,
width = 200.dp,
)
}
}
}
@ -162,6 +160,11 @@ private fun ActionButtons(buttons: ImmutableList<TangemButtonUM>) {
) {
buttons.fastForEach { button ->
key(button.text) {
val textColor = if (button.isEnabled) {
TangemTheme.colors2.text.neutral.primary
} else {
TangemTheme.colors2.text.status.disabled
}
Column(
modifier = Modifier.padding(horizontal = TangemTheme.dimens2.x2_5),
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
@ -170,12 +173,13 @@ private fun ActionButtons(buttons: ImmutableList<TangemButtonUM>) {
SecondaryTangemButton(
iconRes = button.iconRes,
onClick = button.onClick,
isEnabled = button.isEnabled,
shape = TangemButtonShape.Rounded,
)
Text(
text = button.text.orEmpty().resolveReference(),
style = TangemTheme.typography2.bodySemibold15,
color = TangemTheme.colors2.text.neutral.primary,
color = textColor,
)
}
}
@ -187,25 +191,34 @@ private fun ActionButtons(buttons: ImmutableList<TangemButtonUM>) {
@Composable
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun WalletBalance_Preview(@PreviewParameter(WalletBalancePreviewProvider::class) params: WalletBalanceUM) {
private fun WalletBalance_Preview(
@PreviewParameter(WalletBalancePreviewProvider::class)
params: WalletBalancePreviewData,
) {
TangemThemePreviewRedesign {
WalletBalance(
walletBalanceUM = params,
walletBalanceUM = params.walletBalanceUM,
behavior = rememberTangemExitUntilCollapsedScrollBehavior(),
buttons = WalletPreviewData.actionButtons,
buttons = params.actionsList,
isBalanceHidden = false,
modifier = Modifier.background(TangemTheme.colors2.surface.level1),
)
}
}
private class WalletBalancePreviewProvider : PreviewParameterProvider<WalletBalanceUM> {
override val values: Sequence<WalletBalanceUM>
private data class WalletBalancePreviewData(
val walletBalanceUM: WalletBalanceUM,
val actionsList: ImmutableList<TangemButtonUM>,
)
private class WalletBalancePreviewProvider : PreviewParameterProvider<WalletBalancePreviewData> {
override val values: Sequence<WalletBalancePreviewData>
get() = sequenceOf(
WalletBalancePreview.content,
WalletBalancePreview.content.copy(isBalanceFlickering = true),
WalletBalancePreview.loading,
WalletBalancePreview.error,
WalletBalancePreviewData(WalletBalancePreview.content, WalletPreviewData.actionButtons),
WalletBalancePreviewData(WalletBalancePreview.hiddenBalanceContent, WalletPreviewData.actionButtons),
WalletBalancePreviewData(WalletBalancePreview.loading, WalletPreviewData.disabledActionButtons),
WalletBalancePreviewData(WalletBalancePreview.error, WalletPreviewData.disabledActionButtons),
WalletBalancePreviewData(WalletBalancePreview.empty, WalletPreviewData.disabledActionButtons),
)
}
// endregion

View file

@ -98,6 +98,7 @@ internal fun LazyListScope.tokensListItems2(
when (walletTokensListUM) {
is WalletTokensListUM.Loading,
is WalletTokensListUM.Content,
WalletTokensListUM.Locked,
-> {
walletTokensListUM.tokenList.fastForEachIndexed { index, listItem ->
when (listItem) {
@ -337,11 +338,15 @@ internal fun PortfolioRowItem(
TangemIcon(
tangemIconUM = sizedHeadIcon,
modifier = modifier.sharedBounds(
sharedContentState = iconSharedContentState,
animatedVisibilityScope = animatedContentScope,
boundsTransform = boundsTransform,
),
modifier = modifier
.conditionalCompose(headIcon is TangemIconUM.Empty) {
size(TangemTheme.dimens2.x9)
}
.sharedBounds(
sharedContentState = iconSharedContentState,
animatedVisibilityScope = animatedContentScope,
boundsTransform = boundsTransform,
),
)
},
title = { modifier ->