diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountBlockV2.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountBlockV2.kt
index 611e613b72..b9fa864b84 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountBlockV2.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountBlockV2.kt
@@ -4,7 +4,7 @@ import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
-import androidx.compose.material.Text
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -13,14 +13,14 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
-import com.tangem.common.ui.R
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.amountScreen.preview.AmountStatePreviewData
+import com.tangem.core.ui.components.SpacerWMax
import com.tangem.core.ui.components.currency.icon.CurrencyIcon
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.extensions.TextReference
+import com.tangem.core.ui.extensions.conditional
import com.tangem.core.ui.extensions.resolveReference
-import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
@@ -30,11 +30,11 @@ import com.tangem.core.ui.res.TangemThemePreview
@Composable
fun AmountBlockV2(
amountState: AmountState,
- currencyIconState: CurrencyIconState,
isClickDisabled: Boolean,
isEditingDisabled: Boolean,
- onClick: () -> Unit,
modifier: Modifier = Modifier,
+ onClick: (() -> Unit)? = null,
+ extraContent: @Composable () -> Unit = {},
) {
if (amountState !is AmountState.Data) return
val amount = amountState.amountTextField
@@ -48,7 +48,7 @@ fun AmountBlockV2(
val fiatAmount = amount.fiatAmount.value.format {
fiat(
- fiatCurrencySymbol = amount.fiatAmount.currencySymbol,
+ fiatCurrencySymbol = amountState.appCurrency.symbol,
fiatCurrencyCode = amountState.appCurrency.code,
)
}
@@ -58,33 +58,26 @@ fun AmountBlockV2(
} else {
cryptoAmount to fiatAmount
}
-
- val title = TextReference.Str(
- stringResourceSafe(
- R.string.send_from_wallet_name,
- amountState.title
- .resolveReference(),
- ),
- )
val currencyTitle = amount.cryptoAmount.currencySymbol
AmountBlockV2(
- title = title,
+ title = amountState.title,
balance = amountState.availableBalance,
currencyTitle = currencyTitle,
- currencyIconState = currencyIconState,
+ currencyIconState = amountState.tokenIconState,
firstAmount = firstAmount,
secondAmount = secondAmount,
isClickDisabled = isClickDisabled,
isEditingDisabled = isEditingDisabled,
onClick = onClick,
modifier = modifier,
+ extraContent = extraContent,
)
}
@Suppress("LongParameterList")
@Composable
-internal fun AmountBlockV2(
+private fun AmountBlockV2(
title: TextReference,
balance: TextReference,
currencyTitle: String,
@@ -93,14 +86,17 @@ internal fun AmountBlockV2(
secondAmount: String,
isClickDisabled: Boolean,
isEditingDisabled: Boolean,
- onClick: () -> Unit,
modifier: Modifier = Modifier,
+ onClick: (() -> Unit)? = null,
+ extraContent: @Composable () -> Unit = {},
) {
Column(
modifier = modifier
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(TangemTheme.colors.background.action)
- .clickable(enabled = !isClickDisabled && !isEditingDisabled, onClick = onClick)
+ .conditional(onClick != null) {
+ clickable(enabled = !isClickDisabled && !isEditingDisabled, onClick = onClick!!)
+ }
.padding(TangemTheme.dimens.spacing16),
) {
Row {
@@ -109,7 +105,7 @@ internal fun AmountBlockV2(
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.tertiary,
)
- Spacer(modifier = modifier.weight(1f))
+ SpacerWMax()
Text(
text = balance.resolveReference(),
style = TangemTheme.typography.body2,
@@ -130,11 +126,16 @@ internal fun AmountBlockV2(
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
)
- Text(
- text = secondAmount,
- style = TangemTheme.typography.body2,
- color = TangemTheme.colors.text.tertiary,
- )
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(4.dp),
+ ) {
+ Text(
+ text = secondAmount,
+ style = TangemTheme.typography.body2,
+ color = TangemTheme.colors.text.tertiary,
+ )
+ extraContent()
+ }
}
Column(
verticalArrangement = Arrangement.spacedBy(2.dp),
@@ -163,7 +164,6 @@ private fun AmountBlockPreview(@PreviewParameter(AmountBlockV2PreviewProvider::c
TangemThemePreview {
AmountBlockV2(
amountState = value,
- currencyIconState = CurrencyIconState.Empty(),
isClickDisabled = false,
isEditingDisabled = false,
onClick = {},
diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml
index e90c8e89e2..f369cd7218 100644
--- a/core/res/src/main/res/values-de/strings.xml
+++ b/core/res/src/main/res/values-de/strings.xml
@@ -574,13 +574,13 @@
Netzwerk nicht hinzugefügt
Nicht hinzugefügt
Nicht unterstützte NFT-Arten
- cNFTs und pNFTs werden noch nicht unterstützt
+ cNFTs und pNFTs werden derzeit nicht unterstützt. Bitte sende diese nicht an Deine Wallet.
NFT senden
Eigenschaften
Unbetitelte Sammlung
%1$d NFTs in der %2$d Sammlung
-
+ - %1$d NFT in der %2$d Sammlung
- %1$d NFTs in der %2$d Sammlung
Tippe hier, um das erste NFT zu erhalten
@@ -821,6 +821,7 @@
Überprüfe deine Netzwerkverbindung
Informationen zur Netzwerkgebühr nicht erreichbar
Von
+ Von %s
Grenzwert Gasgebühr
Dies ist die maximale Gasgebühr, die für den Abschluss einer Transaktion oder eines Vertrags ausgegeben wird. Ein Gaslimit verhindert unerwartete oder unbegrenzte Gebühren bei der Ausführung einer Transaktion.
Gaspreis
@@ -828,6 +829,7 @@
Max
Höchstbetrag
Gebühr bis zu
+ Memo: %s
Ungültiges Memo
Abdeckung der Netzgebühren
Nonce
@@ -878,6 +880,7 @@
Die Netzwerkgebühr wird durch die Nutzung von %1$s Energieträgern gedeckt.
Die Netzwerkgebühr wird durch den Verbrauch von %1$s Energie reduziert
inklusive einer Netzgebühr von %1$s
+ Zur Adresse
Die Transaktion wurde erfolgreich signiert und an den Blockchain-Knoten gesendet. Die Walletbilanz wird aktualisiert
%1$s ist ein Vermögenswert im Tron-Netzwerk. Um die Gebühr zu berechnen und eine Transaktion durchzuführen, musst du etwas Tron (TRX) auf deinem Konto einzahlen.
Der Betrag geht über die Bilanz hinaus
@@ -887,6 +890,7 @@
Die Gebühr geht über die Bilanz hinaus
Der Gesamtbetrag geht über die Bilanz hinaus
Sende uns ein Token, und wir konvertieren es unterwegs. Dein Empfänger erhält genau das, was er braucht – nahtlos.
+ Wird an den Empfänger gesendet
Senden mit Swap
Transaktion gesendet
Bereite das Scannen der Karte oder Ring vor, die du einrichten möchtest.
@@ -1316,7 +1320,7 @@
Wir haben einen unbekannten Fehler festgestellt.
Tangem unterstützt derzeit das erforderliches Netzwerk von %s nicht.
Nicht unterstützte Netzwerke
- Tangem unterstützt ein erforderliches Netzwerk um %s.
+ Tangem unterstützt ein erforderliches Netzwerk um %s
Verifizierte Domain
Falsche Karte oder falscher Ring in der App ausgewählt
Wir haben eine Art Problem
diff --git a/core/res/src/main/res/values-es/strings.xml b/core/res/src/main/res/values-es/strings.xml
index bc9fd8a0e8..46aea2ae3b 100644
--- a/core/res/src/main/res/values-es/strings.xml
+++ b/core/res/src/main/res/values-es/strings.xml
@@ -560,7 +560,7 @@
Colección sin título
%1$d NFT en %2$d colecciones
-
+ - %1$d NFT en %2$d coleccione
- %1$d NFTs en %2$d colecciones
Pulse aquí para recibir el primer NFT
diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml
index 5bd4e39551..b3b307cac4 100644
--- a/core/res/src/main/res/values-fr/strings.xml
+++ b/core/res/src/main/res/values-fr/strings.xml
@@ -561,7 +561,7 @@
Collection sans titre
%1$d NFT dans la collection %2$d
-
+ - %1$d NFT dans la collection %2$d
- %1$d NFT dans la collection %2$d
Appuyez ici pour recevoir le premier NFT
diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml
index 11cb1fb068..731aa8038e 100644
--- a/core/res/src/main/res/values-ja/strings.xml
+++ b/core/res/src/main/res/values-ja/strings.xml
@@ -1,5 +1,14 @@
+ アクセスコードがないとウォレットは保護されません。
+ とにかくスキップ
+ アクセスコードが設定されていません
+ アクセスコードを入力
+ 続行するには、以前に入力したコードを確認してください
+ アクセスコードを再入力
+ ウォレットのロックを解除するには、 %s桁のアクセスコードを設定します。
+ アクセスコードの作成
+ アクセスコード
トークンが見つかりませんか?メインページのマーケットセクションに移動し、ポートフォリオに追加して買付できるようにします。
トークンが見つかりませんか?メインページのマーケットセクションに移動し、ポートフォリオに追加して売却できるようにします。
売却
@@ -49,6 +58,17 @@
システムのデフォルト
テーマ
アプリ設定
+ ウォレットのバックアップが正常に完了しました。
+ バックアップが完了しました
+ シークレットリカバリーフレーズは、ウォレットへのアクセスと復元のために使用される%sのランダムな単語のセットです。
+ これらの単語は紛失した場合、復元できません。必ず安全な場所に保管してください。
+ 安全に保管してください
+ これらの%s個の単語をパスワードマネージャーなどの安全な場所に保存し、決して他の人と共有しないでください。
+ 復元は不可能です
+ リカバリーフレーズ
+ これらの%s語を順番に書き留めて、安全かつプライベートに保管してください。
+ ウォレットと、リカバリーフレーズのセキュリティとバックアップの全責任は、Tangemではなくユーザーにあります。
+ リカバリーフレーズ
残高を表示または非表示にするには、デバイスの画面を下向きにするか、設定でオフにしてください。
今後表示しない
わかりました
@@ -101,6 +121,7 @@
承認
承認
注意
+ バックアップ
残高: %s
残高
生体認証
@@ -117,10 +138,12 @@
請求
報酬を受け取る
閉じる
+ 近日公開
確認
Tangemサポートへ問い合わせる
Visaサポートへ問い合わせる
続ける
+ 変換する
コピー
アドレスをコピー
作成
@@ -150,6 +173,7 @@
遅い
速度と料金
終了
+ 無料
アドレスを同期する
プロバイダーへ移動
トークンへ移動
@@ -202,6 +226,7 @@
もっと見る
署名
署名して送信
+ スキップ
ステーキング
ステーキング
始める
@@ -222,6 +247,7 @@
アクセスできません
ステーキング解除
%1$sの制限により、1つのトランザクションに収まるUTXOは%2$d個のみです。つまり、 %3$s以下しか送信できません。量を減らす必要があります。
+ 値がコピーされました
週
で
はい
@@ -377,10 +403,24 @@
許可を与える
無制限
既存のウォレットを追加
+ 新しいウォレットを作成する
Tangemを注文
Tangemをスキャン
%sへ
%sネットワーク
+ iCloudバックアップに保存されている既存のウォレットを復元する
+ iCloudバックアップ
+ バックアップへ移動
+ アクセスコードを使用してウォレットを保護するには、まずバックアップを完了してください。
+ まずバックアップを完了する
+ バックアップなし
+ 暗号資産をオフラインで安全に保管する物理カード
+ リカバリーフレーズ
+ 受信取引の通知を受け取る
+ 鍵はアプリに保存されます
+ 最新の機能とニュースをお届けします
+ シードフレーズのバックアップ
+ モバイルウォレットを作成する
この情報はAIで生成されました。 \nエラーが見つかった場合は、ここをタップしてください。
アクセスコードを変更するには、上図のようにカードまたはリングをタップし、操作が終了するまで取り外さないでください。
パスコードを変更するには、上記のようにカードをタップし、操作が終了するまで取り外さないでください。
@@ -565,7 +605,7 @@
ネットワークが追加されていません
未追加
サポートされていないNFTタイプ
- cNFTとpNFTはまだサポートされていません
+ cNFTとpNFTはまだサポートされていません。ウォレットに送信しないでください。
NFTを送信する
特徴
無題のコレクション
@@ -621,6 +661,7 @@
秘密鍵を非公開で生成する
カードは有効化され、使用可能になりました
成功!
+ ウォレットの設定が完了し、使用できるようになりました。
この場合は、最初からやり直す必要があります。
アクティベーション処理を途中で終了しますか?
スタート
@@ -783,7 +824,9 @@
推奨手数料を下回る手数料が指定されたため、取引に遅れが生じる可能性があります。続行しますか?
理由: %1$s \nコード: %2$s
取引は完了していません
+ 別のトークンに変換する
金額
+ 受取人に送信されます
取引手数料は、vByteフィールドのSatoshiの値を調整して設定できます。
取引にかかる手数料です。自由に設定できます。
最大手数料
@@ -807,6 +850,7 @@
ネットワーク接続を確認してください
ネットワーク手数料についての情報にアクセスできません
より
+ %sから
ガス上限
これは、取引または契約を完了するために使用されるガスの最大額です。ガス上限を設定することで、取引実行時に予期せぬ請求や無制限の請求を防ぐことができます。
ガス代
@@ -814,6 +858,7 @@
最大
最大金額
最大手数料
+ メモ: %s
無効なメモ
ネットワーク手数料のカバー
ナンス
@@ -827,6 +872,7 @@
カスタム手数料が高くなっています
%1$sネットワークの特殊性により、残高全体を転送する場合の手数料は高くなります。手数料を削減するには、 %2$sを残します。
手数料が高くなっています
+ 受取人の口座が有効化されていません。最低送金額はレント免除残高%1$s以上である必要があります。
口座残高はレンタル料金より低くすることはできません。残高を少なくとも%1$sに維持するか、すべての資金を引き出してください。
手数料が送金金額を超えており、マイナスの値になってしまいます。
無効な金額
@@ -845,7 +891,7 @@
最近の取引
受取人
有効なアドレスではありません
- トークンを失わないように、受信ウォレットアドレスが%sネットワーク上にあることを確認してください。
+ トークンの損失を避けるために、受信ウォレットアドレスが** %sネットワーク**上にあることを確認してください
%s に送信
%sネットワークアドレスを確認してください。エラーが発生すると転送が失われる可能性があります。
送金先
@@ -864,6 +910,7 @@
ネットワーク手数料は%1$sエネルギーを使用してカバーされます
%1$sエネルギーを使用するとネットワーク手数料が減額されます
ネットワーク手数料%1$sを含む
+ アドレスへ
取引は正常に署名され、ブロックチェーンノードに送信されました。ウォレットの残高はしばらくして更新されます。
%1$sはTronネットワークのアセットです。手数料を計算して取引を行うには、アカウントにTron(TRX)を入金する必要があります。
金額が残高を超えています
@@ -873,6 +920,8 @@
手数料が残高を超えています
合計金額が残高を超えています
トークンを送信すれば、送信中に変換されます。受信者は必要なものをシームレスに受け取ります。
+ 受信者に送信されます
+ 受取金額
スワップして送信
取引が送信されました
設定したいカードまたはリングをスキャンするために準備してください。
@@ -1028,6 +1077,7 @@
破格のレート
手間がかからず直感的に操作でき、数回タップするだけでトークンを交換できます。
とにかく便利
+ プロバイダー経由のスワップ
この金額には以下が含まれます:\n- サービスプロバイダーの手数料\n- 取引所からユーザーのアドレスに%s を送り返すためのネットワーク手数料。
金額には以下が含まれます: \n • サービス プロバイダーの手数料\n • 取引所からユーザーのアドレスに%1$sを送金するためのネットワーク手数料。 \n\nプロバイダーのスリッページは最大%2$sです
この金額には、サービスプロバイダーの手数料が含まれています。
@@ -1178,6 +1228,29 @@
dAppsに接続する
WalletConnect
接続には数秒かかる場合があります
+ %sから
+ Tangemウォレットを購入 — 暗号資産をオフラインで安全に保管できる物理カードです。
+ ハードウェアウォレット
+ 数秒でスマートフォンに安全なウォレットが作成されます。
+ モバイルウォレット
+ 何を選ぶべき?
+ すでにTangemウォレットをお持ちですか?
+ 今すぐスキャン
+ ウォレットの作成方法を選択してください
+ Tangemウォレットを購入しますか?
+ 今すぐ購入
+ Googleドライブのバックアップに保存されている既存のウォレットを復元する
+ Googleドライブからインポート
+ 既存のウォレットを追加
+ 暗号資産をオフラインで安全に保管する物理カード。
+ Tangemウォレットをスキャンする
+ ウォレットを復元するためのシードフレーズ
+ ウォレットをインポート
+ リカバリーフレーズをインポートする
+ ウォレットのバックアップが正常に完了しました。
+ ウォレットをインポート
+ インポート完了
+ ウォレットをインポート
%s市場価格
直近24時間
%sネットワーク
@@ -1186,6 +1259,8 @@
今すぐ10 % オフで購入
1.3万種類以上の暗号資産にアクセス。ワンタップで買付、売却、スワップ、ステーキングが可能です。\nバックアップ用に最大3枚のカードを連携できます。
Tangemウォレットを見る
+ このウォレットを保護するためのシークレットコードです。ログインと署名に使用されます。
+ アクセスコードの設定 / 変更
ウォレットの受信取引とTangemの更新について通知を受け取る。
取引通知
ウォレット設定
@@ -1274,6 +1349,10 @@
下にスワイプして更新するか、後でもう一度お試しください。
一部のネットワークにアクセスできません
一部のトークン残高を更新できませんでした
+ トラストラインを有効にする
+ %sが足りません。このトークンを関連付けるには、XLMアカウントに資金を追加してください。
+ このトークンを受け取るには、トラストラインを有効にする必要があります。ネットワークには 0.5 XLMの準備金が必要です。
+ トラストラインが必要
これはテストネットカードです。取引処理はできませんので、テストおよび開発目的でのみご利用ください。
テスト目的のみ
残高が古い可能性があります。ページを更新してください。
@@ -1296,7 +1375,7 @@
不明なエラーが発生しました
Tangemは現在%sで必要なネットワークをサポートしていません。
サポートされていないネットワーク
- Tangemは%sで必要なネットワークをサポートします。
+ Tangemは%sで必要なネットワークをサポートします
検証済みドメイン
アプリで間違ったカードまたはリングが選択されました
問題が起きています
diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index 8ac257b580..4c4ce1336a 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -223,6 +223,7 @@
Недоступно
Завершить стейкинг
Из-за ограничений %1$s в одну транзакцию может поместиться только %2$d UTXO. Это означает, что вы можете отправить только %3$s или меньше. Вам нужно уменьшить сумму.
+ Значение скопировано
неделю
с
Да
@@ -843,6 +844,7 @@
Установлена высокая комиссия
Ввиду особенности сети %1$s комиссия при переводе всего баланса выше. Для того, чтобы снизить комиссию Вы можете оставить %2$s.
Комиссия повышена
+ Аккаунт получателя не активирован. Минимальная сумма перевода должна быть не меньше баланса, необходимого для покрытия арендной платы: %1$s.
Баланс вашего счета не может быть ниже арендной платы. Пожалуйста, оставьте на счете не менее %1$s или выведите все средства.
Включенная комиссия превышает сумму перевода, что приводит к отрицательному значению
Недопустимая сумма
@@ -1259,6 +1261,9 @@
Свайпните вниз для обновления или попробуйте позже.
Некоторые сети недоступны
Некоторые балансы токенов не удалось обновить
+ Открыть Trustline
+ Чтобы получить этот токен, необходимо открыть Trustline. Сеть удержит резерв в размере 0.5 XLM.
+ Откройте Trustline
Это Testnet карта. Он не может обрабатывать транзакции и используется только в целях тестирования и разработки.
Только для целей тестирования
Возможно, баланс устарел. Обновите страницу.
diff --git a/core/res/src/main/res/values-uk-rUA/strings.xml b/core/res/src/main/res/values-uk-rUA/strings.xml
index 314b79ecef..d1048dabe7 100644
--- a/core/res/src/main/res/values-uk-rUA/strings.xml
+++ b/core/res/src/main/res/values-uk-rUA/strings.xml
@@ -574,7 +574,7 @@
Риси
NFTs в %2$d колекціях%1$d
-
+ - %1$d NFT в %2$d колекціi
- %1$d NFTs в %2$d колекціях
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index 28efd231b8..9499629f9e 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -1,5 +1,14 @@
+ Your wallet won’t be protected without an Access Code.
+ Skip anyway
+ Access Code not set
+ Enter Access Code
+ Confirm your previously entered code to continue
+ Re-enter Access Code
+ Set a %s-digit Access Code to unlock your wallet.
+ Create Access Code
+ Access code
Can’t find your token? Go to the Market section on the main page and add it to your portfolio for purchase
Can’t find your token? Go to the Market section on the main page and add it to your portfolio for selling.
Sell
@@ -49,6 +58,17 @@
System default
Theme
App settings
+ You successfully backed up your wallet.
+ Backup Completed
+ Your Secret Recovery Phrase is a fixed set of %s random words used to access and recover your wallet.
+ These words can’t be recovered if lost. Make sure to keep it somewhere secure.
+ Keep It Safe
+ Save these %s words in a secure location, such as a password manager, and never share them with anyone.
+ No Recovery Possible
+ Recovery phrase
+ Write down these %s words in order and keep them safe and private
+ Full responsibility for the security and backup of the wallet and recovery phrase lies with the user, not with Tangem.
+ Recovery phrase
To hide or show your balances, simply flip your device screen down, or switch it off in Settings
Don\'t show again
Got it
@@ -103,6 +123,7 @@
Approval
Approve
Attention
+ Backup
Balance: %s
Balance
biometric authentication
@@ -124,6 +145,7 @@
Contact Tangem Support
Contact Visa Support
Continue
+ Convert
Copy
Copy address
Create
@@ -208,6 +230,7 @@
Show more
Sign
Sign and send
+ Skip
Stake
Staking
Start
@@ -229,6 +252,7 @@
Unreachable
Unstake
Due to %1$s limitations only %2$d UTXOs can fit in a single transaction. This means you can only send %3$s or less. You need to reduce the amount.
+ Value copied
week
with
Yes
@@ -596,7 +620,7 @@
Untitled collection
%1$d NFTs in %2$d collections
-
+ - %1$d NFT in %2$d collection
- %1$d NFTs in %2$d collections
Tap here to receive first NFT
@@ -647,6 +671,7 @@
Generate keys privately
Your card is activated and ready to be used
Success!
+ Your wallet is set up and ready to use!
In this case, you will need to start from the beginning.
Do you want to exit the activation process?
Getting started
@@ -813,7 +838,9 @@
You specified a commission below the recommended amount, which could cause a delay in your transaction. Continue?
Reason: %1$s\nCode: %2$s
The transaction is not completed
+ Convert to another token
Amount
+ Will be sent to recipient
You can set your transaction fee by adjusting the value in the Satoshi per vByte field.
The fee that will be charged for your transaction. You can set your own value.
Max fee
@@ -859,6 +886,7 @@
Custom fee is high
Due to the peculiarities of the %1$s network, the fee for transferring the entire balance is higher. To reduce the commission, you can leave %2$s.
The fee is higher
+ The recipient account is not activated. The minimum transfer amount must be equal to or greater than the rent-exempt balance: %1$s.
Your account balance cannot be lower than the rent fee. Please maintain at least %1$s on your account or withdraw all funds.
The included commission exceeds the transfer amount, leading to a negative value
Invalid amount
@@ -877,7 +905,7 @@
Recent
Recipient
Not a valid address
- Ensure the receiving wallet address is on the %s network to avoid losing your tokens
+ Ensure the receiving wallet address is on **the %s network** to avoid losing your tokens
send to %s
Ensure you %s network address, as errors may result in lost transfers
Send to
@@ -907,6 +935,7 @@
Total amount exceeds balance
Send any token, and we’ll convert it on the way. Your recipient gets exactly what they need—seamlessly.
Will be sent a recipient
+ Amount to receive
Send with swap
Transaction sent
Prepare to scan card or ring you want to set up.
@@ -1062,6 +1091,7 @@
Unbeatable Rates
Hassle-free and intuitive, allowing you to swap tokens in just a few taps
Simply Convenient
+ Swap via provider
The amount includes:\n• service provider\'s fee\n• network fee for sending %s from the exchange back to the user\'s address.
The amount includes:\n• service provider\'s fee\n• network fee for sending %1$s from the exchange back to the user\'s address. \n\nProvider slippage is up to %2$s
The amount includes the service provider\'s fee.
@@ -1292,6 +1322,8 @@
Get now with 10% off
Access 13,000+ cryptocurrencies. Buy, sell, swap, and stake with a single tap.\nLink up to three cards for a backup.
Discover Tangem Wallet
+ Secret code to protect this wallet. Used for login and signatures.
+ Set/Change Access Code
Stay notified on wallet incoming transactions and Tangem updates.
Transaction Notifications
Wallet settings
@@ -1381,6 +1413,10 @@
Swipe down to refresh or try again later.
Some networks are unreachable
Some token balances could not be updated
+ Enable Trustline
+ Not enough %s. Top up your XLM account to associate this token
+ A Trustline must be enabled to receive this token. The network requires a 0.5 XLM reserve.
+ Trustline Required
This is a Testnet card. It cannot process transactions and should only be used for testing and development purposes.
For testing purposes only
Balance may be outdated. Refresh the page.
@@ -1403,7 +1439,7 @@
We\'ve encountered unknown error
Tangem does not currently support a required network by %s.
Unsuported networks
- Tangem support a required network by %s.
+ Tangem support a required network by %s
Verified domain
Wrong card or ring selected in the App
We\'ve got some kind of problem
diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountBlockComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountBlockComponent.kt
index 7bf133c215..fe6e07f89a 100644
--- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountBlockComponent.kt
+++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountBlockComponent.kt
@@ -39,11 +39,8 @@ internal class SendAmountBlockComponent(
val isClickEnabled by params.blockClickEnableFlow.collectAsStateWithLifecycle()
if (params.isRedesignEnabled) {
- val amountState = state as? AmountState.Data ?: return
-
AmountBlockV2(
amountState = state,
- currencyIconState = amountState.tokenIconState,
isClickDisabled = !isClickEnabled,
isEditingDisabled = params.predefinedValues is PredefinedValues.Content.Deeplink,
onClick = onClick,
diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt
new file mode 100644
index 0000000000..f0f3fc46a4
--- /dev/null
+++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt
@@ -0,0 +1,104 @@
+package com.tangem.features.swap.v2.impl.amount
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.arkivanov.decompose.ComponentContext
+import com.arkivanov.decompose.extensions.compose.subscribeAsState
+import com.arkivanov.decompose.router.slot.activate
+import com.arkivanov.decompose.router.slot.childSlot
+import com.arkivanov.decompose.router.slot.dismiss
+import com.tangem.core.decompose.context.AppComponentContext
+import com.tangem.core.decompose.context.childByContext
+import com.tangem.core.decompose.model.getOrCreateModel
+import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
+import com.tangem.core.ui.decompose.ComposableContentComponent
+import com.tangem.domain.express.models.ExpressProvider
+import com.tangem.domain.models.currency.CryptoCurrency
+import com.tangem.features.swap.v2.impl.amount.SwapAmountComponentParams.AmountBlockParams
+import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
+import com.tangem.features.swap.v2.impl.amount.model.SwapAmountModel
+import com.tangem.features.swap.v2.impl.amount.ui.SwapAmountBlockContent
+import com.tangem.features.swap.v2.impl.chooseprovider.SwapChooseProviderComponent
+import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
+import kotlinx.collections.immutable.ImmutableList
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+
+internal class SwapAmountBlockComponent(
+ private val appComponentContext: AppComponentContext,
+ private val params: AmountBlockParams,
+ private val onResult: (SwapAmountUM) -> Unit,
+ val onClick: () -> Unit,
+) : ComposableContentComponent, AppComponentContext by appComponentContext {
+
+ private val model: SwapAmountModel = getOrCreateModel(params = params)
+
+ private val bottomSheetSlot = childSlot(
+ source = model.bottomSheetNavigation,
+ serializer = null,
+ handleBackButton = true,
+ childFactory = ::bottomSheetChild,
+ )
+
+ init {
+ model.uiState.onEach {
+ onResult(it)
+ }.launchIn(componentScope)
+ }
+
+ fun updateState(amountUM: SwapAmountUM) = model.updateState(amountUM)
+
+ @Composable
+ override fun Content(modifier: Modifier) {
+ val state by model.uiState.collectAsStateWithLifecycle()
+ val bottomSheet by bottomSheetSlot.subscribeAsState()
+
+ val isClickEnabled by params.blockClickEnableFlow.collectAsStateWithLifecycle()
+
+ SwapAmountBlockContent(
+ amountUM = state,
+ onInfoClick = model::onInfoClick,
+ isClickEnabled = isClickEnabled,
+ onClick = onClick,
+ onProviderSelectClick = {
+ val amountUM = model.uiState.value as? SwapAmountUM.Content ?: return@SwapAmountBlockContent
+ val selectedProvider = amountUM.selectedQuote.provider ?: return@SwapAmountBlockContent
+ val cryptoCurrency = params.secondaryCryptoCurrency ?: return@SwapAmountBlockContent
+
+ model.bottomSheetNavigation.activate(
+ SwapChooseProviderConfig(
+ providers = amountUM.swapQuotes,
+ cryptoCurrency = cryptoCurrency,
+ selectedProvider = selectedProvider,
+ ),
+ )
+ },
+ )
+
+ bottomSheet.child?.instance?.BottomSheet()
+ }
+
+ private fun bottomSheetChild(
+ config: SwapChooseProviderConfig,
+ componentContext: ComponentContext,
+ ): ComposableBottomSheetComponent {
+ return SwapChooseProviderComponent(
+ context = childByContext(componentContext),
+ params = SwapChooseProviderComponent.Params(
+ providers = config.providers,
+ cryptoCurrency = config.cryptoCurrency,
+ selectedProvider = config.selectedProvider,
+ callback = model,
+ onDismiss = { model.bottomSheetNavigation.dismiss() },
+ ),
+ )
+ }
+
+ data class SwapChooseProviderConfig(
+ val providers: ImmutableList,
+ val cryptoCurrency: CryptoCurrency,
+ val selectedProvider: ExpressProvider,
+ )
+}
\ No newline at end of file
diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponentParams.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponentParams.kt
index b482191a87..beb46d4941 100644
--- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponentParams.kt
+++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponentParams.kt
@@ -1,6 +1,5 @@
package com.tangem.features.swap.v2.impl.amount
-import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.swap.models.SwapDirection
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
@@ -15,19 +14,19 @@ internal sealed class SwapAmountComponentParams {
abstract val amountUM: SwapAmountUM
abstract val analyticsCategoryName: String
abstract val userWallet: UserWallet
- abstract val appCurrency: AppCurrency
abstract val swapDirection: SwapDirection
abstract val isBalanceHidingFlow: StateFlow
+ abstract val primaryCryptoCurrencyStatusFlow: StateFlow
+ abstract val secondaryCryptoCurrency: CryptoCurrency?
data class AmountParams(
override val amountUM: SwapAmountUM,
override val analyticsCategoryName: String,
override val userWallet: UserWallet,
override val isBalanceHidingFlow: StateFlow,
- override val appCurrency: AppCurrency,
override val swapDirection: SwapDirection,
- val primaryCryptoCurrency: CryptoCurrency,
- val secondaryCryptoCurrency: CryptoCurrency?,
+ override val primaryCryptoCurrencyStatusFlow: StateFlow,
+ override val secondaryCryptoCurrency: CryptoCurrency?,
val callback: SwapAmountComponent.ModelCallback,
val currentRoute: Flow,
) : SwapAmountComponentParams()
@@ -37,10 +36,9 @@ internal sealed class SwapAmountComponentParams {
override val analyticsCategoryName: String,
override val userWallet: UserWallet,
override val isBalanceHidingFlow: StateFlow,
- override val appCurrency: AppCurrency,
override val swapDirection: SwapDirection,
- val primaryCryptoCurrencyStatus: CryptoCurrencyStatus,
- val secondaryCryptoCurrencyStatus: CryptoCurrencyStatus?,
+ override val primaryCryptoCurrencyStatusFlow: StateFlow,
+ override val secondaryCryptoCurrency: CryptoCurrency?,
val blockClickEnableFlow: StateFlow,
) : SwapAmountComponentParams()
}
\ No newline at end of file
diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/entity/SwapAmountUM.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/entity/SwapAmountUM.kt
index 62b7c86556..2ba34fa932 100644
--- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/entity/SwapAmountUM.kt
+++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/entity/SwapAmountUM.kt
@@ -34,8 +34,8 @@ internal sealed class SwapAmountUM {
override val primaryAmount: SwapAmountFieldUM,
override val secondaryAmount: SwapAmountFieldUM,
override val selectedAmountType: SwapAmountType,
- val primaryCryptoCurrencyStatus: CryptoCurrencyStatus?,
- val secondaryCryptoCurrencyStatus: CryptoCurrencyStatus?,
+ val primaryCryptoCurrencyStatus: CryptoCurrencyStatus,
+ val secondaryCryptoCurrencyStatus: CryptoCurrencyStatus,
// selected swap route
val swapDirection: SwapDirection,
@@ -61,7 +61,7 @@ sealed class SwapAmountFieldUM {
) : SwapAmountFieldUM() {
override val amountField: AmountState = AmountState.Empty(
isPrimaryButtonEnabled = false,
- isRedesignEnabled = false,
+ isRedesignEnabled = true,
)
}
@@ -70,7 +70,7 @@ sealed class SwapAmountFieldUM {
) : SwapAmountFieldUM() {
override val amountField: AmountState = AmountState.Empty(
isPrimaryButtonEnabled = false,
- isRedesignEnabled = false,
+ isRedesignEnabled = true,
)
}
diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAlertFactory.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAlertFactory.kt
new file mode 100644
index 0000000000..80af642d28
--- /dev/null
+++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAlertFactory.kt
@@ -0,0 +1,64 @@
+package com.tangem.features.swap.v2.impl.amount.model
+
+import com.tangem.core.decompose.di.ModelScoped
+import com.tangem.core.decompose.ui.UiMessageSender
+import com.tangem.core.ui.extensions.*
+import com.tangem.core.ui.message.DialogMessage
+import com.tangem.core.ui.utils.parseBigDecimal
+import com.tangem.domain.express.models.ExpressProvider
+import com.tangem.domain.express.models.ExpressProviderType
+import com.tangem.features.swap.v2.impl.R
+import com.tangem.utils.StringsSigns.PERCENT
+import javax.inject.Inject
+
+@ModelScoped
+internal class SwapAlertFactory @Inject constructor(
+ private val uiMessageSender: UiMessageSender,
+) {
+
+ fun priceImpactAlert(hasPriceImpact: Boolean, currencySymbol: String, provider: ExpressProvider) {
+ val slippage = provider.slippage?.let { "${it.parseBigDecimal(1)}$PERCENT" }
+ val combinedMessage = buildList {
+ when (provider.type) {
+ ExpressProviderType.CEX -> {
+ if (slippage != null) {
+ add(
+ resourceReference(
+ id = R.string.swapping_alert_cex_description_with_slippage,
+ formatArgs = wrappedList(currencySymbol, slippage),
+ ),
+ )
+ } else {
+ add(resourceReference(R.string.swapping_alert_cex_description, wrappedList(currencySymbol)))
+ }
+ }
+ ExpressProviderType.DEX,
+ ExpressProviderType.DEX_BRIDGE,
+ -> {
+ if (hasPriceImpact) {
+ add(resourceReference(R.string.swapping_high_price_impact_description))
+ add(stringReference("\n\n"))
+ }
+ if (slippage != null) {
+ add(
+ resourceReference(
+ id = R.string.swapping_alert_dex_description_with_slippage,
+ formatArgs = wrappedList(slippage),
+ ),
+ )
+ } else {
+ add(resourceReference(R.string.swapping_alert_dex_description, wrappedList(currencySymbol)))
+ }
+ }
+ else -> Unit
+ }
+ }
+ uiMessageSender.send(
+ DialogMessage(
+ title = resourceReference(R.string.swapping_alert_title),
+ message = combinedReference(combinedMessage.toWrappedList()),
+ firstActionBuilder = { okAction() },
+ ),
+ )
+ }
+}
\ No newline at end of file
diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountClickIntents.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountClickIntents.kt
index f9c92b77a1..34cf98f664 100644
--- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountClickIntents.kt
+++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountClickIntents.kt
@@ -6,5 +6,6 @@ import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType
internal interface SwapAmountClickIntents : AmountScreenClickIntents {
fun onExpandEditField(selectedAmountType: SwapAmountType)
+ fun onInfoClick()
fun onSelectTokenClick()
}
\ No newline at end of file
diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt
index a7610c26f1..97287393a0 100644
--- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt
+++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt
@@ -1,6 +1,7 @@
package com.tangem.features.swap.v2.impl.amount.model
import arrow.core.getOrElse
+import com.arkivanov.decompose.router.slot.SlotNavigation
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.common.ui.amountScreen.converters.MaxEnterAmountConverter
@@ -14,10 +15,11 @@ import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.format
+import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
+import com.tangem.domain.appcurrency.model.AppCurrency
+import com.tangem.domain.express.models.ExpressError
import com.tangem.domain.express.models.ExpressProvider
-import com.tangem.domain.express.models.ExpressRateType
import com.tangem.domain.models.currency.CryptoCurrency
-import com.tangem.domain.swap.models.SwapCurrencies
import com.tangem.domain.swap.models.SwapDirection
import com.tangem.domain.swap.models.SwapQuoteModel
import com.tangem.domain.swap.usecase.GetSwapPairsUseCase
@@ -28,6 +30,7 @@ import com.tangem.domain.tokens.GetMultiCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.transaction.usecase.GetAllowanceUseCase
import com.tangem.features.swap.v2.impl.R
+import com.tangem.features.swap.v2.impl.amount.SwapAmountBlockComponent.SwapChooseProviderConfig
import com.tangem.features.swap.v2.impl.amount.SwapAmountComponentParams
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType
@@ -41,12 +44,10 @@ import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM.Content.DifferencePercent
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.extensions.orZero
-import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
-import timber.log.Timber
import javax.inject.Inject
import kotlin.properties.Delegates
import com.tangem.utils.transformer.update as transformerUpdate
@@ -63,47 +64,41 @@ internal class SwapAmountModel @Inject constructor(
private val getSwapQuoteUseCase: GetSwapQuoteUseCase,
private val swapChooseTokenNetworkListener: SwapChooseTokenNetworkListener,
private val getAllowanceUseCase: GetAllowanceUseCase,
+ private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val appRouter: AppRouter,
+ private val swapAlertFactory: SwapAlertFactory,
) : Model(), SwapAmountClickIntents, SwapChooseProviderComponent.ModelCallback {
private val params: SwapAmountComponentParams = paramsContainer.require()
private val swapDirection = params.swapDirection
- private val appCurrency = params.appCurrency
+ private var appCurrency = AppCurrency.Default
private var userWallet = params.userWallet
- private var primaryCryptoCurrency: CryptoCurrency by Delegates.notNull()
- private var secondaryCryptoCurrency: CryptoCurrency? = null
+ private var primaryCryptoCurrency: CryptoCurrency = params.primaryCryptoCurrencyStatusFlow.value.currency
+ private var secondaryCryptoCurrency: CryptoCurrency? = params.secondaryCryptoCurrency
- private var primaryCryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull()
- private var secondaryCryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull()
+ private var primaryCryptoCurrencyStatus: CryptoCurrencyStatus = params.primaryCryptoCurrencyStatusFlow.value
+ private var secondaryCryptoCurrencyStatus: CryptoCurrencyStatus = CryptoCurrencyStatus(
+ currency = primaryCryptoCurrencyStatus.currency,
+ value = CryptoCurrencyStatus.Loading,
+ )
private var primaryMaximumAmountBoundary: EnterAmountBoundary by Delegates.notNull()
private var secondaryMaximumAmountBoundary: EnterAmountBoundary by Delegates.notNull()
private var primaryMinimumAmountBoundary: EnterAmountBoundary by Delegates.notNull()
private var secondaryMinimumAmountBoundary: EnterAmountBoundary by Delegates.notNull()
+ val bottomSheetNavigation: SlotNavigation = SlotNavigation()
+
val uiState: StateFlow
field = MutableStateFlow(params.amountUM)
init {
- when (params) {
- is SwapAmountComponentParams.AmountParams -> {
- primaryCryptoCurrency = params.primaryCryptoCurrency
- secondaryCryptoCurrency = params.secondaryCryptoCurrency
- }
- is SwapAmountComponentParams.AmountBlockParams -> {
- primaryCryptoCurrency = params.primaryCryptoCurrencyStatus.currency
- secondaryCryptoCurrency = params.secondaryCryptoCurrencyStatus?.currency
- primaryCryptoCurrencyStatus = params.primaryCryptoCurrencyStatus
- params.secondaryCryptoCurrencyStatus?.let {
- secondaryCryptoCurrencyStatus = it
- }
- }
+ modelScope.launch {
+ appCurrency = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default }
}
-
- initialState()
configAmountNavigation()
-
+ subscribeOnCryptoCurrencyStatusFlow()
observeChooseSelectToken()
// todo observe balance hiding flow
}
@@ -116,8 +111,6 @@ internal class SwapAmountModel @Inject constructor(
uiState.transformerUpdate(
SwapAmountSelectQuoteTransformer(
quoteUM = quoteUM,
- primaryCryptoCurrencyStatus = primaryCryptoCurrencyStatus,
- secondaryCryptoCurrencyStatus = secondaryCryptoCurrencyStatus,
secondaryMaximumAmountBoundary = secondaryMaximumAmountBoundary,
secondaryMinimumAmountBoundary = secondaryMinimumAmountBoundary,
),
@@ -133,11 +126,21 @@ internal class SwapAmountModel @Inject constructor(
}
}
+ override fun onInfoClick() {
+ val amountUM = uiState.value as? SwapAmountUM.Content ?: return
+ val selectedProvider = amountUM.selectedQuote.provider ?: return
+ val cryptoCurrency = secondaryCryptoCurrency ?: return
+
+ swapAlertFactory.priceImpactAlert(
+ hasPriceImpact = (amountUM.secondaryAmount as? SwapAmountFieldUM.Content)?.priceImpact != null,
+ currencySymbol = cryptoCurrency.symbol,
+ provider = selectedProvider,
+ )
+ }
+
override fun onAmountValueChange(value: String) {
uiState.transformerUpdate(
SwapAmountValueChangeTransformer(
- primaryCryptoCurrencyStatus = primaryCryptoCurrencyStatus,
- secondaryCryptoCurrencyStatus = secondaryCryptoCurrencyStatus,
primaryMaximumAmountBoundary = primaryMaximumAmountBoundary,
secondaryMaximumAmountBoundary = secondaryMaximumAmountBoundary,
primaryMinimumAmountBoundary = primaryMinimumAmountBoundary,
@@ -155,8 +158,6 @@ internal class SwapAmountModel @Inject constructor(
override fun onMaxValueClick() {
uiState.transformerUpdate(
SwapAmountValueMaxTransformer(
- primaryCryptoCurrencyStatus = primaryCryptoCurrencyStatus,
- secondaryCryptoCurrencyStatus = secondaryCryptoCurrencyStatus,
primaryMaximumAmountBoundary = primaryMaximumAmountBoundary,
secondaryMaximumAmountBoundary = secondaryMaximumAmountBoundary,
primaryMinimumAmountBoundary = primaryMinimumAmountBoundary,
@@ -169,8 +170,6 @@ internal class SwapAmountModel @Inject constructor(
override fun onCurrencyChangeClick(isFiat: Boolean) {
uiState.transformerUpdate(
SwapAmountChangeCurrencyTransformer(
- primaryCryptoCurrencyStatus = primaryCryptoCurrencyStatus,
- secondaryCryptoCurrencyStatus = secondaryCryptoCurrencyStatus,
isFiatSelected = isFiat,
),
)
@@ -190,6 +189,27 @@ internal class SwapAmountModel @Inject constructor(
)
}
+ private fun subscribeOnCryptoCurrencyStatusFlow() {
+ params.primaryCryptoCurrencyStatusFlow.onEach { primaryCurrencyStatus ->
+ primaryCryptoCurrencyStatus = primaryCurrencyStatus
+
+ val state = uiState.value
+ if (state is SwapAmountUM.Content) {
+ secondaryCryptoCurrency = state.primaryCryptoCurrencyStatus.currency
+ secondaryCryptoCurrencyStatus = state.primaryCryptoCurrencyStatus
+ initCurrencies(
+ primaryStatus = state.primaryCryptoCurrencyStatus,
+ secondaryStatus = state.secondaryCryptoCurrencyStatus,
+ )
+ } else {
+ initPairs(
+ primaryCryptoCurrency = primaryCryptoCurrency,
+ secondaryCryptoCurrency = secondaryCryptoCurrency,
+ )
+ }
+ }.launchIn(modelScope)
+ }
+
private fun observeChooseSelectToken() {
swapChooseTokenNetworkListener.swapChooseTokenNetworkResultFlow
.onEach { currency ->
@@ -210,46 +230,12 @@ internal class SwapAmountModel @Inject constructor(
.launchIn(modelScope)
}
- private fun initialState() {
- if (uiState.value is SwapAmountUM.Empty) {
- initPairs(
- primaryCryptoCurrency = primaryCryptoCurrency,
- secondaryCryptoCurrency = secondaryCryptoCurrency,
- )
- uiState.update {
- SwapAmountUM.Content(
- isPrimaryButtonEnabled = it.isPrimaryButtonEnabled,
- primaryAmount = it.primaryAmount,
- secondaryAmount = it.secondaryAmount,
- swapDirection = swapDirection,
- swapCurrencies = SwapCurrencies.EMPTY,
- appCurrency = appCurrency,
- swapQuotes = persistentListOf(),
- swapRateType = ExpressRateType.Float,
- selectedAmountType = SwapAmountType.From,
- selectedQuote = SwapQuoteUM.Empty,
- primaryCryptoCurrencyStatus = null,
- secondaryCryptoCurrencyStatus = null,
- )
- }
- }
- }
-
private fun initPairs(primaryCryptoCurrency: CryptoCurrency, secondaryCryptoCurrency: CryptoCurrency?) {
modelScope.launch {
val cryptoCurrencyStatusList = getMultiCryptoCurrencyStatusUseCase
.invokeMultiWalletSync(userWallet.walletId)
.getOrElse { emptyList() }
- val primaryStatus = cryptoCurrencyStatusList.firstOrNull {
- it.currency.id == primaryCryptoCurrency.id
- }
- if (primaryStatus == null) {
- Timber.e("Failed to get crypto currency status")
- // todo error
- return@launch
- }
-
val cryptoCurrencyStatusListExceptPrimary = cryptoCurrencyStatusList.filter {
val statusFilter = it.value is CryptoCurrencyStatus.Loaded || it.value is CryptoCurrencyStatus.NoAccount
val notCustomTokenFilter = !it.currency.isCustom
@@ -271,7 +257,9 @@ internal class SwapAmountModel @Inject constructor(
)
if (secondaryStatus != null) {
- initCurrencies(primaryStatus, secondaryStatus)
+ initCurrencies(primaryCryptoCurrencyStatus, secondaryStatus)
+ this@SwapAmountModel.secondaryCryptoCurrency = secondaryCryptoCurrency
+ secondaryCryptoCurrencyStatus = secondaryStatus
uiState.update {
SwapAmountReadyStateConverter(
swapCurrencies = swapCurrencies,
@@ -296,9 +284,6 @@ internal class SwapAmountModel @Inject constructor(
}
private suspend fun initCurrencies(primaryStatus: CryptoCurrencyStatus, secondaryStatus: CryptoCurrencyStatus) {
- primaryCryptoCurrencyStatus = primaryStatus
- secondaryCryptoCurrencyStatus = secondaryStatus
-
primaryMinimumAmountBoundary = EnterAmountBoundary(
amount = getMinimumTransactionAmountSyncUseCase
.invoke(
@@ -325,8 +310,12 @@ internal class SwapAmountModel @Inject constructor(
val state = uiState.value as? SwapAmountUM.Content ?: return
val (fromCryptoCurrency, toCryptoCurrency) = when (state.swapDirection) {
- SwapDirection.Direct -> primaryCryptoCurrencyStatus.currency to secondaryCryptoCurrencyStatus.currency
- SwapDirection.Reverse -> secondaryCryptoCurrencyStatus.currency to primaryCryptoCurrencyStatus.currency
+ SwapDirection.Direct -> {
+ state.primaryCryptoCurrencyStatus.currency to state.secondaryCryptoCurrencyStatus.currency
+ }
+ SwapDirection.Reverse -> {
+ state.secondaryCryptoCurrencyStatus.currency to state.primaryCryptoCurrencyStatus.currency
+ }
}
val fromAmount = when (state.swapDirection) {
@@ -346,9 +335,11 @@ internal class SwapAmountModel @Inject constructor(
uiState.transformerUpdate(SwapQuoteLoadingStateTransformer)
modelScope.launch {
- val quotes = swapGroups.firstOrNull {
+ val quotes = swapGroups.filter {
it.currencyStatus.currency.id == toCryptoCurrency.id
- }?.providers?.map { provider ->
+ }.flatMap {
+ it.providers
+ }.map { provider ->
async {
getSwapQuoteUseCase(
userWallet = userWallet,
@@ -361,7 +352,7 @@ internal class SwapAmountModel @Inject constructor(
SwapQuoteUM.Error(
provider = provider,
expressError = error,
- )
+ ).takeIf { error is ExpressError.AmountError }
},
ifRight = { quote: SwapQuoteModel ->
convertToSwapProviderUM(
@@ -373,13 +364,11 @@ internal class SwapAmountModel @Inject constructor(
},
)
}
- }?.awaitAll().orEmpty()
+ }.awaitAll().filterNotNull()
uiState.transformerUpdate(
SwapAmountSetQuotesTransformer(
quotes = quotes,
- primaryCryptoCurrencyStatus = primaryCryptoCurrencyStatus,
- secondaryCryptoCurrencyStatus = secondaryCryptoCurrencyStatus,
secondaryMaximumAmountBoundary = secondaryMaximumAmountBoundary,
secondaryMinimumAmountBoundary = secondaryMinimumAmountBoundary,
),
diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/converter/SwapAmountErrorConverter.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/converter/SwapAmountErrorConverter.kt
new file mode 100644
index 0000000000..36b944a04d
--- /dev/null
+++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/converter/SwapAmountErrorConverter.kt
@@ -0,0 +1,28 @@
+package com.tangem.features.swap.v2.impl.amount.model.converter
+
+import com.tangem.core.ui.extensions.TextReference
+import com.tangem.core.ui.extensions.resourceReference
+import com.tangem.core.ui.extensions.wrappedList
+import com.tangem.core.ui.format.bigdecimal.crypto
+import com.tangem.core.ui.format.bigdecimal.format
+import com.tangem.domain.express.models.ExpressError
+import com.tangem.domain.models.currency.CryptoCurrency
+import com.tangem.features.swap.v2.impl.R
+import com.tangem.utils.converter.Converter
+
+internal class SwapAmountErrorConverter(
+ private val cryptoCurrency: CryptoCurrency,
+) : Converter {
+
+ override fun convert(value: ExpressError): TextReference? = when (value) {
+ is ExpressError.AmountError.TooSmallError -> resourceReference(
+ R.string.express_provider_min_amount,
+ wrappedList(value.amount.format { crypto(cryptoCurrency = cryptoCurrency) }),
+ )
+ is ExpressError.AmountError.TooBigError -> resourceReference(
+ R.string.express_provider_max_amount,
+ wrappedList(value.amount.format { crypto(cryptoCurrency = cryptoCurrency) }),
+ )
+ else -> null
+ }
+}
\ No newline at end of file
diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountChangeCurrencyTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountChangeCurrencyTransformer.kt
index 05c0a9d16b..e1a149555d 100644
--- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountChangeCurrencyTransformer.kt
+++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountChangeCurrencyTransformer.kt
@@ -1,22 +1,20 @@
package com.tangem.features.swap.v2.impl.amount.model.transformers
import com.tangem.common.ui.amountScreen.converters.AmountCurrencyTransformer
-import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
import com.tangem.features.swap.v2.impl.amount.model.SwapAmountQuoteUtils.updateAmount
import com.tangem.utils.transformer.Transformer
internal class SwapAmountChangeCurrencyTransformer(
- private val primaryCryptoCurrencyStatus: CryptoCurrencyStatus,
- private val secondaryCryptoCurrencyStatus: CryptoCurrencyStatus,
private val isFiatSelected: Boolean,
) : Transformer {
override fun transform(prevState: SwapAmountUM): SwapAmountUM {
+ if (prevState !is SwapAmountUM.Content) return prevState
return prevState.updateAmount(
onPrimaryAmount = {
copy(
amountField = AmountCurrencyTransformer(
- cryptoCurrencyStatus = primaryCryptoCurrencyStatus,
+ cryptoCurrencyStatus = prevState.primaryCryptoCurrencyStatus,
value = isFiatSelected,
).transform(prevState.primaryAmount.amountField),
)
@@ -24,7 +22,7 @@ internal class SwapAmountChangeCurrencyTransformer(
onSecondaryAmount = {
copy(
amountField = AmountCurrencyTransformer(
- cryptoCurrencyStatus = secondaryCryptoCurrencyStatus,
+ cryptoCurrencyStatus = prevState.secondaryCryptoCurrencyStatus,
value = isFiatSelected,
).transform(prevState.secondaryAmount.amountField),
)
diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSelectQuoteTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSelectQuoteTransformer.kt
index 0c11067bfc..b11d0238e1 100644
--- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSelectQuoteTransformer.kt
+++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSelectQuoteTransformer.kt
@@ -3,33 +3,50 @@ package com.tangem.features.swap.v2.impl.amount.model.transformers
import com.tangem.common.ui.amountScreen.converters.field.AmountFieldChangeTransformer
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
+import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.utils.parseBigDecimal
-import com.tangem.domain.swap.models.SwapDirection
-import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
import com.tangem.features.swap.v2.impl.amount.model.SwapAmountQuoteUtils.calculatePriceImpact
+import com.tangem.features.swap.v2.impl.amount.model.converter.SwapAmountErrorConverter
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
import com.tangem.utils.extensions.orZero
import com.tangem.utils.transformer.Transformer
internal class SwapAmountSelectQuoteTransformer(
private val quoteUM: SwapQuoteUM,
- private val primaryCryptoCurrencyStatus: CryptoCurrencyStatus,
- private val secondaryCryptoCurrencyStatus: CryptoCurrencyStatus,
private val secondaryMaximumAmountBoundary: EnterAmountBoundary,
private val secondaryMinimumAmountBoundary: EnterAmountBoundary,
) : Transformer {
override fun transform(prevState: SwapAmountUM): SwapAmountUM {
if (prevState !is SwapAmountUM.Content) return prevState
+ val providerErrorConverter = SwapAmountErrorConverter(
+ cryptoCurrency = prevState.primaryCryptoCurrencyStatus.currency,
+ )
+
return prevState.copy(
- isPrimaryButtonEnabled = true,
+ isPrimaryButtonEnabled = quoteUM is SwapQuoteUM.Content,
selectedQuote = quoteUM,
- secondaryAmount = if (
- prevState.selectedAmountType == SwapAmountType.From && prevState.swapDirection == SwapDirection.Direct
- ) {
+ primaryAmount = if (prevState.selectedAmountType == SwapAmountType.From) {
+ val swapAmountField = prevState.primaryAmount as? SwapAmountFieldUM.Content
+ val amountField = swapAmountField?.amountField as? AmountState.Data
+
+ val amountError = (quoteUM as? SwapQuoteUM.Error)?.expressError?.let(providerErrorConverter::convert)
+
+ swapAmountField?.copy(
+ amountField = amountField?.copy(
+ amountTextField = amountField.amountTextField.copy(
+ error = amountError ?: TextReference.EMPTY,
+ isError = amountError != null,
+ ),
+ ) ?: swapAmountField.amountField,
+ ) ?: prevState.primaryAmount
+ } else {
+ prevState.primaryAmount
+ },
+ secondaryAmount = if (prevState.selectedAmountType == SwapAmountType.From) {
val secondaryAmountField = prevState.secondaryAmount as? SwapAmountFieldUM.Content
val fromAmount = (prevState.primaryAmount.amountField as? AmountState.Data)
?.amountTextField?.cryptoAmount?.value.orZero()
@@ -38,17 +55,17 @@ internal class SwapAmountSelectQuoteTransformer(
swapDirection = prevState.swapDirection,
fromTokenAmount = fromAmount,
toTokenAmount = toAmount.orZero(),
- primaryCryptoCurrencyStatus = primaryCryptoCurrencyStatus,
- secondaryCryptoCurrencyStatus = secondaryCryptoCurrencyStatus,
+ primaryCryptoCurrencyStatus = prevState.primaryCryptoCurrencyStatus,
+ secondaryCryptoCurrencyStatus = prevState.secondaryCryptoCurrencyStatus,
)
secondaryAmountField?.copy(
priceImpact = priceImpact,
amountField = AmountFieldChangeTransformer(
- cryptoCurrencyStatus = secondaryCryptoCurrencyStatus,
+ cryptoCurrencyStatus = prevState.secondaryCryptoCurrencyStatus,
maxEnterAmount = secondaryMaximumAmountBoundary,
minimumTransactionAmount = secondaryMinimumAmountBoundary,
- value = toAmount?.parseBigDecimal(secondaryCryptoCurrencyStatus.currency.decimals)
+ value = toAmount?.parseBigDecimal(prevState.secondaryCryptoCurrencyStatus.currency.decimals)
.orEmpty(),
).transform(secondaryAmountField.amountField),
) ?: prevState.secondaryAmount
diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt
index ac39b1417e..d890417658 100644
--- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt
+++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt
@@ -1,29 +1,15 @@
package com.tangem.features.swap.v2.impl.amount.model.transformers
-import com.tangem.common.ui.amountScreen.converters.field.AmountFieldChangeTransformer
-import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
-import com.tangem.core.ui.extensions.TextReference
-import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
-import com.tangem.core.ui.extensions.wrappedList
-import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.format.bigdecimal.percent
-import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.domain.express.models.ExpressError
-import com.tangem.domain.swap.models.SwapDirection
-import com.tangem.domain.tokens.model.CryptoCurrencyStatus
-import com.tangem.features.swap.v2.impl.R
-import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM
-import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
-import com.tangem.features.swap.v2.impl.amount.model.SwapAmountQuoteUtils.calculatePriceImpact
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM.Content.DifferencePercent
import com.tangem.utils.StringsSigns
import com.tangem.utils.extensions.isPositive
-import com.tangem.utils.extensions.orZero
import com.tangem.utils.transformer.Transformer
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toPersistentList
@@ -31,91 +17,30 @@ import java.math.BigDecimal
internal class SwapAmountSetQuotesTransformer(
private val quotes: List,
- private val primaryCryptoCurrencyStatus: CryptoCurrencyStatus,
- private val secondaryCryptoCurrencyStatus: CryptoCurrencyStatus,
private val secondaryMaximumAmountBoundary: EnterAmountBoundary,
private val secondaryMinimumAmountBoundary: EnterAmountBoundary,
) : Transformer {
override fun transform(prevState: SwapAmountUM): SwapAmountUM {
if (prevState !is SwapAmountUM.Content) return prevState
- val fromAmount = when (prevState.swapDirection) {
- SwapDirection.Direct -> prevState.primaryAmount.amountField
- SwapDirection.Reverse -> prevState.secondaryAmount.amountField
- } as? AmountState.Data
- val fromAmountValue = fromAmount?.amountTextField?.cryptoAmount?.value.orZero()
-
val sortedQuotes = quotes.sortedWith(SwapQuotesComparator)
val bestQuote = findBestQuote(quotes) ?: SwapQuoteUM.Empty
- return prevState.copy(
- isPrimaryButtonEnabled = bestQuote is SwapQuoteUM.Content,
+ val selectQuoteTransformer = SwapAmountSelectQuoteTransformer(
+ quoteUM = bestQuote,
+ secondaryMaximumAmountBoundary = secondaryMaximumAmountBoundary,
+ secondaryMinimumAmountBoundary = secondaryMinimumAmountBoundary,
+ )
+
+ val updatedState = selectQuoteTransformer.transform(prevState = prevState)
+ if (updatedState !is SwapAmountUM.Content) return prevState
+
+ return updatedState.copy(
+ isPrimaryButtonEnabled = updatedState.isPrimaryButtonEnabled && quotes.isNotEmpty(),
swapQuotes = getQuotesWithDiff(sortedQuotes, bestQuote),
- selectedQuote = bestQuote,
- primaryAmount = if (
- prevState.selectedAmountType == SwapAmountType.From && prevState.swapDirection == SwapDirection.Direct
- ) {
- val swapAmountField = prevState.primaryAmount as? SwapAmountFieldUM.Content
- val amountField = swapAmountField?.amountField as? AmountState.Data
-
- val amountError = (bestQuote as? SwapQuoteUM.Error)?.expressError.getAmountError()
-
- if (amountField?.amountTextField?.isError == true) {
- prevState.primaryAmount
- } else {
- swapAmountField?.copy(
- amountField = amountField?.copy(
- amountTextField = amountField.amountTextField.copy(
- error = amountError ?: TextReference.EMPTY,
- isError = amountError != null,
- ),
- ) ?: swapAmountField.amountField,
- ) ?: prevState.primaryAmount
- }
- } else {
- prevState.primaryAmount
- },
- secondaryAmount = if (
- prevState.selectedAmountType == SwapAmountType.From && prevState.swapDirection == SwapDirection.Direct
- ) {
- val amountField = prevState.secondaryAmount as? SwapAmountFieldUM.Content
- val toAmount = (bestQuote as? SwapQuoteUM.Content)?.quoteAmount
-
- val priceImpact = calculatePriceImpact(
- swapDirection = prevState.swapDirection,
- fromTokenAmount = fromAmountValue,
- toTokenAmount = toAmount.orZero(),
- primaryCryptoCurrencyStatus = primaryCryptoCurrencyStatus,
- secondaryCryptoCurrencyStatus = secondaryCryptoCurrencyStatus,
- )
-
- amountField?.copy(
- priceImpact = priceImpact,
- amountField = AmountFieldChangeTransformer(
- cryptoCurrencyStatus = secondaryCryptoCurrencyStatus,
- maxEnterAmount = secondaryMaximumAmountBoundary,
- minimumTransactionAmount = secondaryMinimumAmountBoundary,
- value = toAmount?.parseBigDecimal(secondaryCryptoCurrencyStatus.currency.decimals).orEmpty(),
- ).transform(amountField.amountField),
- ) ?: prevState.secondaryAmount
- } else {
- prevState.secondaryAmount
- },
)
}
- private fun ExpressError?.getAmountError(): TextReference? = when (this) {
- is ExpressError.AmountError.TooSmallError -> resourceReference(
- R.string.express_provider_min_amount,
- wrappedList(amount.format { crypto(cryptoCurrency = primaryCryptoCurrencyStatus.currency) }),
- )
- is ExpressError.AmountError.TooBigError -> resourceReference(
- R.string.express_provider_max_amount,
- wrappedList(amount.format { crypto(cryptoCurrency = primaryCryptoCurrencyStatus.currency) }),
- )
- else -> null
- }
-
private fun getQuotesWithDiff(sortedQuotes: List, bestQuote: SwapQuoteUM): ImmutableList {
return sortedQuotes.sortedWith(SwapQuotesComparator)
.map { quote ->
diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueChangeTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueChangeTransformer.kt
index 59687934ce..1c0dbd8086 100644
--- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueChangeTransformer.kt
+++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueChangeTransformer.kt
@@ -2,15 +2,11 @@ package com.tangem.features.swap.v2.impl.amount.model.transformers
import com.tangem.common.ui.amountScreen.converters.field.AmountFieldChangeTransformer
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
-import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
import com.tangem.features.swap.v2.impl.amount.model.SwapAmountQuoteUtils.updateAmount
import com.tangem.utils.transformer.Transformer
-@Suppress("LongParameterList")
internal class SwapAmountValueChangeTransformer(
- private val primaryCryptoCurrencyStatus: CryptoCurrencyStatus,
- private val secondaryCryptoCurrencyStatus: CryptoCurrencyStatus,
private val primaryMaximumAmountBoundary: EnterAmountBoundary,
private val secondaryMaximumAmountBoundary: EnterAmountBoundary,
private val primaryMinimumAmountBoundary: EnterAmountBoundary,
@@ -25,7 +21,7 @@ internal class SwapAmountValueChangeTransformer(
onPrimaryAmount = {
copy(
amountField = AmountFieldChangeTransformer(
- cryptoCurrencyStatus = primaryCryptoCurrencyStatus,
+ cryptoCurrencyStatus = prevState.primaryCryptoCurrencyStatus,
maxEnterAmount = primaryMaximumAmountBoundary,
minimumTransactionAmount = primaryMinimumAmountBoundary,
value = value,
@@ -35,7 +31,7 @@ internal class SwapAmountValueChangeTransformer(
onSecondaryAmount = {
copy(
amountField = AmountFieldChangeTransformer(
- cryptoCurrencyStatus = secondaryCryptoCurrencyStatus,
+ cryptoCurrencyStatus = prevState.secondaryCryptoCurrencyStatus,
maxEnterAmount = secondaryMaximumAmountBoundary,
minimumTransactionAmount = secondaryMinimumAmountBoundary,
value = value,
diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueMaxTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueMaxTransformer.kt
index 8392215305..980216e2a3 100644
--- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueMaxTransformer.kt
+++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueMaxTransformer.kt
@@ -2,15 +2,12 @@ package com.tangem.features.swap.v2.impl.amount.model.transformers
import com.tangem.common.ui.amountScreen.converters.field.AmountFieldSetMaxAmountTransformer
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
-import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
import com.tangem.features.swap.v2.impl.amount.model.SwapAmountQuoteUtils.updateAmount
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
import com.tangem.utils.transformer.Transformer
internal class SwapAmountValueMaxTransformer(
- private val primaryCryptoCurrencyStatus: CryptoCurrencyStatus,
- private val secondaryCryptoCurrencyStatus: CryptoCurrencyStatus,
private val primaryMaximumAmountBoundary: EnterAmountBoundary,
private val secondaryMaximumAmountBoundary: EnterAmountBoundary,
private val primaryMinimumAmountBoundary: EnterAmountBoundary,
@@ -25,7 +22,7 @@ internal class SwapAmountValueMaxTransformer(
onPrimaryAmount = {
copy(
amountField = AmountFieldSetMaxAmountTransformer(
- cryptoCurrencyStatus = primaryCryptoCurrencyStatus,
+ cryptoCurrencyStatus = prevState.primaryCryptoCurrencyStatus,
maxAmount = primaryMaximumAmountBoundary,
minAmount = primaryMinimumAmountBoundary,
).transform(prevState.primaryAmount.amountField),
@@ -34,7 +31,7 @@ internal class SwapAmountValueMaxTransformer(
onSecondaryAmount = {
copy(
amountField = AmountFieldSetMaxAmountTransformer(
- cryptoCurrencyStatus = secondaryCryptoCurrencyStatus,
+ cryptoCurrencyStatus = prevState.secondaryCryptoCurrencyStatus,
maxAmount = secondaryMaximumAmountBoundary,
minAmount = secondaryMinimumAmountBoundary,
).transform(prevState.secondaryAmount.amountField),
diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountBlockContent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountBlockContent.kt
new file mode 100644
index 0000000000..291627de98
--- /dev/null
+++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountBlockContent.kt
@@ -0,0 +1,201 @@
+package com.tangem.features.swap.v2.impl.amount.ui
+
+import android.content.res.Configuration
+import androidx.compose.foundation.Canvas
+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.RoundedCornerShape
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.material3.ripple
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.PathEffect
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import androidx.compose.ui.res.vectorResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.constraintlayout.compose.ConstraintLayout
+import com.tangem.common.ui.amountScreen.models.AmountState
+import com.tangem.common.ui.amountScreen.ui.AmountBlockV2
+import com.tangem.core.ui.extensions.TextReference
+import com.tangem.core.ui.extensions.resolveReference
+import com.tangem.core.ui.extensions.resourceReference
+import com.tangem.core.ui.extensions.stringResourceSafe
+import com.tangem.core.ui.res.TangemTheme
+import com.tangem.core.ui.res.TangemThemePreview
+import com.tangem.features.swap.v2.impl.R
+import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM
+import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
+import com.tangem.features.swap.v2.impl.amount.ui.preview.SwapAmountContentPreview
+import com.tangem.features.swap.v2.impl.chooseprovider.ui.SwapChooseProviderContent
+
+@Suppress("DestructuringDeclarationWithTooManyEntries")
+@Composable
+internal fun SwapAmountBlockContent(
+ amountUM: SwapAmountUM,
+ isClickEnabled: Boolean,
+ onProviderSelectClick: () -> Unit,
+ onInfoClick: () -> Unit,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ if (amountUM !is SwapAmountUM.Content) return
+ ConstraintLayout(
+ modifier = modifier
+ .clip(RoundedCornerShape(16.dp))
+ .background(TangemTheme.colors.background.action)
+ .clickable(
+ indication = ripple(),
+ interactionSource = remember { MutableInteractionSource() },
+ enabled = isClickEnabled,
+ onClick = onClick,
+ ),
+ ) {
+ val (from, to, separator, provider) = createRefs()
+ AmountBlockV2(
+ amountState = amountUM.primaryAmount.amountField,
+ isClickDisabled = true,
+ isEditingDisabled = false,
+ modifier = Modifier.constrainAs(from) {
+ top.linkTo(parent.top)
+ start.linkTo(parent.start)
+ end.linkTo(parent.end)
+ },
+ extraContent = {
+ SwapPriceImpact(amountFieldUM = amountUM.primaryAmount, onInfoClick = onInfoClick)
+ },
+ )
+ AmountBlockV2(
+ amountState = (amountUM.secondaryAmount.amountField as? AmountState.Data)?.copy(
+ title = resourceReference(R.string.send_with_swap_recipient_amount_title),
+ availableBalance = TextReference.EMPTY,
+ ) ?: amountUM.secondaryAmount.amountField,
+ isClickDisabled = true,
+ isEditingDisabled = false,
+ modifier = Modifier.constrainAs(to) {
+ top.linkTo(from.bottom, 8.dp)
+ start.linkTo(parent.start)
+ end.linkTo(parent.end)
+ },
+ extraContent = {
+ SwapPriceImpact(amountFieldUM = amountUM.secondaryAmount, onInfoClick = onInfoClick)
+ },
+ )
+ SwapAmountDivider(
+ modifier = Modifier.constrainAs(separator) {
+ top.linkTo(from.bottom)
+ bottom.linkTo(to.top)
+ start.linkTo(parent.start)
+ end.linkTo(parent.end)
+ },
+ )
+ SwapChooseProviderContent(
+ expressProvider = amountUM.selectedQuote.provider,
+ onClick = onProviderSelectClick,
+ modifier = Modifier.constrainAs(provider) {
+ top.linkTo(to.bottom)
+ bottom.linkTo(parent.bottom)
+ start.linkTo(parent.start)
+ end.linkTo(parent.end)
+ },
+ )
+ }
+}
+
+@Composable
+private fun SwapPriceImpact(amountFieldUM: SwapAmountFieldUM, onInfoClick: () -> Unit) {
+ val priceImpact = (amountFieldUM as? SwapAmountFieldUM.Content)?.priceImpact
+ if (priceImpact != null) {
+ Text(
+ text = priceImpact.resolveReference(),
+ style = TangemTheme.typography.body2,
+ color = TangemTheme.colors.text.attention,
+ )
+ Icon(
+ painter = rememberVectorPainter(
+ ImageVector.vectorResource(R.drawable.ic_information_24),
+ ),
+ tint = TangemTheme.colors.icon.attention,
+ contentDescription = null,
+ modifier = Modifier
+ .size(20.dp)
+ .clickable(
+ interactionSource = remember { MutableInteractionSource() },
+ indication = ripple(bounded = false),
+ onClick = onInfoClick,
+ ),
+ )
+ }
+}
+
+@Composable
+private fun SwapAmountDivider(modifier: Modifier = Modifier) {
+ Box(modifier = modifier) {
+ SwapDivider()
+ Text(
+ text = stringResourceSafe(R.string.swap_via_provider),
+ style = TangemTheme.typography.caption1,
+ color = TangemTheme.colors.text.tertiary,
+ modifier = Modifier
+ .background(TangemTheme.colors.stroke.primary, RoundedCornerShape(32.dp))
+ .padding(1.dp)
+ .background(TangemTheme.colors.text.primary2, RoundedCornerShape(32.dp)) // workaround
+ .background(TangemTheme.colors.icon.informative.copy(alpha = 0.1f), RoundedCornerShape(32.dp))
+ .padding(horizontal = 11.dp, vertical = 5.dp)
+ .align(Alignment.Center),
+ )
+ }
+}
+
+@Suppress("MagicNumber")
+@Composable
+private fun BoxScope.SwapDivider() {
+ val color = TangemTheme.colors.background.tertiary
+ Canvas(
+ Modifier
+ .fillMaxWidth()
+ .align(Alignment.Center),
+ ) {
+ val gapWidth = 2.dp.toPx()
+ val radius = 2.dp.toPx()
+
+ val pathEffect = PathEffect.dashPathEffect(
+ intervals = floatArrayOf(gapWidth, gapWidth * 3),
+ phase = 0f,
+ )
+ drawLine(
+ color = color,
+ start = Offset(0f, 0f),
+ end = Offset(size.width, 0f),
+ pathEffect = pathEffect,
+ cap = StrokeCap.Round,
+ strokeWidth = radius,
+ )
+ }
+}
+
+// region Preview
+@Composable
+@Preview(showBackground = true, widthDp = 360)
+@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
+private fun SwapAmountBlockContent_Preview() {
+ TangemThemePreview {
+ SwapAmountBlockContent(
+ amountUM = SwapAmountContentPreview.defaultState,
+ isClickEnabled = true,
+ onProviderSelectClick = {},
+ onInfoClick = {},
+ onClick = {},
+ )
+ }
+}
+// endregion
\ No newline at end of file
diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountClickIntentsStub.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountClickIntentsStub.kt
index 98bcb53665..194e61c3d3 100644
--- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountClickIntentsStub.kt
+++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountClickIntentsStub.kt
@@ -6,6 +6,8 @@ import com.tangem.features.swap.v2.impl.amount.model.SwapAmountClickIntents
internal object SwapAmountClickIntentsStub : SwapAmountClickIntents {
override fun onExpandEditField(selectedAmountType: SwapAmountType) {}
+ override fun onInfoClick() {}
+
override fun onSelectTokenClick() {}
override fun onAmountValueChange(value: String) {}
diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt
index 6db113e583..596cecef01 100644
--- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt
+++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt
@@ -6,8 +6,11 @@ import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.express.models.ExpressRateType
+import com.tangem.domain.models.currency.CryptoCurrency
+import com.tangem.domain.models.network.Network
import com.tangem.domain.swap.models.SwapCurrencies
import com.tangem.domain.swap.models.SwapDirection
+import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
@@ -16,6 +19,34 @@ import kotlinx.collections.immutable.persistentListOf
internal data object SwapAmountContentPreview {
+ private val cryptoCurrencyStatus = CryptoCurrencyStatus(
+ currency = CryptoCurrency.Coin(
+ id = CryptoCurrency.ID.fromValue("coin⟨BITCOIN⟩bitcoin"),
+ network = Network(
+ id = Network.ID(
+ value = "bitcoin",
+ derivationPath = Network.DerivationPath.None,
+ ),
+ backendId = "bitcoin",
+ name = "Bitcoin",
+ currencySymbol = "BTC",
+ derivationPath = Network.DerivationPath.None,
+ isTestnet = false,
+ standardType = Network.StandardType.Unspecified("bitcoin"),
+ hasFiatFeeRate = false,
+ canHandleTokens = false,
+ transactionExtrasType = Network.TransactionExtrasType.NONE,
+
+ ),
+ name = "Bitcoin",
+ symbol = "BTC",
+ decimals = 8,
+ iconUrl = "",
+ isCustom = false,
+ ),
+ value = CryptoCurrencyStatus.Loading,
+ )
+
val emptyState = SwapAmountUM.Content(
isPrimaryButtonEnabled = false,
primaryAmount = SwapAmountFieldUM.Empty(
@@ -29,8 +60,8 @@ internal data object SwapAmountContentPreview {
swapCurrencies = SwapCurrencies.EMPTY,
swapQuotes = persistentListOf(),
selectedQuote = SwapQuoteUM.Empty,
- primaryCryptoCurrencyStatus = null,
- secondaryCryptoCurrencyStatus = null,
+ primaryCryptoCurrencyStatus = cryptoCurrencyStatus,
+ secondaryCryptoCurrencyStatus = cryptoCurrencyStatus,
swapRateType = ExpressRateType.Float,
appCurrency = AppCurrency.Default,
)
@@ -65,8 +96,8 @@ internal data object SwapAmountContentPreview {
swapCurrencies = SwapCurrencies.EMPTY,
swapQuotes = persistentListOf(),
selectedQuote = SwapQuoteUM.Empty,
- primaryCryptoCurrencyStatus = null,
- secondaryCryptoCurrencyStatus = null,
+ primaryCryptoCurrencyStatus = cryptoCurrencyStatus,
+ secondaryCryptoCurrencyStatus = cryptoCurrencyStatus,
swapRateType = ExpressRateType.Float,
isPrimaryButtonEnabled = true,
)