From d3138c662e08f78a3e88d37ffa28b2a16ba29ffb Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 22 Oct 2025 16:48:40 +0700 Subject: [PATCH] Updated on 2026-08-14 --- ...ccountCryptoPortfolioItemStateConverter.kt | 28 ++++++- .../tangem/common/ui/account/AccountNameUM.kt | 2 +- .../common/ui/account/PortfolioSelectRow.kt | 2 + .../core/ui/components/token/TokenItem.kt | 6 +- .../token/internal/TokenCryptoAmount.kt | 12 ++- .../components/token/internal/TokenPrice.kt | 18 ++-- .../components/token/state/TokenItemState.kt | 6 ++ .../createedit/AccountCreateEditModel.kt | 9 +- .../account/details/AccountDetailsModel.kt | 32 ++++--- .../referral/DefaultReferralComponent.kt | 3 +- .../feature/referral/model/ReferralModel.kt | 7 +- .../referral/ui/ParticipateBottomBlock.kt | 83 ++++++++++++++++--- .../feature/referral/ui/ReferralScreen.kt | 70 +++++++++------- .../common/preview/WalletScreenPreviewData.kt | 30 +++++++ .../converter/TokenListStateConverter.kt | 1 + .../presentation/wallet/ui/WalletScreen.kt | 2 + .../MultiCurrencyAccountContent.kt | 39 +++++++-- .../multicurrency/MultiCurrencyContent.kt | 46 +++++----- 18 files changed, 288 insertions(+), 108 deletions(-) diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/AccountCryptoPortfolioItemStateConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/account/AccountCryptoPortfolioItemStateConverter.kt index 6baef4fa51..099ff971f8 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/account/AccountCryptoPortfolioItemStateConverter.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/account/AccountCryptoPortfolioItemStateConverter.kt @@ -1,6 +1,8 @@ package com.tangem.common.ui.account import com.tangem.common.ui.R +import com.tangem.core.ui.components.marketprice.PriceChangeType +import com.tangem.core.ui.components.marketprice.utils.PriceChangeConverter import com.tangem.core.ui.components.token.state.TokenItemState import com.tangem.core.ui.components.token.state.TokenItemState.FiatAmountState import com.tangem.core.ui.components.token.state.TokenItemState.Subtitle2State @@ -8,15 +10,21 @@ import com.tangem.core.ui.extensions.pluralReference import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format +import com.tangem.core.ui.format.bigdecimal.percent import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.core.lce.Lce import com.tangem.domain.models.StatusSource import com.tangem.domain.models.TotalFiatBalance import com.tangem.domain.models.account.Account +import com.tangem.domain.models.quote.PriceChange import com.tangem.utils.converter.Converter +import com.tangem.utils.extensions.isZero +import java.math.BigDecimal class AccountCryptoPortfolioItemStateConverter( private val appCurrency: AppCurrency, private val account: Account.CryptoPortfolio, + private val priceChangeLce: Lce? = null, private val onItemClick: ((Account.CryptoPortfolio) -> Unit)? = null, private val onItemLongClick: ((Account.CryptoPortfolio) -> Unit)? = null, ) : Converter { @@ -32,6 +40,14 @@ class AccountCryptoPortfolioItemStateConverter( private fun Account.CryptoPortfolio.mapToContentState( fiatBalance: TotalFiatBalance.Loaded, ): TokenItemState.Content { + val subtitle2State = when (fiatBalance.amount.isZero()) { + true -> null + false -> priceChangeLce?.fold( + ifLoading = { priceChange -> priceChange?.toSubtitle2State() ?: Subtitle2State.Loading }, + ifError = { null }, + ifContent = { priceChange -> priceChange.toSubtitle2State() }, + ) + } return TokenItemState.Content( id = account.accountId.value, iconState = AccountIconItemStateConverter.convert(this), @@ -51,7 +67,7 @@ class AccountCryptoPortfolioItemStateConverter( .format { fiat(fiatCurrencyCode = appCurrency.code, fiatCurrencySymbol = appCurrency.symbol) }, isFlickering = fiatBalance.source == StatusSource.CACHE, ), - subtitle2State = null, + subtitle2State = subtitle2State, onItemClick = onItemClick?.let { onItemClick -> { onItemClick(account) } }, onItemLongClick = onItemLongClick?.let { onItemLongClick -> { onItemLongClick(account) } }, ) @@ -102,4 +118,14 @@ class AccountCryptoPortfolioItemStateConverter( }, ) } + + private fun BigDecimal.getPriceChangeType(): PriceChangeType = PriceChangeConverter.fromBigDecimal(value = this) + + private fun StatusSource.isFlickering(): Boolean = this == StatusSource.CACHE + + private fun PriceChange.toSubtitle2State(): Subtitle2State = Subtitle2State.PriceChangeContent( + priceChangePercent = this.value.format { percent() }, + type = this.value.getPriceChangeType(), + isFlickering = this.source.isFlickering(), + ) } \ No newline at end of file diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/AccountNameUM.kt b/common/ui/src/main/java/com/tangem/common/ui/account/AccountNameUM.kt index e86dda636b..39ba4af467 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/account/AccountNameUM.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/account/AccountNameUM.kt @@ -33,7 +33,7 @@ sealed interface AccountNameUM { * * @property raw the raw string value of the custom account name */ - class Custom(internal val raw: String) : AccountNameUM { + data class Custom(internal val raw: String) : AccountNameUM { override val value: TextReference = stringReference(value = raw) } diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/PortfolioSelectRow.kt b/common/ui/src/main/java/com/tangem/common/ui/account/PortfolioSelectRow.kt index 676a394468..91b1895006 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/account/PortfolioSelectRow.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/account/PortfolioSelectRow.kt @@ -41,6 +41,8 @@ fun PortfolioSelectRow(state: PortfolioSelectUM, modifier: Modifier = Modifier) Text( modifier = Modifier.weight(1f), text = stringResourceSafe(leftText), + maxLines = 1, + overflow = TextOverflow.Ellipsis, style = TangemTheme.typography.body1, color = TangemTheme.colors.text.primary1, ) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/token/TokenItem.kt b/core/ui/src/main/java/com/tangem/core/ui/components/token/TokenItem.kt index 68212fff41..374b6de2e3 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/token/TokenItem.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/token/TokenItem.kt @@ -27,6 +27,7 @@ import com.tangem.core.ui.components.marketprice.PriceChangeType import com.tangem.core.ui.components.token.internal.* import com.tangem.core.ui.components.token.state.TokenItemState import com.tangem.core.ui.components.token.state.TokenItemState.FiatAmountState +import com.tangem.core.ui.components.token.state.TokenItemState.Subtitle2State import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.rememberHapticFeedback import com.tangem.core.ui.extensions.stringReference @@ -691,7 +692,10 @@ object AccountItemPreviewData { value = stringReference("24 tokens"), isAvailable = false, ), - subtitle2State = null, + subtitle2State = Subtitle2State.PriceChangeContent( + priceChangePercent = "0,43 %", + type = PriceChangeType.UP, + ), onItemClick = {}, onItemLongClick = {}, ) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/token/internal/TokenCryptoAmount.kt b/core/ui/src/main/java/com/tangem/core/ui/components/token/internal/TokenCryptoAmount.kt index 58524bee7e..03beb520e2 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/token/internal/TokenCryptoAmount.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/token/internal/TokenCryptoAmount.kt @@ -11,7 +11,6 @@ import com.tangem.core.ui.R import com.tangem.core.ui.components.RectangleShimmer import com.tangem.core.ui.components.audits.AuditLabel import com.tangem.core.ui.components.text.applyBladeBrush -import com.tangem.core.ui.components.token.state.TokenItemState import com.tangem.core.ui.extensions.orMaskWithStars import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme @@ -31,7 +30,7 @@ internal fun TokenCryptoAmount( isFlickering = state.isFlickering, ) } - is TokenItemState.Subtitle2State.LabelContent -> { + is TokenCryptoAmountState.LabelContent -> { AuditLabel(state = state.auditLabelUM, modifier = modifier) } is TokenCryptoAmountState.Unreachable -> { @@ -46,6 +45,15 @@ internal fun TokenCryptoAmount( is TokenCryptoAmountState.Locked -> { LockedRectangle(modifier = modifier.placeholderSize()) } + is TokenCryptoAmountState.PriceChangeContent -> { + PriceBlock( + modifier = modifier, + price = null, + type = state.type, + priceChangePercent = state.priceChangePercent, + isFlickering = state.isFlickering, + ) + } null -> Unit } } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/token/internal/TokenPrice.kt b/core/ui/src/main/java/com/tangem/core/ui/components/token/internal/TokenPrice.kt index b2ee3f5568..6b5d2315d8 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/token/internal/TokenPrice.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/token/internal/TokenPrice.kt @@ -64,8 +64,8 @@ internal fun TokenPrice(state: TokenPriceState?, modifier: Modifier = Modifier) } @Composable -private fun PriceBlock( - price: String, +internal fun PriceBlock( + price: String?, isFlickering: Boolean, modifier: Modifier = Modifier, type: PriceChangeType? = null, @@ -75,13 +75,15 @@ private fun PriceBlock( modifier = modifier, verticalAlignment = Alignment.CenterVertically, ) { - PriceText( - modifier = Modifier.weight(weight = 1f, fill = false), - text = price, - isFlickering = isFlickering, - ) + if (price != null) { + PriceText( + modifier = Modifier.weight(weight = 1f, fill = false), + text = price, + isFlickering = isFlickering, + ) - SpacerW6() + SpacerW6() + } if (type != null) { PriceChangeIcon( diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/token/state/TokenItemState.kt b/core/ui/src/main/java/com/tangem/core/ui/components/token/state/TokenItemState.kt index 88fc97f796..1e23a9359e 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/token/state/TokenItemState.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/token/state/TokenItemState.kt @@ -230,6 +230,12 @@ sealed class TokenItemState { val isFlickering: Boolean = false, ) : Subtitle2State() + data class PriceChangeContent( + val priceChangePercent: String, + val type: PriceChangeType, + val isFlickering: Boolean = false, + ) : Subtitle2State() + data class LabelContent(val auditLabelUM: AuditLabelUM) : Subtitle2State() data object Unreachable : Subtitle2State() diff --git a/features/account/impl/src/main/java/com/tangem/features/account/createedit/AccountCreateEditModel.kt b/features/account/impl/src/main/java/com/tangem/features/account/createedit/AccountCreateEditModel.kt index 85d4b0d97f..bc827c729f 100644 --- a/features/account/impl/src/main/java/com/tangem/features/account/createedit/AccountCreateEditModel.kt +++ b/features/account/impl/src/main/java/com/tangem/features/account/createedit/AccountCreateEditModel.kt @@ -205,8 +205,8 @@ internal class AccountCreateEditModel @Inject constructor( is AccountCreateEditComponent.Params.Edit -> { val oldName = params.account.accountName.toUM() - val isNewName = this.account.name != oldName - val isNewIcon = this.account.portfolioIcon != params.account.portfolioIcon + val isNewName = this.account.name.trim() != oldName + val isNewIcon = this.account.portfolioIcon != params.account.portfolioIcon.toUM() isValidName && (isNewName || isNewIcon) } } @@ -275,4 +275,9 @@ internal class AccountCreateEditModel @Inject constructor( ) messageSender.send(dialogMessage) } +} + +private fun AccountNameUM.trim(): AccountNameUM = when (this) { + is AccountNameUM.Custom -> this.toDomain().getOrNull()?.toUM() ?: this + AccountNameUM.DefaultMain -> this } \ No newline at end of file diff --git a/features/account/impl/src/main/java/com/tangem/features/account/details/AccountDetailsModel.kt b/features/account/impl/src/main/java/com/tangem/features/account/details/AccountDetailsModel.kt index 9c0e965b55..1dbce4ca26 100644 --- a/features/account/impl/src/main/java/com/tangem/features/account/details/AccountDetailsModel.kt +++ b/features/account/impl/src/main/java/com/tangem/features/account/details/AccountDetailsModel.kt @@ -12,7 +12,8 @@ import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.message.DialogMessage import com.tangem.core.ui.message.EventMessageAction import com.tangem.core.ui.message.ToastMessage -import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier +import com.tangem.domain.account.producer.SingleAccountProducer +import com.tangem.domain.account.supplier.SingleAccountSupplier import com.tangem.domain.account.usecase.ArchiveCryptoPortfolioUseCase import com.tangem.domain.models.PortfolioId import com.tangem.domain.models.account.Account @@ -35,7 +36,7 @@ internal class AccountDetailsModel @Inject constructor( private val router: Router, override val dispatchers: CoroutineDispatcherProvider, private val archiveCryptoPortfolioUseCase: ArchiveCryptoPortfolioUseCase, - singleAccountStatusListSupplier: SingleAccountStatusListSupplier, + singleAccountSupplier: SingleAccountSupplier, private val getUserWalletUseCase: GetUserWalletUseCase, ) : Model() { @@ -46,22 +47,19 @@ internal class AccountDetailsModel @Inject constructor( private val accountId = params.account.accountId init { - singleAccountStatusListSupplier(accountId.userWalletId) - .map { it.accountStatuses.find { status -> status.account.accountId == accountId }?.account } - .filterNotNull() - .distinctUntilChanged() - .map { account -> buildUI(account) } + singleAccountSupplier(SingleAccountProducer.Params(accountId)) + .onEach { account -> _uiState.update { buildUI(account) } } .launchIn(modelScope) } - private fun onEditAccountClick() { - router.push(AppRoute.EditAccount(params.account)) + private fun onEditAccountClick(account: Account) { + router.push(AppRoute.EditAccount(account)) } - private fun onManageTokensClick() { + private fun onManageTokensClick(account: Account) { val route = AppRoute.ManageTokens( source = AppRoute.ManageTokens.Source.SETTINGS, - portfolioId = PortfolioId(params.account.accountId), + portfolioId = PortfolioId(account.accountId), ) router.push(route) } @@ -92,7 +90,7 @@ internal class AccountDetailsModel @Inject constructor( private fun archiveCryptoPortfolio() = modelScope.launch { _uiState.update { it.toggleProgress(true) } - archiveCryptoPortfolioUseCase(params.account.accountId) + archiveCryptoPortfolioUseCase(accountId) .onLeft { error -> failedArchiveDialog(error) _uiState.update { it.toggleProgress(false) } @@ -138,14 +136,14 @@ internal class AccountDetailsModel @Inject constructor( ) } } - val isMultiCurrency = getUserWalletUseCase(params.account.accountId.userWalletId) + val isMultiCurrency = getUserWalletUseCase(account.accountId.userWalletId) .getOrNull()?.isMultiCurrency ?: false return AccountDetailsUM( - accountName = params.account.accountName.toUM().value, - accountIcon = params.account.portfolioIcon.toUM(), + accountName = account.accountName.toUM().value, + accountIcon = account.portfolioIcon.toUM(), onCloseClick = { router.pop() }, - onAccountEditClick = ::onEditAccountClick, - onManageTokensClick = ::onManageTokensClick, + onAccountEditClick = { onEditAccountClick(account) }, + onManageTokensClick = { onManageTokensClick(account) }, archiveMode = archiveMode, isManageTokensAvailable = isMultiCurrency, ) diff --git a/features/referral/impl/src/main/java/com/tangem/feature/referral/DefaultReferralComponent.kt b/features/referral/impl/src/main/java/com/tangem/feature/referral/DefaultReferralComponent.kt index da7d10bcf7..d85d5291f1 100644 --- a/features/referral/impl/src/main/java/com/tangem/feature/referral/DefaultReferralComponent.kt +++ b/features/referral/impl/src/main/java/com/tangem/feature/referral/DefaultReferralComponent.kt @@ -17,6 +17,7 @@ import com.tangem.features.account.PortfolioSelectorComponent import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import kotlinx.serialization.builtins.serializer class DefaultReferralComponent @AssistedInject constructor( private val portfolioSelectorComponentFactory: PortfolioSelectorComponent.Factory, @@ -27,7 +28,7 @@ class DefaultReferralComponent @AssistedInject constructor( private val model: ReferralModel = getOrCreateModel(params) private val bottomSheetSlot = childSlot( source = model.bottomSheetNavigation, - serializer = null, + serializer = Unit.serializer(), handleBackButton = false, childFactory = { configuration, context -> bottomSheetChild(context) }, ) diff --git a/features/referral/impl/src/main/java/com/tangem/feature/referral/model/ReferralModel.kt b/features/referral/impl/src/main/java/com/tangem/feature/referral/model/ReferralModel.kt index 029f05c2d3..4564ce9ded 100644 --- a/features/referral/impl/src/main/java/com/tangem/feature/referral/model/ReferralModel.kt +++ b/features/referral/impl/src/main/java/com/tangem/feature/referral/model/ReferralModel.kt @@ -85,8 +85,8 @@ internal class ReferralModel @Inject constructor( private set val portfolioSelectorCallback = object : PortfolioSelectorComponent.BottomSheetCallback { - override val onDismiss: () -> Unit = { bottomSheetNavigation::dismiss } - override val onBack: () -> Unit = { bottomSheetNavigation::dismiss } + override val onDismiss: () -> Unit = { bottomSheetNavigation.dismiss() } + override val onBack: () -> Unit = { bottomSheetNavigation.dismiss() } } init { @@ -115,7 +115,8 @@ internal class ReferralModel @Inject constructor( } private fun combineAccountUI(referralData: ReferralData): Flow = combine( - flow = portfolioSelectorController.selectedAccountWithData(portfolioFetcher), + flow = portfolioSelectorController.selectedAccountWithData(portfolioFetcher) + .onEach { bottomSheetNavigation.dismiss() }, flow2 = getBalanceHidingSettingsUseCase.isBalanceHidden(), flow3 = getSelectedAppCurrencyUseCase.invokeOrDefault(), flow4 = portfolioFetcher.data, diff --git a/features/referral/impl/src/main/java/com/tangem/feature/referral/ui/ParticipateBottomBlock.kt b/features/referral/impl/src/main/java/com/tangem/feature/referral/ui/ParticipateBottomBlock.kt index 12ebf8162c..0165c19809 100644 --- a/features/referral/impl/src/main/java/com/tangem/feature/referral/ui/ParticipateBottomBlock.kt +++ b/features/referral/impl/src/main/java/com/tangem/feature/referral/ui/ParticipateBottomBlock.kt @@ -10,25 +10,33 @@ import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.shadow +import androidx.compose.ui.draw.clip import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.style.TextOverflow 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.common.ui.account.AccountIcon import com.tangem.core.res.getStringSafe import com.tangem.core.ui.components.PrimaryButtonIconStart +import com.tangem.core.ui.components.SpacerH12 +import com.tangem.core.ui.components.SpacerW12 +import com.tangem.core.ui.components.account.AccountIconSize import com.tangem.core.ui.components.rows.RoundableCornersRow import com.tangem.core.ui.extensions.pluralStringResourceSafe +import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.feature.referral.domain.models.ExpectedAward import com.tangem.feature.referral.domain.models.ExpectedAwards +import com.tangem.feature.referral.models.ReferralStateHolder import com.tangem.feature.referral.presentation.R import kotlinx.coroutines.launch @@ -43,6 +51,7 @@ internal fun ParticipateBottomBlock( onAgreementClick: () -> Unit, onCopyClick: () -> Unit, onShareClick: (String) -> Unit, + accountAward: ReferralStateHolder.AccountAward?, ) { Column( modifier = Modifier @@ -53,7 +62,7 @@ internal fun ParticipateBottomBlock( .padding(horizontal = TangemTheme.dimens.spacing16), verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16), ) { - PersonalCodeCard(code = code) + PersonalCodeCard(code = code, accountAward = accountAward) AdditionalButtons( code = code, shareLink = shareLink, @@ -242,19 +251,13 @@ private fun ExtraItems(extraItems: List, overallItemsLastIndex: I } @Composable -private fun PersonalCodeCard(code: String) { +private fun PersonalCodeCard(code: String, accountAward: ReferralStateHolder.AccountAward?) { Column( modifier = Modifier - .shadow( - elevation = TangemTheme.dimens.elevation2, - shape = RoundedCornerShape(TangemTheme.dimens.radius12), - ) - .background( - color = TangemTheme.colors.background.secondary, - shape = RoundedCornerShape(TangemTheme.dimens.radius12), - ) + .clip(RoundedCornerShape(TangemTheme.dimens.radius12)) + .background(color = TangemTheme.colors.background.primary) .fillMaxWidth() - .padding(vertical = TangemTheme.dimens.spacing12), + .padding(top = TangemTheme.dimens.spacing12), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), ) { @@ -270,6 +273,53 @@ private fun PersonalCodeCard(code: String) { maxLines = 1, style = TangemTheme.typography.h2, ) + + if (accountAward != null) { + AwardAccount(accountAward) + } else { + SpacerH12() + } + } +} + +@Composable +private fun AwardAccount(accountAward: ReferralStateHolder.AccountAward) { + Column { + HorizontalDivider( + thickness = 0.5.dp, + color = TangemTheme.colors.stroke.primary, + modifier = Modifier.padding(horizontal = 12.dp), + ) + Row( + modifier = Modifier.padding(12.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + modifier = Modifier.weight(1f), + text = stringResourceSafe(R.string.account_for_rewards), + style = TangemTheme.typography.body1, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + color = TangemTheme.colors.text.secondary, + ) + SpacerW12() + val icon = accountAward.accountSelectUM.icon + if (icon != null) { + AccountIcon( + name = accountAward.accountSelectUM.name, + icon = icon, + size = AccountIconSize.Small, + ) + } + Text( + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.padding(horizontal = 4.dp), + text = accountAward.accountSelectUM.name.resolveReference(), + style = TangemTheme.typography.body1, + color = TangemTheme.colors.text.primary1, + ) + } } } @@ -351,10 +401,11 @@ private fun ParticipateBottomBlockPreview( code = data.code, shareLink = data.shareLink, expectedAwards = data.expectedAwards, - onAgreementClick = data.onAgreementClick, snackbarHostState = SnackbarHostState(), + onAgreementClick = data.onAgreementClick, onCopyClick = data.onCopyClick, onShareClick = data.onShareClick, + accountAward = data.accountAward, ) } } @@ -410,12 +461,18 @@ private class ParticipateBottomBlockDataProvider : CollectionPreviewParameterPro purchasedWalletCount = 0, expectedAwards = null, ), + ParticipateBottomBlockData( + purchasedWalletCount = 0, + expectedAwards = null, + accountAward = accountReward, + ), ), ) private data class ParticipateBottomBlockData( val purchasedWalletCount: Int, val expectedAwards: ExpectedAwards?, + val accountAward: ReferralStateHolder.AccountAward? = null, val code: String = "x4JDK", val shareLink: String = "", val onAgreementClick: () -> Unit = {}, diff --git a/features/referral/impl/src/main/java/com/tangem/feature/referral/ui/ReferralScreen.kt b/features/referral/impl/src/main/java/com/tangem/feature/referral/ui/ReferralScreen.kt index c32525809a..76efe5d729 100644 --- a/features/referral/impl/src/main/java/com/tangem/feature/referral/ui/ReferralScreen.kt +++ b/features/referral/impl/src/main/java/com/tangem/feature/referral/ui/ReferralScreen.kt @@ -185,6 +185,7 @@ private fun ReferralInfo( code = state.code, shareLink = state.shareLink, expectedAwards = state.expectedAwards, + accountAward = state.accountAward, snackbarHostState = snackbarHostState, onAgreementClick = onAgreementClick, onCopyClick = stateHolder.analytics.onCopyClicked, @@ -479,6 +480,7 @@ private fun Preview_ReferralScreen_Participant_With_Referrals() { code = "x4JdK", shareLink = "", url = "", + accountAward = accountReward, expectedAwards = ExpectedAwards( numberOfWallets = 5, expectedAwards = listOf( @@ -535,17 +537,45 @@ private fun Preview_ReferralScreen_NonParticipant() { } } +internal val account = PortfolioSelectUM( + icon = AccountIconPreviewData.randomAccountIcon(), + name = AccountName.DefaultMain.toUM().value, + isAccountMode = true, + isMultiChoice = true, + onClick = {}, +) + +internal val accountReward + get() = AccountAward( + accountSelectUM = account, + isBalanceHidden = false, + tokenState = TokenItemState.Content( + id = UUID.randomUUID().toString(), + iconState = CurrencyIconState.TokenIcon( + url = null, + topBadgeIconResId = R.drawable.img_tron_22, + fallbackTint = TangemColorPalette.Black, + fallbackBackground = TangemColorPalette.Meadow, + isGrayscale = false, + shouldShowCustomBadge = false, + ), + titleState = TokenItemState.TitleState.Content( + text = stringReference(value = "Tether"), + ), + fiatAmountState = FiatAmountState.Content( + text = "129,65 $", + ), + subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "129,65 USDT"), + subtitleState = TokenItemState.SubtitleState.TextContent(value = stringReference("USDT")), + onItemClick = null, + onItemLongClick = null, + ), + ) + @Preview(widthDp = 360, showBackground = true) @Preview(widthDp = 360, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable private fun Preview_ReferralScreen_NonParticipantAccount() { - val account = PortfolioSelectUM( - icon = AccountIconPreviewData.randomAccountIcon(), - name = AccountName.DefaultMain.toUM().value, - isAccountMode = true, - isMultiChoice = true, - onClick = {}, - ) TangemThemePreview { ReferralScreen( stateHolder = ReferralStateHolder( @@ -557,31 +587,7 @@ private fun Preview_ReferralScreen_NonParticipantAccount() { url = "", onParticipateClicked = {}, participateButtonIcon = R.drawable.ic_tangem_24, - accountAward = AccountAward( - accountSelectUM = account, - isBalanceHidden = false, - tokenState = TokenItemState.Content( - id = UUID.randomUUID().toString(), - iconState = CurrencyIconState.TokenIcon( - url = null, - topBadgeIconResId = R.drawable.img_tron_22, - fallbackTint = TangemColorPalette.Black, - fallbackBackground = TangemColorPalette.Meadow, - isGrayscale = false, - shouldShowCustomBadge = false, - ), - titleState = TokenItemState.TitleState.Content( - text = stringReference(value = "Tether"), - ), - fiatAmountState = FiatAmountState.Content( - text = "129,65 $", - ), - subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "129,65 USDT"), - subtitleState = TokenItemState.SubtitleState.TextContent(value = stringReference("USDT")), - onItemClick = null, - onItemLongClick = null, - ), - ), + accountAward = accountReward, ), errorSnackbar = null, analytics = Analytics( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/preview/WalletScreenPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/preview/WalletScreenPreviewData.kt index 50569e441a..abedb142a7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/preview/WalletScreenPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/preview/WalletScreenPreviewData.kt @@ -106,6 +106,28 @@ internal object WalletScreenPreviewData { ), ) + private val emptyPortfolioContentState = WalletTokensListState.ContentState.PortfolioContent( + items = persistentListOf( + TokensListItemUM.Portfolio( + tokens = textContentTokensState.items.filterIsInstance().toPersistentList(), + isExpanded = false, + isCollapsable = true, + tokenItemUM = AccountItemPreviewData.accountItem + .copy(iconState = AccountItemPreviewData.accountLetterIcon), + ), + TokensListItemUM.Portfolio( + tokens = persistentListOf(), + isExpanded = true, + isCollapsable = true, + tokenItemUM = AccountItemPreviewData.accountItem, + ), + ), + organizeTokensButtonConfig = WalletTokensListState.OrganizeTokensButtonConfig( + isEnabled = true, + onClick = {}, + ), + ) + private val noteLockedCard by lazy { WalletCardState.LockedContent( id = UserWalletId(stringValue = "1"), @@ -208,4 +230,12 @@ internal object WalletScreenPreviewData { multiWalletState.copy(tokensListState = portfolioContentState), ), ) + + internal val accountScreenWithEmptyTokensState = + walletScreenState.copy( + wallets = persistentListOf( + singleWalletLockedState, + multiWalletState.copy(tokensListState = emptyPortfolioContentState), + ), + ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt index 9134b2295d..68827d346e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt @@ -100,6 +100,7 @@ internal class TokenListStateConverter( appCurrency = appCurrency, account = account, onItemClick = onItemClick, + priceChangeLce = this.priceChangeLce, ) val accountItem = converter.convert(tokenList.totalFiatBalance) val tokenConverter = tokenStatusConverter(account.accountId) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index 0f62bfd51f..47408a1b03 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -69,6 +69,7 @@ import com.tangem.core.ui.utils.moveTo import com.tangem.core.ui.utils.toPx import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.accountScreenState +import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.accountScreenWithEmptyTokensState import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.walletScreenState import com.tangem.feature.wallet.presentation.wallet.state.model.* import com.tangem.feature.wallet.presentation.wallet.state.model.holder.TxHistoryStateHolder @@ -753,6 +754,7 @@ private class WalletScreenPreviewProvider : PreviewParameterProvider item.id }, @@ -74,6 +93,7 @@ internal fun LazyListScope.portfolioTokensList( .testModifier(indexWithHeader) .animateItem() .roundedShapeItemDecoration( + radius = TangemTheme.dimens.radius14, currentIndex = indexWithHeader, lastIndex = lastIndex, backgroundColor = TangemTheme.colors.background.primary, @@ -101,6 +121,12 @@ private fun LazyListScope.portfolioItem( val tokens = portfolio.tokens val isExpanded = portfolio.isExpanded + val lastIndex = when { + isExpanded && tokens.isEmpty() -> 1 + isExpanded -> tokens.lastIndex.inc() + else -> 0 + } + item( key = "account-${portfolio.id}-isExpanded$isExpanded", contentType = "account-isExpanded$isExpanded", @@ -110,27 +136,24 @@ private fun LazyListScope.portfolioItem( .animateItem() .roundedShapeItemDecoration( currentIndex = 0, - lastIndex = if (isExpanded) tokens.lastIndex.inc() else 0, + radius = TangemTheme.dimens.radius14, + lastIndex = lastIndex, backgroundColor = TangemTheme.colors.background.primary, ) val isPreview = LocalInspectionMode.current val appear = remember { - MutableTransitionState(selectedWalletChanged || isPreview).apply { targetState = true } + MutableTransitionState(selectedWalletChanged || isPreview || tokens.isEmpty()) + .apply { targetState = true } } if (isExpanded) { SlideInItemVisibility( modifier = anchorModifier, visibleState = appear, ) { - val modifier = if (portfolio.tokens.isEmpty()) { - Modifier.padding(vertical = 8.dp) - } else { - Modifier.padding(top = 8.dp) - } PortfolioListItem( state = portfolio, isBalanceHidden = isBalanceHidden, - modifier = modifier, + modifier = Modifier.padding(vertical = 8.dp), ) } } else { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt index 008c859f5f..edf6506e26 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material3.Icon 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.testTag @@ -25,7 +26,7 @@ import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState import kotlinx.collections.immutable.ImmutableList -private const val NON_CONTENT_TOKENS_LIST_KEY = "NON_CONTENT_TOKENS_LIST" +internal const val NON_CONTENT_TOKENS_LIST_KEY = "NON_CONTENT_TOKENS_LIST" /** * LazyList extension for [WalletTokensListState] @@ -91,27 +92,34 @@ private fun LazyListScope.nonContentItem(modifier: Modifier = Modifier) { key = NON_CONTENT_TOKENS_LIST_KEY, contentType = NON_CONTENT_TOKENS_LIST_KEY, ) { - Column( + NonContentItemContent( modifier = modifier .animateItem(fadeInSpec = null, fadeOutSpec = null) .padding(top = TangemTheme.dimens.spacing96), - verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing16), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Icon( - painter = painterResource(id = R.drawable.ic_empty_64), - contentDescription = null, - modifier = Modifier.size(size = TangemTheme.dimens.size64), - tint = TangemTheme.colors.icon.inactive, - ) + ) + } +} - Text( - text = stringResourceSafe(id = R.string.main_empty_tokens_list_message), - modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing48), - color = TangemTheme.colors.text.tertiary, - textAlign = TextAlign.Center, - style = TangemTheme.typography.caption2, - ) - } +@Composable +internal fun NonContentItemContent(modifier: Modifier = Modifier) { + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing16), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Icon( + painter = painterResource(id = R.drawable.ic_empty_64), + contentDescription = null, + modifier = Modifier.size(size = TangemTheme.dimens.size64), + tint = TangemTheme.colors.icon.inactive, + ) + + Text( + text = stringResourceSafe(id = R.string.main_empty_tokens_list_message), + modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing48), + color = TangemTheme.colors.text.tertiary, + textAlign = TextAlign.Center, + style = TangemTheme.typography.caption2, + ) } } \ No newline at end of file