diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index 636d609568..8730764f58 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -47,6 +47,7 @@
Принять
Добавить
Применить
+ Одобрение
Внимание
Баланс: %s
биометрическую аутентификацию
@@ -74,7 +75,9 @@
Начать
Отправить
Успешно
+ Обмен
условия участия
+ Перевод
Я понял
Да
Адрес контракта скопирован!
@@ -386,7 +389,6 @@
Безлимитно
Открыть в обозревателе
В процессе
- Обмен
Обменять
Обмен %s на
Котировки включают дополнительную комиссию Tangem в размере %s. Это помогает нам предоставлять первоклассный продукт.
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 08f4e9e68b..f9ce322037 100644
--- a/core/res/src/main/res/values-zh-rTW/strings.xml
+++ b/core/res/src/main/res/values-zh-rTW/strings.xml
@@ -69,6 +69,7 @@
開始
提交
成功
+ 交換
條款和條件
我了解
是
@@ -367,7 +368,6 @@
要繼續,您需要允許 1inch 智能合約使用您的 %s
在瀏覽器中查看
進行中
- 交換
交易
交易 %s 至
此外,報價包括%s的 Tangem 費用。這有助於我們提供一流的產品
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index af04c2fd30..0c933df75f 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -45,6 +45,7 @@
Accept
Add
Apply
+ Approval
Attention
Balance: %s
biometric authentication
@@ -72,7 +73,9 @@
Start
Submit
Success
+ Swap
terms and conditions
+ Transfer
I understand
Yes
Contract address copied!
@@ -382,7 +385,6 @@
Unlimited
View in Explorer
In progress
- Swap
Swap
Swap of %s to
Quotes include an additional Tangem commission of %s. This helps us deliver a top-of-the-line product.
diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts
index 1dc31c8335..94bb016bdc 100644
--- a/core/ui/build.gradle.kts
+++ b/core/ui/build.gradle.kts
@@ -9,8 +9,10 @@ dependencies {
implementation(deps.androidx.fragment.ktx)
/** Compose */
+ implementation(deps.compose.constraintLayout)
implementation(deps.compose.foundation)
implementation(deps.compose.material)
+ implementation(deps.compose.material3)
implementation(deps.compose.ui.tooling)
/** Other libraries */
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt
new file mode 100644
index 0000000000..0f265ecce9
--- /dev/null
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt
@@ -0,0 +1,342 @@
+package com.tangem.core.ui.components.transactions
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Surface
+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.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.tooling.preview.PreviewParameter
+import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
+import androidx.constraintlayout.compose.ConstraintLayout
+import androidx.constraintlayout.compose.Dimension
+import com.tangem.core.ui.R
+import com.tangem.core.ui.components.CircleShimmer
+import com.tangem.core.ui.components.RectangleShimmer
+import com.tangem.core.ui.res.TangemTheme
+
+/**
+ * Transaction component
+ *
+ * @param state state
+ *
+ * @see Figma Component
+ *
+[REDACTED_AUTHOR]
+ */
+@Composable
+fun Transaction(state: TransactionState) {
+ Surface(
+ modifier = Modifier
+ .background(TangemTheme.colors.background.primary)
+ .defaultMinSize(minHeight = TangemTheme.dimens.size56)
+ .padding(horizontal = TangemTheme.dimens.spacing12, vertical = TangemTheme.dimens.spacing10),
+ color = TangemTheme.colors.background.primary,
+ ) {
+ @Suppress("DestructuringDeclarationWithTooManyEntries")
+ ConstraintLayout(modifier = Modifier.fillMaxWidth()) {
+ val (iconItem, titleItem, subtitleItem, amountItem, timestampItem) = createRefs()
+
+ Icon(
+ state = state,
+ modifier = Modifier
+ .size(TangemTheme.dimens.size40)
+ .constrainAs(iconItem) {
+ start.linkTo(parent.start)
+ centerVerticallyTo(parent)
+ },
+ )
+
+ Title(
+ state = state,
+ modifier = Modifier
+ .padding(horizontal = TangemTheme.dimens.spacing12)
+ .constrainAs(titleItem) {
+ start.linkTo(iconItem.end)
+ top.linkTo(iconItem.top)
+ },
+ )
+
+ Subtitle(
+ state = state,
+ modifier = Modifier
+ .padding(top = TangemTheme.dimens.spacing6)
+ .padding(horizontal = TangemTheme.dimens.spacing12)
+ .constrainAs(subtitleItem) {
+ start.linkTo(iconItem.end)
+ top.linkTo(amountItem.bottom)
+ end.linkTo(timestampItem.start)
+ width = Dimension.fillToConstraints
+ },
+ )
+
+ Amount(
+ state = state,
+ modifier = Modifier.constrainAs(amountItem) {
+ start.linkTo(titleItem.end)
+ top.linkTo(titleItem.top)
+ end.linkTo(parent.end)
+ width = Dimension.fillToConstraints
+ },
+ )
+
+ Timestamp(
+ state = state,
+ modifier = Modifier
+ .padding(top = TangemTheme.dimens.spacing6)
+ .constrainAs(timestampItem) {
+ top.linkTo(amountItem.bottom)
+ end.linkTo(parent.end)
+ },
+ )
+ }
+ }
+}
+
+@Composable
+private fun Icon(state: TransactionState, modifier: Modifier = Modifier) {
+ when (state) {
+ is TransactionState.Content -> {
+ Box(
+ modifier = modifier
+ .size(TangemTheme.dimens.size40)
+ .background(
+ color = when (state) {
+ is TransactionState.ProcessedTransactionContent -> {
+ TangemTheme.colors.icon.attention.copy(alpha = 0.1f)
+ }
+ is TransactionState.CompletedTransactionContent -> {
+ TangemTheme.colors.background.secondary
+ }
+ },
+ shape = CircleShape,
+ ),
+ ) {
+ Icon(
+ painter = painterResource(
+ id = when (state) {
+ is TransactionState.Sending,
+ is TransactionState.Send,
+ -> R.drawable.ic_arrow_up_24
+
+ is TransactionState.Receiving,
+ is TransactionState.Receive,
+ -> R.drawable.ic_arrow_down_24
+
+ is TransactionState.Approving,
+ is TransactionState.Approved,
+ -> R.drawable.ic_doc_24
+
+ is TransactionState.Swapping,
+ is TransactionState.Swapped,
+ -> R.drawable.ic_exchange_vertical_24
+ },
+ ),
+ contentDescription = null,
+ modifier = Modifier
+ .size(TangemTheme.dimens.size20)
+ .align(Alignment.Center),
+ tint = when (state) {
+ is TransactionState.ProcessedTransactionContent -> TangemTheme.colors.icon.attention
+ is TransactionState.CompletedTransactionContent -> TangemTheme.colors.icon.informative
+ },
+ )
+ }
+ }
+ is TransactionState.Loading -> {
+ CircleShimmer(modifier = modifier.size(TangemTheme.dimens.size40))
+ }
+ }
+}
+
+@Composable
+private fun Title(state: TransactionState, modifier: Modifier = Modifier) {
+ when (state) {
+ is TransactionState.ProcessedTransactionContent -> {
+ Row(modifier = modifier, horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing6)) {
+ Text(
+ text = stringResource(
+ id = when (state) {
+ is TransactionState.Sending -> R.string.common_transfer
+ is TransactionState.Receiving -> R.string.common_transfer
+ is TransactionState.Approving -> R.string.common_approval
+ is TransactionState.Swapping -> R.string.common_swap
+ },
+ ),
+ color = TangemTheme.colors.text.primary1,
+ style = TangemTheme.typography.subtitle2,
+ )
+
+ Image(
+ modifier = Modifier.align(Alignment.CenterVertically),
+ painter = painterResource(id = R.drawable.img_loader_15),
+ contentDescription = null,
+ )
+ }
+ }
+ is TransactionState.CompletedTransactionContent -> {
+ Text(
+ text = stringResource(
+ id = when (state) {
+ is TransactionState.Send -> R.string.common_transfer
+ is TransactionState.Receive -> R.string.common_transfer
+ is TransactionState.Approved -> R.string.common_approval
+ is TransactionState.Swapped -> R.string.common_swap
+ },
+ ),
+ modifier = modifier,
+ color = TangemTheme.colors.text.primary1,
+ style = TangemTheme.typography.subtitle2,
+ )
+ }
+ is TransactionState.Loading -> {
+ RectangleShimmer(
+ modifier = modifier.size(width = TangemTheme.dimens.size70, height = TangemTheme.dimens.size12),
+ )
+ }
+ }
+}
+
+@Composable
+private fun Subtitle(state: TransactionState, modifier: Modifier = Modifier) {
+ when (state) {
+ is TransactionState.Content -> {
+ Text(
+ text = when (state) {
+ is TransactionState.Sending -> "to: ${state.address}"
+ is TransactionState.Send -> "to: ${state.address}"
+ is TransactionState.Receiving -> "from: ${state.address}"
+ is TransactionState.Receive -> "from: ${state.address}"
+ is TransactionState.Approving -> "from: ${state.address}"
+ is TransactionState.Approved -> "from: ${state.address}"
+ is TransactionState.Swapping -> "contract: ${state.address}"
+ is TransactionState.Swapped -> "contract ${state.address}"
+ },
+ modifier = modifier,
+ textAlign = TextAlign.Start,
+ color = TangemTheme.colors.text.tertiary,
+ style = TangemTheme.typography.caption,
+ )
+ }
+ is TransactionState.Loading -> {
+ RectangleShimmer(
+ modifier = modifier.size(width = TangemTheme.dimens.size52, height = TangemTheme.dimens.size12),
+ )
+ }
+ }
+}
+
+@Composable
+private fun Amount(state: TransactionState, modifier: Modifier = Modifier) {
+ when (state) {
+ is TransactionState.Content -> {
+ Text(
+ text = state.amount,
+ modifier = modifier,
+ textAlign = TextAlign.End,
+ color = TangemTheme.colors.text.primary1,
+ style = TangemTheme.typography.body2,
+ )
+ }
+ is TransactionState.Loading -> {
+ RectangleShimmer(
+ modifier = modifier.size(width = TangemTheme.dimens.size40, height = TangemTheme.dimens.size12),
+ )
+ }
+ }
+}
+
+@Composable
+private fun Timestamp(state: TransactionState, modifier: Modifier = Modifier) {
+ when (state) {
+ is TransactionState.Content -> {
+ Text(
+ text = state.timestamp,
+ modifier = modifier,
+ textAlign = TextAlign.End,
+ color = TangemTheme.colors.text.tertiary,
+ style = TangemTheme.typography.caption,
+ )
+ }
+ is TransactionState.Loading -> {
+ RectangleShimmer(
+ modifier = modifier.size(width = TangemTheme.dimens.size40, height = TangemTheme.dimens.size12),
+ )
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun Preview_TransactionItem_LightTheme(
+ @PreviewParameter(TransactionItemStateProvider::class) state: TransactionState,
+) {
+ TangemTheme(isDark = false) {
+ Transaction(state)
+ }
+}
+
+@Preview
+@Composable
+private fun Preview_TransactionItem_DarkTheme(
+ @PreviewParameter(TransactionItemStateProvider::class) state: TransactionState,
+) {
+ TangemTheme(isDark = true) {
+ Transaction(state)
+ }
+}
+
+private class TransactionItemStateProvider : CollectionPreviewParameterProvider(
+ collection = listOf(
+ TransactionState.Sending(
+ address = "33BddS...ga2B",
+ amount = "-0.500913 BTC",
+ timestamp = "8:41",
+ ),
+ TransactionState.Receiving(
+ address = "33BddS...ga2B",
+ amount = "+0.500913 BTC",
+ timestamp = "8:41",
+ ),
+ TransactionState.Approving(
+ address = "33BddS...ga2B",
+ amount = "+0.500913 BTC",
+ timestamp = "8:41",
+ ),
+ TransactionState.Swapping(
+ address = "33BddS...ga2B",
+ amount = "+0.500913 BTC",
+ timestamp = "8:41",
+ ),
+ TransactionState.Send(
+ address = "33BddS...ga2B",
+ amount = "-0.500913 BTC",
+ timestamp = "8:41",
+ ),
+ TransactionState.Receive(
+ address = "33BddS...ga2B",
+ amount = "+0.500913 BTC",
+ timestamp = "8:41",
+ ),
+ TransactionState.Approved(
+ address = "33BddS...ga2B",
+ amount = "+0.500913 BTC",
+ timestamp = "8:41",
+ ),
+ TransactionState.Swapped(
+ address = "33BddS...ga2B",
+ amount = "+0.500913 BTC",
+ timestamp = "8:41",
+ ),
+ TransactionState.Loading,
+ ),
+)
\ No newline at end of file
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionState.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionState.kt
new file mode 100644
index 0000000000..f0502a4a21
--- /dev/null
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionState.kt
@@ -0,0 +1,155 @@
+package com.tangem.core.ui.components.transactions
+
+/**
+ * Transaction component state
+ *
+[REDACTED_AUTHOR]
+ */
+sealed interface TransactionState {
+
+ /**
+ * Content state
+ *
+ * @property address address
+ * @property amount amount
+ * @property timestamp timestamp
+ */
+ sealed class Content(
+ open val address: String,
+ open val amount: String,
+ open val timestamp: String,
+ ) : TransactionState
+
+ /**
+ * Content state for processed transaction
+ *
+ * @property address address
+ * @property amount amount
+ * @property timestamp timestamp
+ */
+ sealed class ProcessedTransactionContent(
+ override val address: String,
+ override val amount: String,
+ override val timestamp: String,
+ ) : Content(address, amount, timestamp)
+
+ /**
+ * Content state for completed transaction
+ *
+ * @property address address
+ * @property amount amount
+ * @property timestamp timestamp
+ */
+ sealed class CompletedTransactionContent(
+ override val address: String,
+ override val amount: String,
+ override val timestamp: String,
+ ) : Content(address, amount, timestamp)
+
+ /**
+ * Processed sending transaction state
+ *
+ * @property address address
+ * @property amount amount
+ * @property timestamp timestamp
+ */
+ data class Sending(
+ override val address: String,
+ override val amount: String,
+ override val timestamp: String,
+ ) : ProcessedTransactionContent(address, amount, timestamp)
+
+ /**
+ * Processed receiving transaction state
+ *
+ * @property address address
+ * @property amount amount
+ * @property timestamp timestamp
+ */
+ data class Receiving(
+ override val address: String,
+ override val amount: String,
+ override val timestamp: String,
+ ) : ProcessedTransactionContent(address, amount, timestamp)
+
+ /**
+ * Processed approving transaction state
+ *
+ * @property address address
+ * @property amount amount
+ * @property timestamp timestamp
+ */
+ data class Approving(
+ override val address: String,
+ override val amount: String,
+ override val timestamp: String,
+ ) : ProcessedTransactionContent(address, amount, timestamp)
+
+ /**
+ * Processed swapping transaction state
+ *
+ * @property address address
+ * @property amount amount
+ * @property timestamp timestamp
+ */
+ data class Swapping(
+ override val address: String,
+ override val amount: String,
+ override val timestamp: String,
+ ) : ProcessedTransactionContent(address, amount, timestamp)
+
+ /**
+ * Completed sending transaction state
+ *
+ * @property address address
+ * @property amount amount
+ * @property timestamp timestamp
+ */
+ data class Send(
+ override val address: String,
+ override val amount: String,
+ override val timestamp: String,
+ ) : CompletedTransactionContent(address, amount, timestamp)
+
+ /**
+ * Completed receiving transaction state
+ *
+ * @property address address
+ * @property amount amount
+ * @property timestamp timestamp
+ */
+ data class Receive(
+ override val address: String,
+ override val amount: String,
+ override val timestamp: String,
+ ) : CompletedTransactionContent(address, amount, timestamp)
+
+ /**
+ * Completed approving transaction state
+ *
+ * @property address address
+ * @property amount amount
+ * @property timestamp timestamp
+ */
+ data class Approved(
+ override val address: String,
+ override val amount: String,
+ override val timestamp: String,
+ ) : CompletedTransactionContent(address, amount, timestamp)
+
+ /**
+ * Completed swapping transaction state
+ *
+ * @property address address
+ * @property amount amount
+ * @property timestamp timestamp
+ */
+ data class Swapped(
+ override val address: String,
+ override val amount: String,
+ override val timestamp: String,
+ ) : CompletedTransactionContent(address, amount, timestamp)
+
+ /** Loading state */
+ object Loading : TransactionState
+}
\ 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 c2fbac9891..421c82c9e1 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
@@ -56,9 +56,11 @@ data class TangemDimens internal constructor(
val size46: Dp = 46.dp,
val size48: Dp = 48.dp,
val size50: Dp = 50.dp,
+ val size52: Dp = 52.dp,
val size56: Dp = 56.dp,
val size62: Dp = 62.dp,
val size68: Dp = 68.dp,
+ val size70: Dp = 70.dp,
val size72: Dp = 72.dp,
val size80: Dp = 80.dp,
val size84: Dp = 84.dp,
diff --git a/core/ui/src/main/res/drawable/ic_doc_24.xml b/core/ui/src/main/res/drawable/ic_doc_24.xml
new file mode 100644
index 0000000000..8855153e59
--- /dev/null
+++ b/core/ui/src/main/res/drawable/ic_doc_24.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt
index f5a39df29e..acad315b28 100644
--- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt
+++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt
@@ -54,7 +54,7 @@ internal fun SwapScreenContent(state: SwapStateHolder, onPermissionWarningClick:
Column {
AppBarWithBackButton(
- text = stringResource(R.string.swapping_swap),
+ text = stringResource(R.string.common_swap),
onBackClick = state.onBackClicked,
iconRes = R.drawable.ic_close_24,
)
@@ -348,7 +348,7 @@ private fun MainButton(state: SwapStateHolder, onPermissionWarningClick: () -> U
else -> {
PrimaryButtonIconEnd(
modifier = Modifier.fillMaxWidth(),
- text = stringResource(id = R.string.swapping_swap),
+ text = stringResource(id = R.string.common_swap),
iconResId = R.drawable.ic_tangem_24,
enabled = state.swapButton.enabled,
showProgress = state.swapButton.loading,
@@ -384,7 +384,7 @@ private val receiveCard = SwapCardData(
coinId = "",
)
-val stateSelectable = SelectableItemsState(
+val stateSelectable = SelectableItemsState(
selectedItem = Item(
0,
TextReference.Str("Balance"),
diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSuccessScreen.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSuccessScreen.kt
index d8ad3e646a..2d6f3e52b4 100644
--- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSuccessScreen.kt
+++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSuccessScreen.kt
@@ -39,7 +39,7 @@ fun SwapSuccessScreen(state: SwapSuccessStateHolder, onBack: () -> Unit) {
},
topBar = {
AppBarWithBackButton(
- text = stringResource(R.string.swapping_swap),
+ text = stringResource(R.string.common_swap),
onBackClick = onBack,
iconRes = R.drawable.ic_close_24,
)