Updated on 2026-08-14
This commit is contained in:
parent
9abc118480
commit
d3138c662e
18 changed files with 288 additions and 108 deletions
|
|
@ -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<Unit, PriceChange>? = null,
|
||||
private val onItemClick: ((Account.CryptoPortfolio) -> Unit)? = null,
|
||||
private val onItemLongClick: ((Account.CryptoPortfolio) -> Unit)? = null,
|
||||
) : Converter<TotalFiatBalance, TokenItemState> {
|
||||
|
|
@ -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(),
|
||||
)
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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 = {},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -276,3 +276,8 @@ 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
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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) },
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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<AccountAward?> = combine(
|
||||
flow = portfolioSelectorController.selectedAccountWithData(portfolioFetcher),
|
||||
flow = portfolioSelectorController.selectedAccountWithData(portfolioFetcher)
|
||||
.onEach { bottomSheetNavigation.dismiss() },
|
||||
flow2 = getBalanceHidingSettingsUseCase.isBalanceHidden(),
|
||||
flow3 = getSelectedAppCurrencyUseCase.invokeOrDefault(),
|
||||
flow4 = portfolioFetcher.data,
|
||||
|
|
|
|||
|
|
@ -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<ExpectedAward>, 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 = {},
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -106,6 +106,28 @@ internal object WalletScreenPreviewData {
|
|||
),
|
||||
)
|
||||
|
||||
private val emptyPortfolioContentState = WalletTokensListState.ContentState.PortfolioContent(
|
||||
items = persistentListOf(
|
||||
TokensListItemUM.Portfolio(
|
||||
tokens = textContentTokensState.items.filterIsInstance<PortfolioTokensListItemUM>().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),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<WalletScree
|
|||
walletScreenState,
|
||||
walletScreenState.copy(selectedWalletIndex = 1),
|
||||
accountScreenState.copy(selectedWalletIndex = 1),
|
||||
accountScreenWithEmptyTokensState.copy(selectedWalletIndex = 1),
|
||||
)
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -58,6 +58,25 @@ internal fun LazyListScope.portfolioTokensList(
|
|||
selectedWalletChanged = selectedWalletChanged,
|
||||
)
|
||||
if (!isExpanded) return
|
||||
if (tokens.isEmpty()) {
|
||||
item(
|
||||
key = "$NON_CONTENT_TOKENS_LIST_KEY account-${portfolio.id}",
|
||||
contentType = "$NON_CONTENT_TOKENS_LIST_KEY account-${portfolio.id}",
|
||||
) {
|
||||
NonContentItemContent(
|
||||
modifier = Modifier
|
||||
.animateItem()
|
||||
.roundedShapeItemDecoration(
|
||||
radius = TangemTheme.dimens.radius14,
|
||||
currentIndex = 1,
|
||||
lastIndex = 1,
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
)
|
||||
.padding(vertical = TangemTheme.dimens.spacing28),
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
itemsIndexed(
|
||||
items = tokens,
|
||||
key = { _, item -> 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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue