Updated on 2026-08-14
This commit is contained in:
parent
385cc79a32
commit
a3f2c421bf
69 changed files with 749 additions and 542 deletions
|
|
@ -47,14 +47,15 @@ inline fun <reified T> SegmentedButtons(
|
|||
selectedColor: Color = TangemTheme.colors.background.action,
|
||||
dividerColor: Color = TangemTheme.colors.stroke.primary,
|
||||
showIndication: Boolean = true,
|
||||
selectedItem: T? = null,
|
||||
initialSelectedItem: T? = null,
|
||||
isEnabled: Boolean = true,
|
||||
crossinline buttonContent: @Composable (T) -> Unit,
|
||||
) {
|
||||
if (config.isEmpty() || config.size == 1) return
|
||||
|
||||
var selected by remember {
|
||||
val selectedIndex = if (selectedItem == null) 0 else config.indexOf(selectedItem)
|
||||
mutableIntStateOf(selectedIndex)
|
||||
var selectedIndex by remember {
|
||||
val index = if (initialSelectedItem == null) 0 else config.indexOf(initialSelectedItem)
|
||||
mutableIntStateOf(index)
|
||||
}
|
||||
|
||||
Row(
|
||||
|
|
@ -69,7 +70,7 @@ inline fun <reified T> SegmentedButtons(
|
|||
val rightRadius = if (index == config.lastIndex) TangemTheme.dimens.radius26 else TangemTheme.dimens.radius0
|
||||
|
||||
val animateColor by animateColorAsState(
|
||||
targetValue = if (index == selected) selectedColor else color,
|
||||
targetValue = if (index == selectedIndex) selectedColor else color,
|
||||
label = "Segmented Button Selected Color Animation",
|
||||
animationSpec = spring(stiffness = Spring.StiffnessMedium),
|
||||
)
|
||||
|
|
@ -86,11 +87,12 @@ inline fun <reified T> SegmentedButtons(
|
|||
),
|
||||
)
|
||||
.clickable(
|
||||
enabled = isEnabled,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = if (showIndication) LocalIndication.current else null,
|
||||
) {
|
||||
selectedIndex = index
|
||||
onClick(config[index])
|
||||
selected = index
|
||||
},
|
||||
) {
|
||||
buttonContent.invoke(config[index])
|
||||
|
|
|
|||
|
|
@ -4,11 +4,16 @@ import androidx.annotation.DrawableRes
|
|||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.ColorMatrix
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.currency.DefaultCurrencyIcon
|
||||
|
||||
private const val GRAY_SCALE_SATURATION = 0f
|
||||
private const val GRAY_SCALE_ALPHA = 0.4f
|
||||
|
||||
/**
|
||||
* Simple icon from network
|
||||
*
|
||||
|
|
@ -20,16 +25,19 @@ import com.tangem.core.ui.components.currency.DefaultCurrencyIcon
|
|||
fun FiatIcon(
|
||||
url: String?,
|
||||
size: Dp,
|
||||
isGrayscale: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
@DrawableRes fallbackResId: Int = R.drawable.ic_shape_circle,
|
||||
) {
|
||||
val iconData: Any = if (url.isNullOrBlank()) fallbackResId else url
|
||||
val alpha = if (isGrayscale) GRAY_SCALE_ALPHA else 1f
|
||||
val colorFilter = if (isGrayscale) GrayscaleColorFilter else null
|
||||
|
||||
DefaultCurrencyIcon(
|
||||
iconData = iconData,
|
||||
size = size,
|
||||
alpha = 1f,
|
||||
colorFilter = null,
|
||||
alpha = alpha,
|
||||
colorFilter = colorFilter,
|
||||
errorIcon = {
|
||||
Image(
|
||||
painter = painterResource(id = fallbackResId),
|
||||
|
|
@ -38,4 +46,7 @@ fun FiatIcon(
|
|||
},
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val GrayscaleColorFilter: ColorFilter
|
||||
get() = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(GRAY_SCALE_SATURATION) })
|
||||
|
|
@ -20,6 +20,26 @@ class CryptoCurrencyToIconStateConverter : Converter<CryptoCurrencyStatus, Token
|
|||
}
|
||||
}
|
||||
|
||||
fun convertCustom(
|
||||
value: CryptoCurrencyStatus,
|
||||
forceGrayscale: Boolean,
|
||||
showCustomTokenBadge: Boolean,
|
||||
): TokenIconState {
|
||||
return when (val currency = value.currency) {
|
||||
is CryptoCurrency.Coin -> getIconStateForCoin(
|
||||
coin = currency,
|
||||
isUnreachable = value.value.isError,
|
||||
forceGrayscale = forceGrayscale,
|
||||
)
|
||||
is CryptoCurrency.Token -> getIconStateForToken(
|
||||
token = currency,
|
||||
isErrorStatus = value.value.isError,
|
||||
forceGrayscale = forceGrayscale,
|
||||
showCustomBadge = showCustomTokenBadge,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun convert(currency: CryptoCurrency): TokenIconState {
|
||||
return when (currency) {
|
||||
is CryptoCurrency.Coin -> getIconStateForCoin(currency, isUnreachable = false)
|
||||
|
|
@ -27,18 +47,27 @@ class CryptoCurrencyToIconStateConverter : Converter<CryptoCurrencyStatus, Token
|
|||
}
|
||||
}
|
||||
|
||||
private fun getIconStateForCoin(coin: CryptoCurrency.Coin, isUnreachable: Boolean): TokenIconState.CoinIcon {
|
||||
private fun getIconStateForCoin(
|
||||
coin: CryptoCurrency.Coin,
|
||||
isUnreachable: Boolean,
|
||||
forceGrayscale: Boolean = false,
|
||||
): TokenIconState.CoinIcon {
|
||||
return TokenIconState.CoinIcon(
|
||||
url = coin.iconUrl,
|
||||
fallbackResId = coin.networkIconResId,
|
||||
isGrayscale = coin.network.isTestnet || isUnreachable,
|
||||
isGrayscale = forceGrayscale || coin.network.isTestnet || isUnreachable,
|
||||
showCustomBadge = coin.isCustom,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getIconStateForToken(token: CryptoCurrency.Token, isErrorStatus: Boolean): TokenIconState {
|
||||
val isGrayscale = token.network.isTestnet || isErrorStatus
|
||||
val background = token.tryGetBackgroundForTokenIcon(isGrayscale)
|
||||
private fun getIconStateForToken(
|
||||
token: CryptoCurrency.Token,
|
||||
isErrorStatus: Boolean,
|
||||
showCustomBadge: Boolean = true,
|
||||
forceGrayscale: Boolean = false,
|
||||
): TokenIconState {
|
||||
val grayScale = forceGrayscale || token.network.isTestnet || isErrorStatus
|
||||
val background = token.tryGetBackgroundForTokenIcon(grayScale)
|
||||
val tint = getTintForTokenIcon(background)
|
||||
|
||||
return if (token.isCustom && token.iconUrl == null) {
|
||||
|
|
@ -46,16 +75,16 @@ class CryptoCurrencyToIconStateConverter : Converter<CryptoCurrencyStatus, Token
|
|||
tint = tint,
|
||||
background = background,
|
||||
networkBadgeIconResId = token.networkIconResId,
|
||||
isGrayscale = isGrayscale,
|
||||
isGrayscale = grayScale,
|
||||
)
|
||||
} else {
|
||||
TokenIconState.TokenIcon(
|
||||
url = token.iconUrl,
|
||||
networkBadgeIconResId = token.networkIconResId,
|
||||
isGrayscale = isGrayscale,
|
||||
isGrayscale = grayScale,
|
||||
fallbackTint = tint,
|
||||
fallbackBackground = background,
|
||||
showCustomBadge = token.isCustom, // `true` for tokens with custom derivation
|
||||
showCustomBadge = token.isCustom && showCustomBadge, // `true` for tokens with custom derivation
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ fun PasteButton(isPasteButtonVisible: Boolean, onClick: (String) -> Unit, modifi
|
|||
modifier = modifier,
|
||||
) {
|
||||
Text(
|
||||
text = "Paste",
|
||||
text = stringResource(R.string.common_paste),
|
||||
style = TangemTheme.typography.button,
|
||||
color = TangemTheme.colors.text.primary2,
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.utils.toDateFormat
|
||||
import com.tangem.core.ui.utils.DateTimeFormatters
|
||||
import com.tangem.core.ui.utils.toTimeFormat
|
||||
|
||||
/**
|
||||
|
|
@ -47,7 +47,11 @@ fun TransactionDoneTitle(@StringRes titleRes: Int, date: Long, modifier: Modifie
|
|||
.padding(top = TangemTheme.dimens.spacing16),
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.send_date_format, date.toDateFormat(), date.toTimeFormat()),
|
||||
text = stringResource(
|
||||
id = R.string.send_date_format,
|
||||
date.toTimeFormat(DateTimeFormatters.dateFormatter),
|
||||
date.toTimeFormat(),
|
||||
),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -40,6 +40,29 @@ object BigDecimalFormatter {
|
|||
}
|
||||
}
|
||||
|
||||
fun formatCryptoAmountUncapped(
|
||||
cryptoAmount: BigDecimal?,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
locale: Locale = Locale.getDefault(),
|
||||
): String {
|
||||
if (cryptoAmount == null) return EMPTY_BALANCE_SIGN
|
||||
|
||||
val formatter = NumberFormat.getNumberInstance(locale).apply {
|
||||
maximumFractionDigits = cryptoCurrency.decimals
|
||||
minimumFractionDigits = 2
|
||||
isGroupingUsed = true
|
||||
roundingMode = RoundingMode.DOWN
|
||||
}
|
||||
|
||||
return formatter.format(cryptoAmount).let {
|
||||
if (cryptoCurrency.symbol.isEmpty()) {
|
||||
it
|
||||
} else {
|
||||
it + "\u2009${cryptoCurrency.symbol}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun formatCryptoAmount(
|
||||
cryptoAmount: BigDecimal?,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import org.joda.time.format.DateTimeFormatter
|
|||
* If [this] timestamp is today or yesterday, returns relative date,
|
||||
* otherwise returns formatting date.
|
||||
*/
|
||||
fun Long.toDateFormat(formatter: DateTimeFormatter = DateTimeFormatters.dateFormatter): String {
|
||||
fun Long.toDateFormatWithTodayYesterday(formatter: DateTimeFormatter = DateTimeFormatters.dateFormatter): String {
|
||||
val localDate = DateTime(this, DateTimeZone.getDefault())
|
||||
return if (localDate.isToday() || localDate.isYesterday()) {
|
||||
DateUtils.getRelativeTimeSpanString(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue