diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml
index 15e20368f1..3f051e2972 100644
--- a/core/res/src/main/res/values-ja/strings.xml
+++ b/core/res/src/main/res/values-ja/strings.xml
@@ -375,7 +375,7 @@
トレンド
%sについて
- - %1$s取引所
+ - %d取引所
- %d のレーティングに基づいて
@@ -405,6 +405,7 @@
指定された期間中のトークンに利用可能な流動性の変化
流動性
流動性指数
+ 上場先
低い
時価総額
暗号資産の市場価値の合計。コインの現在の価格と、流通しているコインの総数を掛けて計算されます。
diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index 040fca1460..00cebaa9f8 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -383,10 +383,10 @@
В тренде
О %s
- - %1$s биржа
- - %1$s биржи
- - %1$s бирж
- - %1$s бирж
+ - %d биржа
+ - %d биржи
+ - %d бирж
+ - %d бирж
- На основе %d оценки
@@ -402,6 +402,8 @@
Общее количество монет, доступных для торговли и находящихся в обращении на рынке
Циркулирующее предложение
Биржи не найдены
+ Биржа
+ Биржи
Опытные трейдеры
Покупатели, у которых было как минимум 100 исходящих транзакций.
Опытные трейдеры
@@ -442,6 +444,7 @@
Объем торгов (24ч)
Общая сумма криптовалюты, которая была продана за последние 24 часа, показывающая уровень активности и ликвидности на рынке.
Объем торгов (24ч)
+ Объем
Потяните вверх или коснитесь поисковой строки, чтобы добавить токены напрямую из маркета
Добавить токены
Функция NFC недоступна на вашем устройстве
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index 7e90b04d5e..da6e4bbab5 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -379,8 +379,8 @@
Trending
About %s
- - %1$s exchange
- - %1$s exchanges
+ - %d exchange
+ - %d exchanges
- Based on %d rating
@@ -394,6 +394,11 @@
The total number of coins that are available for trading and are circulating in the market
Circulating supply
No exchanges found
+ Exchange
+ Caution
+ Risky
+ Trusted
+ Exchanges
Experienced buyers
Net buyers with the additional requirement of having at least 100 outgoing transactions
Experienced buyers
@@ -434,6 +439,7 @@
Trading volume (24h)
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
Trading volume (24h)
+ Volume
Pull this up or tap the search bar to add tokens directly from the market
Add tokens
NFC is not available on your device
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 fbed4994dd..528978ea61 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
@@ -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
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/ExchangesBottomSheet.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/ExchangesBottomSheet.kt
new file mode 100644
index 0000000000..e791211b63
--- /dev/null
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/ExchangesBottomSheet.kt
@@ -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(
+ 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(
+ 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(),
+ ),
+ ),
+)
\ No newline at end of file
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/state/ExchangesBottomSheetContent.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/state/ExchangesBottomSheetContent.kt
new file mode 100644
index 0000000000..cccbf85d31
--- /dev/null
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/state/ExchangesBottomSheetContent.kt
@@ -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
+
+ /**
+ * Loading state
+ *
+ * @property exchangesCount count of exchanges
+ */
+ data class Loading(val exchangesCount: Int) : ExchangesBottomSheetContent {
+
+ override val exchangeItems: ImmutableList
+ get() = List(size = exchangesCount) { index -> TokenItemState.Loading(id = "loading#$index") }
+ .toImmutableList()
+ }
+
+ /**
+ * Content state
+ *
+ * @property exchangeItems exchanges
+ */
+ data class Content(
+ override val exchangeItems: ImmutableList,
+ ) : ExchangesBottomSheetContent
+
+ /** Error state */
+ data class Error(
+ val onRetryClick: () -> Unit,
+ ) : ExchangesBottomSheetContent {
+ override val exchangeItems: ImmutableList = persistentListOf()
+
+ @StringRes
+ val message: Int = R.string.markets_loading_error_title
+ }
+}
\ No newline at end of file