From d10cf0ac2e75a4d1ef9641fc8a7d6b3f93978d47 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 24 Nov 2023 18:28:09 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../layout/layout_single_wallet_balance.xml | 2 +- .../res/layout/view_onboarding_tv_balance.xml | 2 +- core/res/src/main/res/values-ru/strings.xml | 7 +- .../src/main/res/values-zh-rTW/strings.xml | 2 +- core/res/src/main/res/values/strings.xml | 27 ++- .../bottomsheets/TangemBottomSheet.kt | 6 +- .../ui/components/inputrow/InputRowApprox.kt | 150 ++++++++++++ .../components/inputrow/InputRowBestRate.kt | 221 ++++++++++++++++++ .../com/tangem/core/ui/res/TangemDimens.kt | 1 + .../res/drawable/ic_alert_triangle_20.xml | 10 + .../ui/src/main/res/drawable/ic_approx_24.xml | 9 + .../main/res/drawable/ic_exclamation_24.xml | 12 + .../src/main/res/drawable/ic_forward_24.xml | 9 + .../domain/models/domain/ExchangeStatus.kt | 20 ++ features/tokendetails/impl/build.gradle.kts | 2 + .../state/SwapTransactionsState.kt | 31 +++ .../ui/components/TokenDetailsBalanceBlock.kt | 2 +- .../components/exchange/ExchangeEstimate.kt | 67 ++++++ .../components/exchange/ExchangeProvider.kt | 41 ++++ .../exchange/ExchangeStatusBlock.kt | 209 +++++++++++++++++ .../exchange/ExchangeStatusBottomSheet.kt | 84 +++++++ .../exchange/ExchangeStatusItems.kt | 180 ++++++++++++++ .../viewmodels/TokenDetailsViewModel.kt | 33 ++- 23 files changed, 1117 insertions(+), 10 deletions(-) create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowApprox.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowBestRate.kt create mode 100644 core/ui/src/main/res/drawable/ic_alert_triangle_20.xml create mode 100644 core/ui/src/main/res/drawable/ic_approx_24.xml create mode 100644 core/ui/src/main/res/drawable/ic_exclamation_24.xml create mode 100644 core/ui/src/main/res/drawable/ic_forward_24.xml create mode 100644 features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/ExchangeStatus.kt create mode 100644 features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/SwapTransactionsState.kt create mode 100644 features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeEstimate.kt create mode 100644 features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeProvider.kt create mode 100644 features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBlock.kt create mode 100644 features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBottomSheet.kt create mode 100644 features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusItems.kt diff --git a/app/src/main/res/layout/layout_single_wallet_balance.xml b/app/src/main/res/layout/layout_single_wallet_balance.xml index c3a75693bc..d3c6de4ff3 100644 --- a/app/src/main/res/layout/layout_single_wallet_balance.xml +++ b/app/src/main/res/layout/layout_single_wallet_balance.xml @@ -21,7 +21,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginEnd="8dp" - android:text="@string/onboarding_balance_title" + android:text="@string/common_balance_title" android:textColor="@color/text_tertiary" android:textSize="14sp" android:textStyle="bold" diff --git a/app/src/main/res/layout/view_onboarding_tv_balance.xml b/app/src/main/res/layout/view_onboarding_tv_balance.xml index f421e4d992..f84e954980 100644 --- a/app/src/main/res/layout/view_onboarding_tv_balance.xml +++ b/app/src/main/res/layout/view_onboarding_tv_balance.xml @@ -12,7 +12,7 @@ android:elevation="0dp" android:fontFamily="sans-serif-medium" android:letterSpacing="0.036" - android:text="@string/onboarding_balance_title" + android:text="@string/common_balance_title" android:textAllCaps="true" android:textColor="@color/text_secondary" android:textSize="14sp" diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 857c2f4ff1..325b697ba9 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -192,6 +192,11 @@ Недоступен для обмена с %s Провайдеры проводят транзакции, обеспечивая плавный и эффективный обмен токенами Выберите провайдера + Чтобы узнать причину, посетите сайт провайдера + Операция не выполнена провайдером + Посетите сайт провайдера для проверки + Провайдер: требуется верификация + Провайдер Лучший курс Требуется разрешение Информация ниже не является обязательной. Вы можете стереть её, если хотите. @@ -266,7 +271,7 @@ Введенные коды доступа не совпадают Вы добавили одну резервную карту. После того, как процесс будет завершен, Вы больше не сможете добавить карт. Если у Вас есть еще одна карта, добавьте ее в резервную копию. Хотите продолжить? Процесс резервного копирования почти завершен. Вы не можете выйти из него сейчас. - Баланс + Баланс Добавить резервную карту Сканировать карту #%d Создать резервную копию diff --git a/core/res/src/main/res/values-zh-rTW/strings.xml b/core/res/src/main/res/values-zh-rTW/strings.xml index b80f9ce410..0b9c4e90a9 100644 --- a/core/res/src/main/res/values-zh-rTW/strings.xml +++ b/core/res/src/main/res/values-zh-rTW/strings.xml @@ -178,7 +178,7 @@ 輸入的訪問密碼與初始訪問密碼不匹配 您已添加一張備用卡。備份過程完成後,您將無法添加更多備份卡。如果您還有一張卡,請將其添加到備份中。您想繼續備份過程嗎? 備份過程已部分完成。你現在不能退出 - 餘額 + 餘額 添加備用卡 掃描卡片 #%d 立即備份 diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 6bd6a2445a..e88c410260 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -192,7 +192,30 @@ Unavailable for swap from %s Providers facilitate transactions, ensuring smooth and efficient token exchanges Choose Provider - Best Rate + Estimated amount + Visit provider’s website to see why + Operation failed by provider + Visit provider’s website for verification + Provider: Verification needed + Confirmed + Confirming + Exchanged + Exchanging + Failed + Deposit received + Awaiting deposit + Sending to you + Sent + Provider-sourced data. Estimated amount subject to change. + Exchange status + Verified + Verification required + Fetching best rates... + Floating rate + Go to provider + Provider + Best rate + Exchange by %s Permission Needed The following information is optional. You can erase it if you don\'t want to share it. Tell us what functions you are missing, and we will try to help you. @@ -267,7 +290,7 @@ Entered access code didn\'t match the initial access code You\'ve added one backup card. When backup process is finished you can\'t add more backup cards. If you have one more card, add it to backup. Do you like to continue the backup process? The backup process is partly complete. You can\'t exit it now. - Balance + Balance Add a backup card Scan the card #%d Backup now diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheet.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheet.kt index 2d9d85b164..5278a24911 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheet.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheet.kt @@ -6,6 +6,7 @@ import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.SheetState import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.* +import androidx.compose.ui.graphics.Color import com.tangem.core.ui.res.TangemTheme import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch @@ -20,6 +21,7 @@ import kotlinx.coroutines.launch @Composable inline fun TangemBottomSheet( config: TangemBottomSheetConfig, + color: Color = TangemTheme.colors.background.primary, crossinline content: @Composable ColumnScope.(T) -> Unit, ) { var isVisible by remember { mutableStateOf(value = config.isShow) } @@ -29,9 +31,9 @@ inline fun TangemBottomSheet( ModalBottomSheet( onDismissRequest = config.onDismissRequest, sheetState = sheetState, - containerColor = TangemTheme.colors.background.primary, + containerColor = color, shape = TangemTheme.shapes.bottomSheetLarge, - dragHandle = { TangemBottomSheetDraggableHeader() }, + dragHandle = { TangemBottomSheetDraggableHeader(color) }, ) { content(config.content) } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowApprox.kt b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowApprox.kt new file mode 100644 index 0000000000..3b085833a3 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowApprox.kt @@ -0,0 +1,150 @@ +package com.tangem.core.ui.components.inputrow + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +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.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import com.tangem.core.ui.R +import com.tangem.core.ui.components.currency.tokenicon.TokenIcon +import com.tangem.core.ui.components.currency.tokenicon.TokenIconState +import com.tangem.core.ui.components.inputrow.inner.DividerContainer +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.res.TangemTheme + +/** + * [Input Row Approx](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=2207-810&mode=design&t=fM1ZU6zQF6g3CaTv-4) + * + * @param leftIcon left token state + * @param leftTitle left token title + * @param leftSubtitle left token subtitle + * @param rightIcon right token state + * @param rightTitle right token title + * @param rightSubtitle right token subtitle + * @param modifier composable modifier + * @param showDivider show divider + */ +@Suppress("LongParameterList") +@Composable +fun InputRowApprox( + leftIcon: TokenIconState, + leftTitle: TextReference, + leftSubtitle: TextReference, + rightIcon: TokenIconState, + rightTitle: TextReference, + rightSubtitle: TextReference, + modifier: Modifier = Modifier, + showDivider: Boolean = false, +) { + DividerContainer( + showDivider = showDivider, + modifier = modifier, + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier + .padding(TangemTheme.dimens.spacing12) + .fillMaxWidth(), + ) { + InputRowApproxItem( + iconState = leftIcon, + title = leftTitle, + subtitle = leftSubtitle, + ) + Icon( + painter = painterResource(id = R.drawable.ic_approx_24), + contentDescription = null, + tint = TangemTheme.colors.text.tertiary, + modifier = Modifier + .padding( + horizontal = TangemTheme.dimens.spacing8, + vertical = TangemTheme.dimens.spacing10, + ), + ) + InputRowApproxItem( + iconState = rightIcon, + title = rightTitle, + subtitle = rightSubtitle, + ) + } + } +} + +@Composable +private fun InputRowApproxItem( + iconState: TokenIconState, + title: TextReference, + subtitle: TextReference, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier, + ) { + TokenIcon( + state = iconState, + modifier = Modifier + .size(TangemTheme.dimens.size36), + ) + Column( + modifier = Modifier + .padding( + start = TangemTheme.dimens.spacing12, + ), + ) { + Text( + text = title.resolveReference(), + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.primary1, + ) + Text( + text = subtitle.resolveReference(), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + modifier = Modifier + .padding(top = TangemTheme.dimens.spacing2), + ) + } + } +} + +//region Preview +@Preview +@Composable +private fun InputRowApproxPreview_Light() { + TangemTheme { + InputRowApprox( + leftIcon = TokenIconState.Loading, + leftTitle = TextReference.Str("Left title"), + leftSubtitle = TextReference.Str("Left subtitle"), + rightIcon = TokenIconState.Loading, + rightTitle = TextReference.Str("Right title"), + rightSubtitle = TextReference.Str("Right subtitle"), + modifier = Modifier + .background(TangemTheme.colors.background.action), + ) + } +} + +@Preview +@Composable +private fun InputRowApproxPreview_Dark() { + TangemTheme(isDark = true) { + InputRowApprox( + leftIcon = TokenIconState.Loading, + leftTitle = TextReference.Str("Left title"), + leftSubtitle = TextReference.Str("Left subtitle"), + rightIcon = TokenIconState.Loading, + rightTitle = TextReference.Str("Right title"), + rightSubtitle = TextReference.Str("Right subtitle"), + modifier = Modifier + .background(TangemTheme.colors.background.action), + ) + } +} +//endregion \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowBestRate.kt b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowBestRate.kt new file mode 100644 index 0000000000..620e3346cf --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowBestRate.kt @@ -0,0 +1,221 @@ +package com.tangem.core.ui.components.inputrow + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.ripple.rememberRipple +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import coil.compose.SubcomposeAsyncImage +import coil.request.ImageRequest +import com.tangem.core.ui.R +import com.tangem.core.ui.components.SpacerWMax +import com.tangem.core.ui.components.currency.tokenicon.LoadingIcon +import com.tangem.core.ui.components.inputrow.inner.DividerContainer +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.res.TangemTheme + +/** + * [Input Row Best Rate](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=2100-889&mode=dev) + * + * @param imageUrl image source url + * @param title title + * @param titleExtra title extra + * @param subtitle subtitle + * @param modifier composable modifier + * @param showTag show tag + * @param showDivider show divider + * @param onIconClick icon click + */ +@Composable +fun InputRowBestRate( + imageUrl: String, + title: TextReference, + titleExtra: TextReference, + subtitle: TextReference, + modifier: Modifier = Modifier, + showTag: Boolean = false, + showDivider: Boolean = false, + onIconClick: (() -> Unit)? = null, +) { + DividerContainer( + showDivider = showDivider, + modifier = modifier, + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .padding(TangemTheme.dimens.spacing12), + ) { + InnerIcon(imageUrl = imageUrl) + Column( + modifier = Modifier + .padding(start = TangemTheme.dimens.spacing12), + ) { + InnerTitle( + title = title, + titleExtra = titleExtra, + showTag = showTag, + ) + Text( + text = subtitle.resolveReference(), + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.tertiary, + modifier = Modifier.padding(top = TangemTheme.dimens.spacing8), + ) + } + SpacerWMax() + onIconClick?.let { + Icon( + painter = painterResource(id = R.drawable.ic_chevron_right_24), + contentDescription = null, + tint = TangemTheme.colors.icon.informative, + modifier = Modifier + .padding(vertical = TangemTheme.dimens.spacing10) + .clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = rememberRipple(bounded = false), + onClick = onIconClick, + ), + ) + } + } + } +} + +@Composable +private fun InnerTitle(title: TextReference, titleExtra: TextReference, showTag: Boolean = false) { + Row { + Text( + text = title.resolveReference(), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.primary1, + ) + Text( + text = titleExtra.resolveReference(), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + modifier = Modifier + .padding(start = TangemTheme.dimens.spacing4), + ) + if (showTag) { + Text( + text = stringResource(R.string.express_provider_best_rate), + style = TangemTheme.typography.caption1, + color = TangemTheme.colors.icon.accent, + modifier = Modifier + .padding(start = TangemTheme.dimens.spacing4) + .background( + color = TangemTheme.colors.icon.accent.copy(alpha = 0.1f), + shape = RoundedCornerShape(TangemTheme.dimens.radius20), + ) + .padding(horizontal = TangemTheme.dimens.spacing6), + ) + } + } +} + +@Composable +private fun InnerIcon(imageUrl: String) { + SubcomposeAsyncImage( + modifier = Modifier.size(TangemTheme.dimens.size40), + model = ImageRequest.Builder(context = LocalContext.current) + .data(imageUrl) + .crossfade(enable = true) + .allowHardware(enable = false) + .build(), + loading = { LoadingIcon() }, + error = { + Box( + modifier = Modifier + .background( + color = TangemTheme.colors.background.tertiary, + shape = CircleShape, + ), + ) + }, + contentDescription = null, + ) +} + +//region preview +@Preview +@Composable +private fun InputRowBestRatePreview_Light( + @PreviewParameter(InputRowBestRatePreviewDataProvider::class) data: InputRowBestRatePreviewData, +) { + TangemTheme { + InputRowBestRate( + imageUrl = "", + title = data.title, + titleExtra = data.titleExtra, + subtitle = data.subtitle, + showTag = data.showTag, + onIconClick = data.iconClick, + modifier = Modifier + .background(TangemTheme.colors.background.action), + ) + } +} + +@Preview +@Composable +private fun InputRowBestRatePreview_Dark( + @PreviewParameter(InputRowBestRatePreviewDataProvider::class) data: InputRowBestRatePreviewData, +) { + TangemTheme(isDark = true) { + InputRowBestRate( + imageUrl = "", + title = data.title, + titleExtra = data.titleExtra, + subtitle = data.subtitle, + showTag = data.showTag, + onIconClick = data.iconClick, + modifier = Modifier + .background(TangemTheme.colors.background.action), + ) + } +} + +private data class InputRowBestRatePreviewData( + val title: TextReference, + val titleExtra: TextReference, + val showTag: Boolean, + val subtitle: TextReference, + val iconClick: (() -> Unit)?, +) + +private class InputRowBestRatePreviewDataProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + InputRowBestRatePreviewData( + title = TextReference.Str("1inch"), + titleExtra = TextReference.Str("DEX"), + subtitle = TextReference.Str("0,64554846 DAI ≈ 1 MATIC "), + showTag = true, + iconClick = {}, + ), + InputRowBestRatePreviewData( + title = TextReference.Str("ChangeNow"), + titleExtra = TextReference.Str("CEX"), + subtitle = TextReference.Str("0,64554846 DAI ≈ 1 MATIC "), + showTag = false, + iconClick = null, + ), + ) +} +//endregion \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt index 2fc319885d..e68dfd96a0 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt @@ -41,6 +41,7 @@ data class TangemDimens internal constructor( val size0: Dp = 0.dp, val size0_5: Dp = 0.5.dp, val size1: Dp = 1.dp, + val size1_5: Dp = 1.5.dp, val size2: Dp = 2.dp, val size4: Dp = 4.dp, val size5: Dp = 5.dp, diff --git a/core/ui/src/main/res/drawable/ic_alert_triangle_20.xml b/core/ui/src/main/res/drawable/ic_alert_triangle_20.xml new file mode 100644 index 0000000000..dfe1b75e71 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_alert_triangle_20.xml @@ -0,0 +1,10 @@ + + + diff --git a/core/ui/src/main/res/drawable/ic_approx_24.xml b/core/ui/src/main/res/drawable/ic_approx_24.xml new file mode 100644 index 0000000000..30eb78ec9d --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_approx_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/ui/src/main/res/drawable/ic_exclamation_24.xml b/core/ui/src/main/res/drawable/ic_exclamation_24.xml new file mode 100644 index 0000000000..d7d5bdca87 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_exclamation_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/core/ui/src/main/res/drawable/ic_forward_24.xml b/core/ui/src/main/res/drawable/ic_forward_24.xml new file mode 100644 index 0000000000..1a35b84bfb --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_forward_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/ExchangeStatus.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/ExchangeStatus.kt new file mode 100644 index 0000000000..a74e8fbb6a --- /dev/null +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/ExchangeStatus.kt @@ -0,0 +1,20 @@ +package com.tangem.feature.swap.domain.models.domain + +data class ExchangeStatusModel( + val providerId: Int, + val status: ExchangeStatus? = null, + val txId: String? = null, + val txUrl: String? = null, +) + +enum class ExchangeStatus { + New, + Waiting, + Confirming, + Verifying, + Exchanging, + Failed, + Sending, + Finished, + Refunded, +} \ No newline at end of file diff --git a/features/tokendetails/impl/build.gradle.kts b/features/tokendetails/impl/build.gradle.kts index bc5b2cda79..c19e3f3577 100644 --- a/features/tokendetails/impl/build.gradle.kts +++ b/features/tokendetails/impl/build.gradle.kts @@ -28,6 +28,7 @@ dependencies { implementation(deps.compose.ui) implementation(deps.compose.ui.tooling) implementation(deps.compose.ui.utils) + implementation(deps.compose.constraintLayout) implementation(deps.arrow.core) implementation(deps.jodatime) @@ -68,4 +69,5 @@ dependencies { /** Feature Apis */ implementation(projects.features.tokendetails.api) implementation(projects.features.send.api) + implementation(projects.features.swap.domain) } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/SwapTransactionsState.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/SwapTransactionsState.kt new file mode 100644 index 0000000000..8b4fb5c8a5 --- /dev/null +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/SwapTransactionsState.kt @@ -0,0 +1,31 @@ +package com.tangem.feature.tokendetails.presentation.tokendetails.state + +import com.tangem.core.ui.components.currency.tokenicon.TokenIconState +import com.tangem.feature.swap.domain.models.domain.ExchangeStatus +import kotlinx.collections.immutable.PersistentList +import java.math.BigDecimal + +internal data class SwapTransactionsState( + val txId: String, + val providerId: Int, + val txUrl: String? = null, + val rate: BigDecimal, + val timestamp: Long, + val status: PersistentList, + val activeStatus: ExchangeStatus?, + val toCryptoAmount: String, + val toFiatAmount: String, + val toCurrencyIcon: TokenIconState, + val fromCryptoAmount: String, + val fromFiatAmount: String, + val fromCurrencyIcon: TokenIconState, + val onClick: () -> Unit, + val onGoToProviderClick: () -> Unit, +) + +internal class ExchangeStatusState( + val status: ExchangeStatus, + val text: String, + val isActive: Boolean, + val isDone: Boolean, +) \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt index 8280f9aeab..f9911ae521 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt @@ -39,7 +39,7 @@ internal fun TokenDetailsBalanceBlock( start = TangemTheme.dimens.spacing12, end = TangemTheme.dimens.spacing12, ), - text = stringResource(id = R.string.onboarding_balance_title), + text = stringResource(id = R.string.common_balance_title), color = TangemTheme.colors.text.tertiary, style = TangemTheme.typography.subtitle2, maxLines = 1, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeEstimate.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeEstimate.kt new file mode 100644 index 0000000000..c33b4fc290 --- /dev/null +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeEstimate.kt @@ -0,0 +1,67 @@ +package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.exchange + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource +import com.tangem.core.ui.components.currency.tokenicon.TokenIconState +import com.tangem.core.ui.components.inputrow.InputRowApprox +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.features.tokendetails.impl.R + +@Suppress("LongParameterList") +@Composable +internal fun ExchangeEstimate( + timestamp: TextReference, + fromTokenIconState: TokenIconState, + toTokenIconState: TokenIconState, + fromCryptoAmount: TextReference, + toCryptoAmount: TextReference, + fromFiatAmount: TextReference, + toFiatAmount: TextReference, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .clip(TangemTheme.shapes.roundedCornersXMedium) + .background(TangemTheme.colors.background.action), + ) { + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .padding( + start = TangemTheme.dimens.spacing12, + end = TangemTheme.dimens.spacing12, + top = TangemTheme.dimens.spacing14, + bottom = TangemTheme.dimens.spacing2, + ), + ) { + Text( + text = stringResource(id = R.string.express_estimated_amount), + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.tertiary, + ) + Text( + text = timestamp.resolveReference(), + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.tertiary, + ) + } + InputRowApprox( + leftIcon = fromTokenIconState, + leftTitle = fromCryptoAmount, + leftSubtitle = fromFiatAmount, + rightIcon = toTokenIconState, + rightTitle = toCryptoAmount, + rightSubtitle = toFiatAmount, + ) + } +} \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeProvider.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeProvider.kt new file mode 100644 index 0000000000..26c7393a85 --- /dev/null +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeProvider.kt @@ -0,0 +1,41 @@ +package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.exchange + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource +import com.tangem.core.ui.R +import com.tangem.core.ui.components.inputrow.InputRowBestRate +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.res.TangemTheme + +@Composable +internal fun ExchangeProvider(providerName: TextReference, providerType: TextReference, imageUrl: String) { + Column( + modifier = Modifier + .clip(TangemTheme.shapes.roundedCornersXMedium) + .background(TangemTheme.colors.background.action), + ) { + Text( + text = stringResource(id = R.string.express_provider), + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.tertiary, + modifier = Modifier + .padding( + start = TangemTheme.dimens.spacing12, + end = TangemTheme.dimens.spacing12, + top = TangemTheme.dimens.spacing12, + ), + ) + InputRowBestRate( + imageUrl = imageUrl, + title = providerName, + titleExtra = providerType, + subtitle = TextReference.Res(R.string.express_floating_rate), + ) + } +} \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBlock.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBlock.kt new file mode 100644 index 0000000000..a583d1cb56 --- /dev/null +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBlock.kt @@ -0,0 +1,209 @@ +package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.exchange + +import androidx.annotation.DrawableRes +import androidx.compose.animation.AnimatedContent +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.CircularProgressIndicator +import androidx.compose.material.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.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import com.tangem.core.ui.components.SpacerWMax +import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.swap.domain.models.domain.ExchangeStatus +import com.tangem.feature.tokendetails.presentation.tokendetails.state.ExchangeStatusState +import com.tangem.features.tokendetails.impl.R +import kotlinx.collections.immutable.PersistentList + +@Composable +internal fun ExchangeStatusBlock( + status: PersistentList, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .clip(TangemTheme.shapes.roundedCornersXMedium) + .background(TangemTheme.colors.background.action) + .padding( + vertical = TangemTheme.dimens.spacing14, + horizontal = TangemTheme.dimens.spacing12, + ), + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .padding(bottom = TangemTheme.dimens.spacing16), + ) { + Text( + text = stringResource(id = R.string.common_balance_title), + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.tertiary, + ) + SpacerWMax() + Row( + modifier = Modifier.clickable { onClick() }, + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + painter = painterResource(id = R.drawable.ic_arrow_top_right_24), + contentDescription = null, + tint = TangemTheme.colors.icon.informative, + modifier = Modifier + .size(TangemTheme.dimens.spacing16) + .padding(end = TangemTheme.dimens.spacing2), + ) + Text( + text = stringResource(id = R.string.express_go_to_provider), + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.tertiary, + ) + } + } + + status.forEachIndexed { index, item -> + ExchangeStatusStep( + stepStatus = item, + isLast = index == status.lastIndex, + ) + } + } +} + +@Composable +private fun ExchangeStatusStep( + stepStatus: ExchangeStatusState, + modifier: Modifier = Modifier, + isLast: Boolean = false, +) { + Row(modifier = modifier) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + AnimatedContent( + targetState = stepStatus, + label = "Exchange Step Change Success", + modifier = Modifier + .size(TangemTheme.dimens.size20), + ) { + when { + it.status == ExchangeStatus.Failed && !it.isDone -> ExchangeStepWaringOrError( + iconRes = R.drawable.ic_close_24, + color = TangemTheme.colors.icon.warning, + ) + it.status == ExchangeStatus.Verifying && !it.isDone -> ExchangeStepWaringOrError( + iconRes = R.drawable.ic_exclamation_24, + color = TangemTheme.colors.icon.attention, + ) + it.isDone -> ExchangeStepSuccess() + it.isActive -> ExchangeStepInProgress() + else -> ExchangeStepDefault() + } + } + if (!isLast) { + ExchangeStepSeparator() + } + } + ExchangeStatusStepText(stepStatus) + } +} + +@Composable +private fun ExchangeStatusStepText(stepStatus: ExchangeStatusState) { + val textColor = when { + stepStatus.status == ExchangeStatus.Failed && !stepStatus.isDone -> TangemTheme.colors.icon.warning + stepStatus.status == ExchangeStatus.Verifying && !stepStatus.isDone -> TangemTheme.colors.icon.attention + stepStatus.isDone -> TangemTheme.colors.text.primary1 + !stepStatus.isActive -> TangemTheme.colors.text.disabled + else -> TangemTheme.colors.text.primary1 + } + + Text( + text = stepStatus.text, + style = TangemTheme.typography.body2, + color = textColor, + modifier = Modifier + .padding(start = TangemTheme.dimens.spacing12), + ) +} + +@Composable +private fun ExchangeStepDefault() { + Box( + modifier = Modifier + .border( + width = TangemTheme.dimens.size1_5, + color = TangemTheme.colors.field.focused, + shape = CircleShape, + ) + .padding(TangemTheme.dimens.spacing2), + ) +} + +@Composable +private fun ExchangeStepSuccess() { + Icon( + painter = painterResource(id = R.drawable.ic_check_24), + contentDescription = null, + tint = TangemTheme.colors.icon.primary1, + modifier = Modifier + .border( + width = TangemTheme.dimens.size1_5, + color = TangemTheme.colors.field.focused, + shape = CircleShape, + ) + .padding(TangemTheme.dimens.spacing2), + ) +} + +@Composable +private fun ExchangeStepWaringOrError(color: Color, @DrawableRes iconRes: Int) { + Icon( + painter = painterResource(id = iconRes), + contentDescription = null, + tint = color, + modifier = Modifier + .border( + width = TangemTheme.dimens.size1_5, + color = color, + shape = CircleShape, + ) + .padding(TangemTheme.dimens.spacing2), + ) +} + +@Composable +private fun ExchangeStepInProgress() { + CircularProgressIndicator( + color = TangemTheme.colors.icon.primary1, + strokeWidth = TangemTheme.dimens.size2, + modifier = Modifier + .padding(TangemTheme.dimens.spacing2) + .size(TangemTheme.dimens.size14), + ) +} + +@Composable +private fun ExchangeStepSeparator() { + Box( + modifier = Modifier + .padding(vertical = TangemTheme.dimens.spacing2) + .size( + width = TangemTheme.dimens.size1_5, + height = TangemTheme.dimens.size10, + ) + .background( + color = TangemTheme.colors.field.focused, + shape = CircleShape, + ), + ) +} \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBottomSheet.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBottomSheet.kt new file mode 100644 index 0000000000..5705f00a63 --- /dev/null +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBottomSheet.kt @@ -0,0 +1,84 @@ +package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.exchange + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment.Companion.CenterHorizontally +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import com.tangem.core.ui.R +import com.tangem.core.ui.components.SpacerH10 +import com.tangem.core.ui.components.SpacerH12 +import com.tangem.core.ui.components.SpacerH16 +import com.tangem.core.ui.components.SpacerH24 +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.utils.toDateFormat +import com.tangem.core.ui.utils.toTimeFormat +import com.tangem.feature.tokendetails.presentation.tokendetails.state.SwapTransactionsState + +@Composable +internal fun ExchangeStatusBottomSheet(config: TangemBottomSheetConfig) { + TangemBottomSheet( + config = config, + color = TangemTheme.colors.background.tertiary, + ) { content: ExchangeStatusBottomSheetConfig -> + ExchangeStatusBottomSheetContent(content = content) + } +} + +@Composable +private fun ExchangeStatusBottomSheetContent(content: ExchangeStatusBottomSheetConfig) { + val config = content.value + Column( + modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16), + ) { + SpacerH10() + Text( + text = stringResource(id = R.string.express_exchange_status_title), + style = TangemTheme.typography.subtitle1, + color = TangemTheme.colors.text.primary1, + modifier = Modifier.align(CenterHorizontally), + ) + SpacerH10() + Text( + text = stringResource(id = R.string.express_exchange_status_subtitle), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.secondary, + modifier = Modifier + .align(CenterHorizontally), + ) + SpacerH16() + val timestamp = config.timestamp + ExchangeEstimate( + timestamp = TextReference.Str("${timestamp.toDateFormat()}, ${timestamp.toTimeFormat()}"), + fromTokenIconState = config.fromCurrencyIcon, + toTokenIconState = config.toCurrencyIcon, + fromCryptoAmount = TextReference.Str(config.fromCryptoAmount), + toCryptoAmount = TextReference.Str(config.toCryptoAmount), + fromFiatAmount = TextReference.Str(config.fromFiatAmount), + toFiatAmount = TextReference.Str(config.toFiatAmount), + ) + SpacerH12() + // todo replace with real provider data + ExchangeProvider( + providerName = TextReference.Str(config.providerId.toString()), + providerType = TextReference.Str("CEX"), + imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/changenow_512.png", + ) + SpacerH12() + ExchangeStatusBlock( + status = config.status, + onClick = config.onGoToProviderClick, + ) + SpacerH24() + } +} + +internal data class ExchangeStatusBottomSheetConfig( + val value: SwapTransactionsState, +) : TangemBottomSheetConfigContent \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusItems.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusItems.kt new file mode 100644 index 0000000000..95ac42480e --- /dev/null +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusItems.kt @@ -0,0 +1,180 @@ +package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.exchange + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.constraintlayout.compose.ConstraintLayout +import androidx.constraintlayout.compose.Visibility +import com.tangem.core.ui.components.currency.tokenicon.TokenIcon +import com.tangem.core.ui.components.currency.tokenicon.TokenIconState +import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.swap.domain.models.domain.ExchangeStatus +import com.tangem.feature.tokendetails.presentation.tokendetails.state.SwapTransactionsState +import com.tangem.features.tokendetails.impl.R +import kotlinx.collections.immutable.PersistentList + +@OptIn(ExperimentalFoundationApi::class) +internal fun LazyListScope.swapTransactionsItems( + swapTxs: PersistentList, + modifier: Modifier = Modifier, +) { + if (swapTxs.isNotEmpty()) { + items( + count = swapTxs.size, + key = { swapTxs[it].txId }, + contentType = { swapTxs[it]::class.java }, + ) { + val item = swapTxs[it] + + val (iconRes, tint) = when (item.activeStatus) { + ExchangeStatus.Verifying -> R.drawable.ic_alert_triangle_20 to TangemTheme.colors.icon.attention + ExchangeStatus.Failed -> R.drawable.ic_alert_circle_24 to TangemTheme.colors.icon.warning + else -> null to null + } + + ExchangeStatusItem( + providerName = item.providerId.toString(), + fromTokenIconState = item.fromCurrencyIcon, + toTokenIconState = item.toCurrencyIcon, + fromAmount = item.fromCryptoAmount, + toAmount = item.toCryptoAmount, + onClick = item.onClick, + infoIconRes = iconRes, + infoIconTint = tint, + modifier = modifier.animateItemPlacement(), + ) + } + } +} + +@Suppress("DestructuringDeclarationWithTooManyEntries", "LongMethod", "LongParameterList") +@Composable +private fun ExchangeStatusItem( + providerName: String, + fromTokenIconState: TokenIconState, + toTokenIconState: TokenIconState, + fromAmount: String, + toAmount: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + @DrawableRes infoIconRes: Int? = null, + infoIconTint: Color? = null, +) { + ConstraintLayout( + modifier = modifier + .fillMaxWidth() + .clip(TangemTheme.shapes.roundedCornersXMedium) + .background(TangemTheme.colors.background.action) + .clickable { onClick() } + .padding(TangemTheme.dimens.spacing12), + ) { + val (titleRef, iconRef, infoIconRef, swapIconRef, fromRef, toRef, fromIconRef, toIconRef) = createRefs() + val padding6 = TangemTheme.dimens.spacing6 + + Text( + text = stringResource(id = R.string.express_exchange_by, providerName), + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.tertiary, + modifier = Modifier.constrainAs(titleRef) { + start.linkTo(parent.start) + top.linkTo(parent.top) + }, + ) + TokenIcon( + state = fromTokenIconState, + shouldDisplayNetwork = false, + modifier = Modifier + .size(TangemTheme.dimens.size20) + .constrainAs(fromIconRef) { + start.linkTo(parent.start) + top.linkTo(titleRef.bottom, padding6) + bottom.linkTo(parent.bottom) + }, + ) + Text( + text = fromAmount, + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.primary1, + modifier = Modifier.constrainAs(fromRef) { + start.linkTo(fromIconRef.end, padding6) + top.linkTo(titleRef.bottom, padding6) + bottom.linkTo(parent.bottom) + }, + ) + Icon( + painter = painterResource(id = R.drawable.ic_forward_24), + contentDescription = null, + tint = TangemTheme.colors.icon.informative, + modifier = Modifier + .size(TangemTheme.dimens.size12) + .constrainAs(swapIconRef) { + start.linkTo(fromRef.end, padding6) + top.linkTo(titleRef.bottom, padding6) + bottom.linkTo(parent.bottom) + }, + ) + TokenIcon( + state = toTokenIconState, + shouldDisplayNetwork = false, + modifier = Modifier + .size(TangemTheme.dimens.size20) + .constrainAs(toIconRef) { + start.linkTo(swapIconRef.end, padding6) + top.linkTo(titleRef.bottom, padding6) + bottom.linkTo(parent.bottom) + }, + ) + Text( + text = toAmount, + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.primary1, + modifier = Modifier.constrainAs(toRef) { + start.linkTo(toIconRef.end, padding6) + top.linkTo(titleRef.bottom, padding6) + bottom.linkTo(parent.bottom) + }, + ) + Icon( + painter = painterResource(id = infoIconRes ?: R.drawable.ic_alert_triangle_20), + contentDescription = null, + tint = infoIconTint ?: TangemTheme.colors.icon.informative, + modifier = Modifier + .size(TangemTheme.dimens.size20) + .constrainAs(infoIconRef) { + top.linkTo(parent.top) + bottom.linkTo(parent.bottom) + end.linkTo(iconRef.start) + visibility = if (infoIconRes == null) { + Visibility.Gone + } else { + Visibility.Visible + } + }, + ) + Icon( + painter = painterResource(id = R.drawable.ic_chevron_right_24), + contentDescription = null, + tint = TangemTheme.colors.icon.informative, + modifier = Modifier + .size(TangemTheme.dimens.size24) + .constrainAs(iconRef) { + end.linkTo(parent.end) + top.linkTo(parent.top) + bottom.linkTo(parent.bottom) + }, + ) + } +} \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index b1496b2e0c..55cb6c48be 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -475,4 +475,35 @@ internal class TokenDetailsViewModel @Inject constructor( override fun onCloseRentInfoNotification() { uiState = stateFactory.getStateWithRemovedRentNotification() } -} \ No newline at end of file +} + +// +// val text = when (stepStatus.status) { +// ExchangeStatus.Failed -> stringResource(id = R.string.express_exchange_status_failed) +// ExchangeStatus.Verifying -> if (stepStatus.isDone) { +// stringResource(id = R.string.express_exchange_status_verified) +// } else { +// stringResource(id = R.string.express_exchange_status_verifying) +// } +// ExchangeStatus.New, ExchangeStatus.Waiting -> if (stepStatus.isDone) { +// stringResource(id = R.string.express_exchange_status_received) +// } else { +// stringResource(id = R.string.express_exchange_status_receiving) +// } +// ExchangeStatus.Confirming -> if (stepStatus.isDone) { +// stringResource(id = R.string.express_exchange_status_confirmed) +// } else { +// stringResource(id = R.string.express_exchange_status_confirming) +// } +// ExchangeStatus.Exchanging -> if (stepStatus.isDone) { +// stringResource(id = R.string.express_exchange_status_exchanged) +// } else { +// stringResource(id = R.string.express_exchange_status_exchanging) +// } +// ExchangeStatus.Sending -> if (stepStatus.isDone) { +// stringResource(id = R.string.express_exchange_status_sent) +// } else { +// stringResource(id = R.string.express_exchange_status_sending) +// } +// else -> "" +// } \ No newline at end of file