Updated on 2026-08-14
This commit is contained in:
commit
edec3fcdff
391 changed files with 10988 additions and 3221 deletions
|
|
@ -126,9 +126,12 @@ sealed class AppRoute(val path: String) : Route {
|
|||
val portfolioId: PortfolioId? = null,
|
||||
) : AppRoute(path = "${source.name.lowercase()}/manage_tokens/${portfolioId?.stringValue}") {
|
||||
|
||||
/**
|
||||
* Source of launching the screen.
|
||||
* ManageTokens screen launched from Onboarding by another route. See `OnboardingRoute.ManageTokens`.
|
||||
*/
|
||||
enum class Source {
|
||||
STORIES,
|
||||
ONBOARDING,
|
||||
SETTINGS,
|
||||
}
|
||||
}
|
||||
|
|
@ -192,9 +195,9 @@ sealed class AppRoute(val path: String) : Route {
|
|||
@Serializable
|
||||
data class Staking(
|
||||
val userWalletId: UserWalletId,
|
||||
val cryptoCurrencyId: CryptoCurrency.ID,
|
||||
val cryptoCurrency: CryptoCurrency,
|
||||
val yieldId: String,
|
||||
) : AppRoute(path = "/staking/${userWalletId.stringValue}/${cryptoCurrencyId.value}/$yieldId")
|
||||
) : AppRoute(path = "/staking/${userWalletId.stringValue}/${cryptoCurrency.id.value}/$yieldId")
|
||||
|
||||
@Serializable
|
||||
data class PushNotification(
|
||||
|
|
@ -217,6 +220,11 @@ sealed class AppRoute(val path: String) : Route {
|
|||
val userWalletId: UserWalletId,
|
||||
) : AppRoute(path = "/wallet_backup/${userWalletId.stringValue}")
|
||||
|
||||
@Serializable
|
||||
data class WalletHardwareBackup(
|
||||
val userWalletId: UserWalletId,
|
||||
) : AppRoute(path = "/wallet_hardware_backup/${userWalletId.stringValue}")
|
||||
|
||||
@Serializable
|
||||
data object Markets : AppRoute(path = "/markets")
|
||||
|
||||
|
|
@ -322,6 +330,9 @@ sealed class AppRoute(val path: String) : Route {
|
|||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
object CreateHardwareWallet : AppRoute(path = "/create_hardware_wallet")
|
||||
|
||||
@Serializable
|
||||
object CreateMobileWallet : AppRoute(path = "/create_mobile_wallet")
|
||||
|
||||
|
|
@ -341,6 +352,7 @@ sealed class AppRoute(val path: String) : Route {
|
|||
@Serializable
|
||||
data class CreateWalletBackup(
|
||||
val userWalletId: UserWalletId,
|
||||
val isUpgradeFlow: Boolean,
|
||||
) : AppRoute(path = "/create_wallet_backup/${userWalletId.stringValue}")
|
||||
|
||||
@Serializable
|
||||
|
|
@ -353,6 +365,11 @@ sealed class AppRoute(val path: String) : Route {
|
|||
val userWalletId: UserWalletId,
|
||||
) : AppRoute(path = "/view_seed_phrase/${userWalletId.stringValue}")
|
||||
|
||||
@Serializable
|
||||
data class ForgetWallet(
|
||||
val userWalletId: UserWalletId,
|
||||
) : AppRoute(path = "/forget_wallet/${userWalletId.stringValue}")
|
||||
|
||||
@Serializable
|
||||
data class SendEntryPoint(
|
||||
val userWalletId: UserWalletId,
|
||||
|
|
@ -383,8 +400,9 @@ sealed class AppRoute(val path: String) : Route {
|
|||
|
||||
@Serializable
|
||||
data class TangemPayDetails(
|
||||
val userWalletId: UserWalletId,
|
||||
val config: TangemPayDetailsConfig,
|
||||
) : AppRoute(path = "/tangem_pay_details")
|
||||
) : AppRoute(path = "/tangem_pay_details/${userWalletId.stringValue}")
|
||||
|
||||
@Serializable
|
||||
data class TangemPayOnboarding(
|
||||
|
|
|
|||
|
|
@ -1,21 +1,30 @@
|
|||
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
|
||||
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> {
|
||||
|
|
@ -31,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),
|
||||
|
|
@ -50,14 +67,14 @@ 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) } },
|
||||
)
|
||||
}
|
||||
|
||||
private fun Account.CryptoPortfolio.mapToLoadingState(): TokenItemState.Loading {
|
||||
return TokenItemState.Loading(
|
||||
private fun Account.CryptoPortfolio.mapToLoadingState(): TokenItemState.Content {
|
||||
return TokenItemState.Content(
|
||||
id = account.accountId.value,
|
||||
iconState = AccountIconItemStateConverter.convert(account),
|
||||
titleState = TokenItemState.TitleState.Content(
|
||||
|
|
@ -71,6 +88,10 @@ class AccountCryptoPortfolioItemStateConverter(
|
|||
),
|
||||
isAvailable = false,
|
||||
),
|
||||
fiatAmountState = FiatAmountState.Loading,
|
||||
subtitle2State = Subtitle2State.Loading,
|
||||
onItemLongClick = null,
|
||||
onItemClick = onItemClick?.let { onItemClick -> { onItemClick(account) } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -97,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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -29,6 +30,7 @@ fun AccountTitle(
|
|||
accountTitleUM: AccountTitleUM,
|
||||
modifier: Modifier = Modifier,
|
||||
textStyle: TextStyle = TangemTheme.typography.subtitle2,
|
||||
textColor: Color = TangemTheme.colors.text.tertiary,
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
|
@ -40,19 +42,20 @@ fun AccountTitle(
|
|||
Text(
|
||||
text = accountTitleUM.prefixText.resolveReference(),
|
||||
style = textStyle,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
color = textColor,
|
||||
)
|
||||
AccountLabel(
|
||||
name = accountTitleUM.name,
|
||||
icon = accountTitleUM.icon,
|
||||
iconSize = AccountIconSize.ExtraSmall,
|
||||
nameStyle = textStyle,
|
||||
nameColor = textColor,
|
||||
)
|
||||
}
|
||||
is AccountTitleUM.Text -> Text(
|
||||
text = accountTitleUM.title.resolveReference(),
|
||||
style = textStyle,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
color = textColor,
|
||||
modifier = Modifier.testTag(SendScreenTestTags.AMOUNT_CONTAINER_TITLE),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,119 @@
|
|||
package com.tangem.common.ui.account
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
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.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.core.ui.components.SpacerW12
|
||||
import com.tangem.core.ui.components.account.AccountIconSize
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.domain.models.account.AccountName
|
||||
|
||||
@Composable
|
||||
fun PortfolioSelectRow(state: PortfolioSelectUM, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.clickable(enabled = state.isMultiChoice, onClick = state.onClick)
|
||||
.padding(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
val leftText = if (state.isAccountMode) R.string.account_details_title else R.string.wc_common_wallet
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = stringResourceSafe(leftText),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
SpacerW12()
|
||||
if (state.icon != null) {
|
||||
AccountIcon(
|
||||
name = state.name,
|
||||
icon = state.icon,
|
||||
size = AccountIconSize.Small,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.padding(horizontal = 4.dp),
|
||||
text = state.name.resolveReference(),
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
if (state.isMultiChoice) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(width = 18.dp, height = 24.dp),
|
||||
painter = painterResource(id = R.drawable.ic_select_18_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class PortfolioSelectUM(
|
||||
val icon: CryptoPortfolioIconUM?,
|
||||
val name: TextReference,
|
||||
val isAccountMode: Boolean,
|
||||
val isMultiChoice: Boolean,
|
||||
val onClick: () -> Unit,
|
||||
)
|
||||
|
||||
@Preview(widthDp = 360, showBackground = true)
|
||||
@Preview(widthDp = 360, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun PortfolioSelectRowPreview(@PreviewParameter(PreviewProvider::class) state: PortfolioSelectUM) {
|
||||
TangemThemePreview {
|
||||
PortfolioSelectRow(
|
||||
state = state,
|
||||
modifier = Modifier.background(TangemTheme.colors.background.tertiary),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class PreviewProvider : PreviewParameterProvider<PortfolioSelectUM> {
|
||||
|
||||
val account
|
||||
get() = PortfolioSelectUM(
|
||||
icon = AccountIconPreviewData.randomAccountIcon(),
|
||||
name = AccountName.DefaultMain.toUM().value,
|
||||
isAccountMode = true,
|
||||
isMultiChoice = true,
|
||||
onClick = {},
|
||||
)
|
||||
val wallet
|
||||
get() = PortfolioSelectUM(
|
||||
icon = null,
|
||||
name = stringReference("Wallet Name"),
|
||||
isMultiChoice = false,
|
||||
isAccountMode = false,
|
||||
onClick = {},
|
||||
)
|
||||
|
||||
override val values: Sequence<PortfolioSelectUM>
|
||||
get() = sequenceOf(account, wallet)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue