Updated on 2026-08-14
This commit is contained in:
parent
1101c09e91
commit
a3bee722b7
6 changed files with 298 additions and 9 deletions
|
|
@ -375,7 +375,7 @@
|
|||
<string name="markets_sort_by_trending_title">トレンド</string>
|
||||
<string name="markets_token_details_about_token_title">%sについて</string>
|
||||
<plurals name="markets_token_details_amount_exchanges">
|
||||
<item quantity="other">%1$s取引所</item>
|
||||
<item quantity="other">%d取引所</item>
|
||||
</plurals>
|
||||
<plurals name="markets_token_details_based_on_ratings">
|
||||
<item quantity="other">%d のレーティングに基づいて</item>
|
||||
|
|
@ -405,6 +405,7 @@
|
|||
<string name="markets_token_details_liquidity_description">指定された期間中のトークンに利用可能な流動性の変化</string>
|
||||
<string name="markets_token_details_liquidity_full">流動性</string>
|
||||
<string name="markets_token_details_liquidity_index">流動性指数</string>
|
||||
<string name="markets_token_details_listed_on">上場先</string>
|
||||
<string name="markets_token_details_low">低い</string>
|
||||
<string name="markets_token_details_market_capitalization">時価総額</string>
|
||||
<string name="markets_token_details_market_capitalization_description">暗号資産の市場価値の合計。コインの現在の価格と、流通しているコインの総数を掛けて計算されます。</string>
|
||||
|
|
|
|||
|
|
@ -383,10 +383,10 @@
|
|||
<string name="markets_sort_by_trending_title">В тренде</string>
|
||||
<string name="markets_token_details_about_token_title">О %s</string>
|
||||
<plurals name="markets_token_details_amount_exchanges">
|
||||
<item quantity="one">%1$s биржа</item>
|
||||
<item quantity="few">%1$s биржи</item>
|
||||
<item quantity="many">%1$s бирж</item>
|
||||
<item quantity="other">%1$s бирж</item>
|
||||
<item quantity="one">%d биржа</item>
|
||||
<item quantity="few">%d биржи</item>
|
||||
<item quantity="many">%d бирж</item>
|
||||
<item quantity="other">%d бирж</item>
|
||||
</plurals>
|
||||
<plurals name="markets_token_details_based_on_ratings">
|
||||
<item quantity="one">На основе %d оценки</item>
|
||||
|
|
@ -402,6 +402,8 @@
|
|||
<string name="markets_token_details_circulating_supply_description">Общее количество монет, доступных для торговли и находящихся в обращении на рынке</string>
|
||||
<string name="markets_token_details_circulating_supply_full">Циркулирующее предложение</string>
|
||||
<string name="markets_token_details_empty_exchanges">Биржи не найдены</string>
|
||||
<string name="markets_token_details_exchange">Биржа</string>
|
||||
<string name="markets_token_details_exchanges_title">Биржи</string>
|
||||
<string name="markets_token_details_experienced_buyers">Опытные трейдеры</string>
|
||||
<string name="markets_token_details_experienced_buyers_description">Покупатели, у которых было как минимум 100 исходящих транзакций.</string>
|
||||
<string name="markets_token_details_experienced_buyers_full">Опытные трейдеры</string>
|
||||
|
|
@ -442,6 +444,7 @@
|
|||
<string name="markets_token_details_trading_volume">Объем торгов (24ч)</string>
|
||||
<string name="markets_token_details_trading_volume_24h_description">Общая сумма криптовалюты, которая была продана за последние 24 часа, показывающая уровень активности и ликвидности на рынке.</string>
|
||||
<string name="markets_token_details_trading_volume_full">Объем торгов (24ч)</string>
|
||||
<string name="markets_token_details_volume">Объем</string>
|
||||
<string name="markets_tooltip_message">Потяните вверх или коснитесь поисковой строки, чтобы добавить токены напрямую из маркета</string>
|
||||
<string name="markets_tooltip_title">Добавить токены</string>
|
||||
<string name="nfc_error_unavailable">Функция NFC недоступна на вашем устройстве</string>
|
||||
|
|
|
|||
|
|
@ -379,8 +379,8 @@
|
|||
<string name="markets_sort_by_trending_title">Trending</string>
|
||||
<string name="markets_token_details_about_token_title">About %s</string>
|
||||
<plurals name="markets_token_details_amount_exchanges">
|
||||
<item quantity="one">%1$s exchange</item>
|
||||
<item quantity="other">%1$s exchanges</item>
|
||||
<item quantity="one">%d exchange</item>
|
||||
<item quantity="other">%d exchanges</item>
|
||||
</plurals>
|
||||
<plurals name="markets_token_details_based_on_ratings">
|
||||
<item quantity="one">Based on %d rating</item>
|
||||
|
|
@ -394,6 +394,11 @@
|
|||
<string name="markets_token_details_circulating_supply_description">The total number of coins that are available for trading and are circulating in the market</string>
|
||||
<string name="markets_token_details_circulating_supply_full">Circulating supply</string>
|
||||
<string name="markets_token_details_empty_exchanges">No exchanges found</string>
|
||||
<string name="markets_token_details_exchange">Exchange</string>
|
||||
<string name="markets_token_details_exchange_trust_score_caution">Caution</string>
|
||||
<string name="markets_token_details_exchange_trust_score_risky">Risky</string>
|
||||
<string name="markets_token_details_exchange_trust_score_trusted">Trusted</string>
|
||||
<string name="markets_token_details_exchanges_title">Exchanges</string>
|
||||
<string name="markets_token_details_experienced_buyers">Experienced buyers</string>
|
||||
<string name="markets_token_details_experienced_buyers_description">Net buyers with the additional requirement of having at least 100 outgoing transactions</string>
|
||||
<string name="markets_token_details_experienced_buyers_full">Experienced buyers</string>
|
||||
|
|
@ -434,6 +439,7 @@
|
|||
<string name="markets_token_details_trading_volume">Trading volume (24h)</string>
|
||||
<string name="markets_token_details_trading_volume_24h_description">The total amount of a cryptocurrency that has been traded within the last 24 hours, indicating the level of activity and liquidity in the market</string>
|
||||
<string name="markets_token_details_trading_volume_full">Trading volume (24h)</string>
|
||||
<string name="markets_token_details_volume">Volume</string>
|
||||
<string name="markets_tooltip_message">Pull this up or tap the search bar to add tokens directly from the market</string>
|
||||
<string name="markets_tooltip_title">Add tokens</string>
|
||||
<string name="nfc_error_unavailable">NFC is not available on your device</string>
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ sealed class TokenItemState {
|
|||
*/
|
||||
data class Loading(
|
||||
override val id: String,
|
||||
override val iconState: CurrencyIconState,
|
||||
override val titleState: TitleState.Content,
|
||||
override val iconState: CurrencyIconState = CurrencyIconState.Loading,
|
||||
override val titleState: TitleState = TitleState.Loading,
|
||||
override val subtitleState: SubtitleState = SubtitleState.Loading,
|
||||
) : TokenItemState() {
|
||||
override val fiatAmountState: FiatAmountState = FiatAmountState.Loading
|
||||
|
|
|
|||
|
|
@ -0,0 +1,206 @@
|
|||
package com.tangem.features.markets.details.impl.ui.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
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 androidx.compose.ui.util.fastForEach
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.SpacerH12
|
||||
import com.tangem.core.ui.components.appbar.TangemTopAppBar
|
||||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
import com.tangem.core.ui.components.audits.AuditLabelUM
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.buttons.SecondarySmallButton
|
||||
import com.tangem.core.ui.components.buttons.SmallButtonConfig
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.token.TokenItem
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.markets.details.impl.ui.state.ExchangesBottomSheetContent
|
||||
import com.tangem.features.markets.impl.R
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
/**
|
||||
* Exchanges bottom sheet
|
||||
*
|
||||
* @param config bottom sheet config
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Composable
|
||||
internal fun ExchangesBottomSheet(config: TangemBottomSheetConfig) {
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
|
||||
TangemBottomSheet<ExchangesBottomSheetContent>(
|
||||
config = config,
|
||||
addBottomInsets = false,
|
||||
title = { Title(textResId = it.titleResId, onBackClick = config.onDismissRequest) },
|
||||
content = { content ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(state = rememberScrollState()),
|
||||
) {
|
||||
Subtitle(
|
||||
subtitleRes = content.subtitleResId,
|
||||
volumeReference = content.volumeReference,
|
||||
modifier = Modifier.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
top = TangemTheme.dimens.spacing12,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing8,
|
||||
),
|
||||
)
|
||||
|
||||
when (content) {
|
||||
is ExchangesBottomSheetContent.Content,
|
||||
is ExchangesBottomSheetContent.Loading,
|
||||
-> {
|
||||
content.exchangeItems.fastForEach { item ->
|
||||
key(item.id) {
|
||||
TokenItem(state = item, isBalanceHidden = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
is ExchangesBottomSheetContent.Error -> {
|
||||
Error(
|
||||
content = content,
|
||||
modifier = Modifier
|
||||
.align(alignment = Alignment.CenterHorizontally)
|
||||
.padding(horizontal = 16.dp)
|
||||
.weight(1f),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
SpacerH(bottomBarHeight)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Title(@StringRes textResId: Int, onBackClick: () -> Unit) {
|
||||
TangemTopAppBar(
|
||||
title = stringResource(id = textResId),
|
||||
startButton = TopAppBarButtonUM.Back(onBackClicked = onBackClick),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Subtitle(@StringRes subtitleRes: Int, volumeReference: TextReference, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
SubtitleText(textReference = resourceReference(id = subtitleRes))
|
||||
|
||||
SubtitleText(textReference = volumeReference)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SubtitleText(textReference: TextReference) {
|
||||
Text(
|
||||
text = textReference.resolveReference(),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Error(content: ExchangesBottomSheetContent.Error, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = content.message),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
textAlign = TextAlign.Center,
|
||||
style = TangemTheme.typography.caption1,
|
||||
)
|
||||
|
||||
SpacerH12()
|
||||
|
||||
SecondarySmallButton(
|
||||
config = SmallButtonConfig(
|
||||
text = resourceReference(id = R.string.alert_button_try_again),
|
||||
onClick = content.onRetryClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(name = "Dark Theme", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview_ExchangesBottomSheet(
|
||||
@PreviewParameter(ExchangesBottomSheetContentProvider::class) content: ExchangesBottomSheetContent,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
ExchangesBottomSheet(
|
||||
config = TangemBottomSheetConfig(
|
||||
onDismissRequest = {},
|
||||
content = content,
|
||||
isShow = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class ExchangesBottomSheetContentProvider : CollectionPreviewParameterProvider<ExchangesBottomSheetContent>(
|
||||
listOf(
|
||||
ExchangesBottomSheetContent.Loading(exchangesCount = 13),
|
||||
ExchangesBottomSheetContent.Error(onRetryClick = {}),
|
||||
ExchangesBottomSheetContent.Content(
|
||||
exchangeItems = List(size = 13) { index ->
|
||||
TokenItemState.Content(
|
||||
id = index.toString(),
|
||||
iconState = CurrencyIconState.CoinIcon(
|
||||
url = null,
|
||||
fallbackResId = R.drawable.ic_facebook_24,
|
||||
isGrayscale = false,
|
||||
showCustomBadge = false,
|
||||
),
|
||||
titleState = TokenItemState.TitleState.Content(text = "OKX"),
|
||||
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "$67.52M"),
|
||||
subtitleState = TokenItemState.SubtitleState.TextContent(value = "CEX"),
|
||||
subtitle2State = TokenItemState.Subtitle2State.LabelContent(
|
||||
auditLabelUM = AuditLabelUM(
|
||||
text = stringReference("Caution"),
|
||||
type = AuditLabelUM.Type.Warning,
|
||||
),
|
||||
),
|
||||
onItemClick = {},
|
||||
onItemLongClick = {},
|
||||
)
|
||||
}
|
||||
.toImmutableList(),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
package com.tangem.features.markets.details.impl.ui.state
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.plus
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.features.markets.impl.R
|
||||
import com.tangem.utils.StringsSigns.DOT
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
/**
|
||||
* Exchanges bottom sheet content
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal sealed interface ExchangesBottomSheetContent : TangemBottomSheetConfigContent {
|
||||
|
||||
/** Title of bottom sheet. Like, app bar. */
|
||||
@get:StringRes
|
||||
val titleResId: Int
|
||||
get() = R.string.markets_token_details_exchanges_title
|
||||
|
||||
/** Subtitle */
|
||||
@get:StringRes
|
||||
val subtitleResId: Int
|
||||
get() = R.string.markets_token_details_exchange
|
||||
|
||||
/** Volume info */
|
||||
@get:StringRes
|
||||
val volumeReference: TextReference
|
||||
get() = resourceReference(id = R.string.markets_token_details_volume) +
|
||||
stringReference(value = " $DOT ") +
|
||||
resourceReference(id = R.string.markets_selector_interval_24h_title)
|
||||
|
||||
/** Exchange items */
|
||||
val exchangeItems: ImmutableList<TokenItemState>
|
||||
|
||||
/**
|
||||
* Loading state
|
||||
*
|
||||
* @property exchangesCount count of exchanges
|
||||
*/
|
||||
data class Loading(val exchangesCount: Int) : ExchangesBottomSheetContent {
|
||||
|
||||
override val exchangeItems: ImmutableList<TokenItemState>
|
||||
get() = List(size = exchangesCount) { index -> TokenItemState.Loading(id = "loading#$index") }
|
||||
.toImmutableList()
|
||||
}
|
||||
|
||||
/**
|
||||
* Content state
|
||||
*
|
||||
* @property exchangeItems exchanges
|
||||
*/
|
||||
data class Content(
|
||||
override val exchangeItems: ImmutableList<TokenItemState>,
|
||||
) : ExchangesBottomSheetContent
|
||||
|
||||
/** Error state */
|
||||
data class Error(
|
||||
val onRetryClick: () -> Unit,
|
||||
) : ExchangesBottomSheetContent {
|
||||
override val exchangeItems: ImmutableList<TokenItemState> = persistentListOf()
|
||||
|
||||
@StringRes
|
||||
val message: Int = R.string.markets_loading_error_title
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue