Updated on 2026-08-14
This commit is contained in:
parent
464f8f8a80
commit
d10cf0ac2e
23 changed files with 1117 additions and 10 deletions
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -192,6 +192,11 @@
|
|||
<string name="exchange_tokens_unavailable_tokens_header">Недоступен для обмена с %s</string>
|
||||
<string name="express_choose_providers_subtitle">Провайдеры проводят транзакции, обеспечивая плавный и эффективный обмен токенами</string>
|
||||
<string name="express_choose_providers_title">Выберите провайдера</string>
|
||||
<string name="express_exchange_notification_failed_text">Чтобы узнать причину, посетите сайт провайдера</string>
|
||||
<string name="express_exchange_notification_failed_title">Операция не выполнена провайдером</string>
|
||||
<string name="express_exchange_notification_verification_text">Посетите сайт провайдера для проверки</string>
|
||||
<string name="express_exchange_notification_verification_title">Провайдер: требуется верификация</string>
|
||||
<string name="express_provider">Провайдер</string>
|
||||
<string name="express_provider_best_rate">Лучший курс</string>
|
||||
<string name="express_provider_permission_needed">Требуется разрешение</string>
|
||||
<string name="feedback_data_collection_message">Информация ниже не является обязательной. Вы можете стереть её, если хотите.</string>
|
||||
|
|
@ -266,7 +271,7 @@
|
|||
<string name="onboarding_access_codes_doesnt_match">Введенные коды доступа не совпадают</string>
|
||||
<string name="onboarding_alert_message_not_max_backup_cards_added">Вы добавили одну резервную карту. После того, как процесс будет завершен, Вы больше не сможете добавить карт. Если у Вас есть еще одна карта, добавьте ее в резервную копию. Хотите продолжить?</string>
|
||||
<string name="onboarding_backup_exit_warning">Процесс резервного копирования почти завершен. Вы не можете выйти из него сейчас.</string>
|
||||
<string name="onboarding_balance_title">Баланс</string>
|
||||
<string name="common_balance_title">Баланс</string>
|
||||
<string name="onboarding_button_add_backup_card">Добавить резервную карту</string>
|
||||
<string name="onboarding_button_backup_card_format">Сканировать карту #%d</string>
|
||||
<string name="onboarding_button_backup_now">Создать резервную копию</string>
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@
|
|||
<string name="onboarding_access_codes_doesnt_match">輸入的訪問密碼與初始訪問密碼不匹配</string>
|
||||
<string name="onboarding_alert_message_not_max_backup_cards_added">您已添加一張備用卡。備份過程完成後,您將無法添加更多備份卡。如果您還有一張卡,請將其添加到備份中。您想繼續備份過程嗎?</string>
|
||||
<string name="onboarding_backup_exit_warning">備份過程已部分完成。你現在不能退出</string>
|
||||
<string name="onboarding_balance_title">餘額</string>
|
||||
<string name="common_balance_title">餘額</string>
|
||||
<string name="onboarding_button_add_backup_card">添加備用卡</string>
|
||||
<string name="onboarding_button_backup_card_format">掃描卡片 #%d</string>
|
||||
<string name="onboarding_button_backup_now">立即備份</string>
|
||||
|
|
|
|||
|
|
@ -192,7 +192,30 @@
|
|||
<string name="exchange_tokens_unavailable_tokens_header">Unavailable for swap from %s</string>
|
||||
<string name="express_choose_providers_subtitle">Providers facilitate transactions, ensuring smooth and efficient token exchanges</string>
|
||||
<string name="express_choose_providers_title">Choose Provider</string>
|
||||
<string name="express_provider_best_rate">Best Rate</string>
|
||||
<string name="express_estimated_amount">Estimated amount</string>
|
||||
<string name="express_exchange_notification_failed_text">Visit provider’s website to see why</string>
|
||||
<string name="express_exchange_notification_failed_title">Operation failed by provider</string>
|
||||
<string name="express_exchange_notification_verification_text">Visit provider’s website for verification</string>
|
||||
<string name="express_exchange_notification_verification_title">Provider: Verification needed</string>
|
||||
<string name="express_exchange_status_confirmed">Confirmed</string>
|
||||
<string name="express_exchange_status_confirming">Confirming</string>
|
||||
<string name="express_exchange_status_exchanged">Exchanged</string>
|
||||
<string name="express_exchange_status_exchanging">Exchanging</string>
|
||||
<string name="express_exchange_status_failed">Failed</string>
|
||||
<string name="express_exchange_status_received">Deposit received</string>
|
||||
<string name="express_exchange_status_receiving">Awaiting deposit</string>
|
||||
<string name="express_exchange_status_sending">Sending to you</string>
|
||||
<string name="express_exchange_status_sent">Sent</string>
|
||||
<string name="express_exchange_status_subtitle">Provider-sourced data. Estimated amount subject to change.</string>
|
||||
<string name="express_exchange_status_title">Exchange status</string>
|
||||
<string name="express_exchange_status_verified">Verified</string>
|
||||
<string name="express_exchange_status_verifying">Verification required</string>
|
||||
<string name="express_fetch_best_rates">Fetching best rates...</string>
|
||||
<string name="express_floating_rate">Floating rate</string>
|
||||
<string name="express_go_to_provider">Go to provider</string>
|
||||
<string name="express_provider">Provider</string>
|
||||
<string name="express_provider_best_rate">Best rate</string>
|
||||
<string name="express_exchange_by">Exchange by %s</string>
|
||||
<string name="express_provider_permission_needed">Permission Needed</string>
|
||||
<string name="feedback_data_collection_message">The following information is optional. You can erase it if you don\'t want to share it.</string>
|
||||
<string name="feedback_preface_rate_negative">Tell us what functions you are missing, and we will try to help you.</string>
|
||||
|
|
@ -267,7 +290,7 @@
|
|||
<string name="onboarding_access_codes_doesnt_match">Entered access code didn\'t match the initial access code</string>
|
||||
<string name="onboarding_alert_message_not_max_backup_cards_added">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?</string>
|
||||
<string name="onboarding_backup_exit_warning">The backup process is partly complete. You can\'t exit it now.</string>
|
||||
<string name="onboarding_balance_title">Balance</string>
|
||||
<string name="common_balance_title">Balance</string>
|
||||
<string name="onboarding_button_add_backup_card">Add a backup card</string>
|
||||
<string name="onboarding_button_backup_card_format">Scan the card #%d</string>
|
||||
<string name="onboarding_button_backup_now">Backup now</string>
|
||||
|
|
|
|||
|
|
@ -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 <reified T : TangemBottomSheetConfigContent> 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 <reified T : TangemBottomSheetConfigContent> 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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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<InputRowBestRatePreviewData> {
|
||||
override val values: Sequence<InputRowBestRatePreviewData>
|
||||
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
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
10
core/ui/src/main/res/drawable/ic_alert_triangle_20.xml
Normal file
10
core/ui/src/main/res/drawable/ic_alert_triangle_20.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="20dp"
|
||||
android:height="20dp"
|
||||
android:viewportWidth="20"
|
||||
android:viewportHeight="20">
|
||||
<path
|
||||
android:pathData="M9.135,2.328L1.702,15.165C1.316,15.832 1.797,16.667 2.568,16.667H17.432C18.202,16.667 18.684,15.832 18.298,15.165L10.865,2.328C10.48,1.663 9.52,1.663 9.135,2.328ZM10.833,11.667H9.167V6.667H10.833V11.667ZM10.833,15H9.167V13.333H10.833V15Z"
|
||||
android:fillColor="#FFB71B"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
9
core/ui/src/main/res/drawable/ic_approx_24.xml
Normal file
9
core/ui/src/main/res/drawable/ic_approx_24.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M14.153,11.13C12.544,11.13 11.142,9.695 9.731,9.695C8.625,9.695 7.825,10.153 7.25,10.798V9.076C7.816,8.457 8.652,8 9.847,8C11.456,8 12.867,9.435 14.269,9.435C15.366,9.435 16.175,8.978 16.75,8.332V10.054C16.184,10.673 15.348,11.13 14.153,11.13ZM14.153,16C12.544,16 11.142,14.565 9.731,14.565C8.625,14.565 7.825,15.022 7.25,15.668V13.946C7.816,13.327 8.652,12.87 9.847,12.87C11.456,12.87 12.867,14.305 14.269,14.305C15.366,14.305 16.175,13.856 16.75,13.202V14.924C16.184,15.543 15.348,16 14.153,16Z"
|
||||
android:fillColor="#909090"/>
|
||||
</vector>
|
||||
12
core/ui/src/main/res/drawable/ic_exclamation_24.xml
Normal file
12
core/ui/src/main/res/drawable/ic_exclamation_24.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M11,13H13V7H11V13Z"
|
||||
android:fillColor="#FFB71B"/>
|
||||
<path
|
||||
android:pathData="M11,17H13V15H11V17Z"
|
||||
android:fillColor="#FFB71B"/>
|
||||
</vector>
|
||||
9
core/ui/src/main/res/drawable/ic_forward_24.xml
Normal file
9
core/ui/src/main/res/drawable/ic_forward_24.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M4.16,11V13H16.16L10.66,18.5L12.08,19.92L20,12L12.08,4.08L10.66,5.5L16.16,11H4.16Z"
|
||||
android:fillColor="#1E1E1E"/>
|
||||
</vector>
|
||||
|
|
@ -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,
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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<ExchangeStatusState>,
|
||||
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,
|
||||
)
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ExchangeStatusState>,
|
||||
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,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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<SwapTransactionsState>,
|
||||
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)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -475,4 +475,35 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
override fun onCloseRentInfoNotification() {
|
||||
uiState = stateFactory.getStateWithRemovedRentNotification()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 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 -> ""
|
||||
// }
|
||||
Loading…
Add table
Add a link
Reference in a new issue