diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt
index e7c231fd1f..6a6d1f4c01 100644
--- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt
+++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt
@@ -320,6 +320,13 @@ internal class ChildFactory @Inject constructor(
userWalletId = route.userWalletId,
isInitialReverseOrder = route.isInitialReverseOrder,
screenSource = route.screenSource,
+ tangemPayInput = route.tangemPayInput?.let { tangemPayInput ->
+ SwapComponent.Params.TangemPayInput(
+ cryptoAmount = tangemPayInput.cryptoAmount,
+ fiatAmount = tangemPayInput.fiatAmount,
+ depositAddress = tangemPayInput.depositAddress,
+ )
+ },
),
componentFactory = swapComponentFactory,
)
diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt
index aab2ba7f89..8358462b1e 100644
--- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt
+++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt
@@ -15,6 +15,7 @@ import com.tangem.domain.models.PortfolioId
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.scan.ScanResponse
+import com.tangem.domain.models.serialization.SerializedBigDecimal
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.nft.models.NFTAsset
import com.tangem.domain.onramp.model.OnrampSource
@@ -181,13 +182,21 @@ sealed class AppRoute(val path: String) : Route {
val userWalletId: UserWalletId,
val isInitialReverseOrder: Boolean = false,
val screenSource: String,
+ val tangemPayInput: TangemPayInput? = null,
) : AppRoute(
path = "/swap" +
"/${currencyFrom.id.value}" +
"/${currencyTo?.id?.value}" +
"/${userWalletId.stringValue}" +
"/$isInitialReverseOrder",
- )
+ ) {
+ @Serializable
+ data class TangemPayInput(
+ val cryptoAmount: SerializedBigDecimal,
+ val fiatAmount: SerializedBigDecimal,
+ val depositAddress: String,
+ )
+ }
@Serializable
data object AppCurrencySelector : AppRoute(path = "/app_currency_selector")
diff --git a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt
index 5879b1920e..e784eb24fe 100644
--- a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt
+++ b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt
@@ -84,6 +84,7 @@ sealed class AnalyticsParam {
data object LongTap : ScreensSources("Long Tap")
data object Markets : ScreensSources("Markets")
data object HotWallet : ScreensSources("Hot Wallet")
+ data object TangemPay : ScreensSources("Tangem Pay")
}
sealed class TxSentFrom(val value: String) {
diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml
index 6960013a5c..656c9a0baa 100644
--- a/core/res/src/main/res/values-de/strings.xml
+++ b/core/res/src/main/res/values-de/strings.xml
@@ -760,6 +760,8 @@
Krypto kaufen
Profitiere von **0 % Gebühren** beim Kauf von Kryptowährungen per SEPA-Überweisung.
Krypto mit SEPA kaufen
+ Trage dich in die Warteliste ein und erhalte eine Zahlungskarte, die es so noch nie gab.
+ Tangem Visa Card
Du musst einen einzigen Zugangscode einrichten, um alle deine Geräte zu schützen
Schützen
Du kannst später auf jeder Karte oder Ring einen individuellen Zugangscode einrichten
@@ -1711,7 +1713,7 @@
Tangem erhebt außerdem eine Servicegebühr von 15% auf den erzielten Ertrag.
Deine Gelder werden automatisch an Aave überwiesen, sobald die Netzwerkgebühren niedriger sind oder Dein Guthaben den erforderlichen Mindestbetrag erreicht.
Historische Renditen
- Die Freigabe für Deine Token im Yield-Modus wurde widerrufen. Öffne den Token, um die Berechtigung erneut zu erteilen.
+ Die Genehmigung für Deine Token im Yield-Modus wurde widerrufen. Öffne den Token, um die Berechtigung erneut zu erteilen.
Token-Genehmigung erforderlich
Prüfe Deine Netzwerkverbindung
Informationen zu den Netzwerkgebühren nicht erreichbar
@@ -1743,6 +1745,7 @@
Die Netzwerkgebühr wird von dem Betrag, den Du abhebst, abgezogen.
Effektiver Jahreszins für Versorgung
Effektiver Jahreszins
+ Zinsen fallen automatisch an
Zinsen fallen automatisch an
Ertragsmodus
Bearbeitung Deiner Einzahlung
diff --git a/core/res/src/main/res/values-es/strings.xml b/core/res/src/main/res/values-es/strings.xml
index 1a93e0147b..fc32c4eedf 100644
--- a/core/res/src/main/res/values-es/strings.xml
+++ b/core/res/src/main/res/values-es/strings.xml
@@ -653,6 +653,8 @@
Comprar cripto
Disfrute de unas comisiones del **0%** al comprar criptomonedas mediante transferencias SEPA.
Comprar criptomonedas con SEPA
+ Únete a la lista de espera y consigue una tarjeta de pago como ninguna otra.
+ Tangem Visa Card
Configure un código de acceso único para proteger todos sus dispositivos.
Proteger
Puede configurar un código de acceso individual en cada tarjeta más adelante
@@ -1544,9 +1546,11 @@
La comisión de red se deducirá del importe que retire.
Suministro APY
APY
+ Los intereses se devengan automáticamente
Los intereses se devengan automáticamente
Modo de rendimiento
Procesando su depósito
+ Modo Rendimiento
Automático
Deposite algo de %1$s %2$s para cubrir la tarifa de red para las transacciones
No se puede cubrir la tarifa %s
diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml
index c1aa707042..ef9f1f5500 100644
--- a/core/res/src/main/res/values-fr/strings.xml
+++ b/core/res/src/main/res/values-fr/strings.xml
@@ -626,6 +626,8 @@
Acheter crypto
Profitez de **0% de frais** lors de l\'achat de cryptomonnaies via des virements SEPA.
Acheter des cryptomonnaies avec SEPA
+ Rejoignez la liste d’attente et obtenez une carte de paiement pas comme les autres.
+ Tangem Visa Card
Vous devez définir un seul code d\'accès pour protéger tous vos appareils.
Protéger
Vous pourrez définir un code d\'accès individuel sur chaque carte plus tard
diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml
index 2d8b7ca984..d7457f6c77 100644
--- a/core/res/src/main/res/values-ja/strings.xml
+++ b/core/res/src/main/res/values-ja/strings.xml
@@ -30,6 +30,7 @@
アカウントをアーカイブする
アーカイブ
このアカウントをアーカイブしますが、いつでも復元できます。
+ アーカイブ中・・・
アカウント
アカウントが保存されました
報酬用のアカウント
@@ -156,6 +157,13 @@
- %1$dトークン
+ 続行するには次のデバイスをリセットしてください。
+ ウォレットのリセット
+ すべてのTangemデバイスがリセットされました。ウォレットのアップグレードを続行できます。
+ もう一度アップグレードしてください
+ リセット完了
+ すべてのTangemデバイスをリセットしていません
+ すべてのTangemデバイスをリセットしていません
このカードを使用して、このウォレット内の他のカードまたはリングのアクセスコードをリセットしたくない場合は、このオプションを無効にしてください。これにより、このカードのアクセスコードもリセットできなくなりますのでご注意ください。
このカードを使用して、このウォレット内の他のカードのアクセスコードをリセットできます。
アクセスコードの復元
@@ -307,6 +315,7 @@
リロード
名前を変更
必須
+ リセット
保存
変更内容を保存
検索
@@ -423,6 +432,7 @@
WalletConnectエラー
別のウォレットのカードまたはリングを使用しました。このウォレットにリンクしているカードまたはリングをタップしてください。
取引に必要な資金が不足しています。アカウントに入金してください。
+ ポートフォリオ内のアセットをこのトークンと交換します
マイトークン
まだトークンを追加していません。マーケットからトークンを追加して交換してください。
%sとの交換はできません
@@ -779,6 +789,8 @@
暗号資産を買い付ける
SEPA送金で暗号資産を買い付けると、**手数料 0% ** になります。
SEPAで暗号資産を購入
+ 事前申し込み受付中。他にない特別なカードを、いち早く体験しよう。
+ Tangem Visaカード
すべてのデバイスを保護するには、単一のアクセスコードを設定してください。
保護する
後で各カードおよびリングに個別のアクセスコードを設定できます。
@@ -1303,6 +1315,7 @@
取引を表示
サービス手数料
手数料
+ サポートを受ける
完了
拒否
保留中
@@ -1315,6 +1328,15 @@
データの読み込みに失敗しました。しばらくしてからもう一度お試しください。
カードの一時停止
非表示
+ Googleウォレットを開く
+ 数回タップするだけでTangem Payの設定完了。Google Payですぐに支払いを始めましょう。
+ Google Payにカードを追加する
+ Googleウォレットを開く
+ 「カードを追加」をタップします
+ カードの詳細を手動で入力してください
+ お使いの端末に送信されたワンタイムパスワード(OTP)でカードを認証してください。
+ 準備完了!カードはすぐに使用できます。
+ Google Payにカードを追加する
技術的な問題が検出されました。しばらくしてからもう一度お試しいただくか、サポートにお問い合わせください。
現在、受け取りは利用できません
表示
diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index 481e539277..8d29bcc1a7 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -701,6 +701,8 @@
Купить криптовалюту
Наслаждайтесь **нулевой комиссией** при покупке криптовалюты через SEPA-переводы.
Покупайте крипту через SEPA
+ Присоединяйтесь к списку ожидания и получите уникальную платёжную карту, не похожую ни на одну другую.
+ Tangem Visa Card
Установите единый код доступа для защиты всех ваших карт или колец
Защита
Установите индивидуальный код доступа для каждой карты или кольца позже.
@@ -1631,7 +1633,7 @@
Следующие пополнения вашего счёта автоматически поступят в Aave.
Активен
На паузе
- Завершить заработок
+ Завершение режима доходности
Выключив эту функцию, вы выведёте средства из Aave, получите их обратно в %s в своем кошельке и перестанете зарабатывать награды.
Комиссия сети будет вычтена из суммы вашего вывода.
Годовая доходность (APY)
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 6812790144..936f193cce 100644
--- a/core/res/src/main/res/values-uk-rUA/strings.xml
+++ b/core/res/src/main/res/values-uk-rUA/strings.xml
@@ -629,6 +629,8 @@
Купити криптовалюту
Насолоджуйтесь **нульовою комісією** при купівлі криптовалюти через перекази SEPA.
Купуйте криптовалюту через SEPA
+ Приєднуйтесь до списку очікування та отримайте унікальну платіжну картку, якої ще не було.
+ Tangem Visa Card
Налаштуйте єдиний код доступу для захисту всіх ваших карток або кілець
Захист
Встановіть індивідуальний код доступу для кожної картки або кільця пізніше.
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index 15e0b5cfc4..51d8c6a65b 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -30,6 +30,7 @@
Archive account
Archive
You are archiving this account, but you can always get it back.
+ Archiving...
Account
Account saved
Account for rewards
@@ -159,6 +160,13 @@
- %d token
- %d tokens
+ Please reset the next device to continue.
+ Wallet reset
+ All Tangem devices have been reset. You can now continue upgrading your wallet.
+ Upgrade again
+ Reset complete
+ You haven’t reset all your Tangem devices
+ You haven’t reset all your Tangem devices
Disable this option if you don\'t want this card to be used to reset access codes on other cards or rings in this wallet. Please note that this will also prevent you from resetting the access code on this card.
Allows you to use this card to reset access code on other cards in this wallet
Access code recovery
@@ -313,6 +321,7 @@
Reload
Rename
Required
+ Reset
Save
Save changes
Search
@@ -431,6 +440,7 @@
WalletConnect error
You have used a card or ring from another wallet. Tap the card or ring associated with this wallet
Not enough funds for the transaction. Please top up your account.
+ Swap any asset in your portfolio for this token
My tokens
You haven\'t added any tokens yet. Add tokens via Market to swap
Cannot be swapped for %s
@@ -795,6 +805,8 @@
Buy crypto
Enjoy **0% fees** when purchasing crypto via SEPA transfers.
Buy Crypto with SEPA
+ Join the waitlist and get a payment card unlike any other
+ Tangem Visa Card
Set up a single access code to protect all your devices.
Protect
Set an individual access code for each card or ring later.
@@ -1326,6 +1338,9 @@
Explore transaction
Service fees
Fee
+ Keep your money safe if your card is lost or stolen. You can unfreeze anytime.
+ Freeze your card?
+ Freeze
Get Help
Completed
Declined
@@ -1333,6 +1348,8 @@
The bank rejected this transaction request.
This fee goes to cover the cost of handling your transfer.
Withdrawal
+ Add funds
+ Top-up options
Change PIN
The card is fully ready for payments.
PIN code created
@@ -1348,9 +1365,11 @@
Verify card using the OTP sent to your device.
All set! Your card is ready to use.
Add card to Google Pay
+ Share your address or show QR-code
Technical issues detected. Please try again later or contact support.
Receive unavailable now
Reveal
+ Swap any asset in your portfolio for card
Card details
Create account and issue a card
Tangem Pay
@@ -1869,7 +1888,7 @@
Send, swap, or sell your funds instantly, anytime you want.
Access your funds anytime
How it works?
- Aave is a decentralized protocol managing over $61 billion in total value.
+ Aave is a decentralized protocol managing over $61 billion in total value.
Decentralized and self-custodial
By using this service, you agree with provider\n%1$s and %2$s
Connect Aave
diff --git a/data/visa/build.gradle.kts b/data/visa/build.gradle.kts
index 75c79ae815..0e59eaa831 100644
--- a/data/visa/build.gradle.kts
+++ b/data/visa/build.gradle.kts
@@ -30,6 +30,7 @@ dependencies {
implementation(projects.domain.tokens)
implementation(projects.domain.networks)
implementation(projects.domain.walletManager)
+ implementation(projects.domain.quotes)
/** Feature API - remove after removing [HotWalletFeatureToggles] */
implementation(projects.features.hotWallet.api)
diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/DefaultDataForReceiveFactory.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/DefaultTangemPayTopUpDataFactory.kt
similarity index 59%
rename from data/visa/src/main/kotlin/com/tangem/data/pay/DefaultDataForReceiveFactory.kt
rename to data/visa/src/main/kotlin/com/tangem/data/pay/DefaultTangemPayTopUpDataFactory.kt
index 7ac7421c08..fafac4cce8 100644
--- a/data/visa/src/main/kotlin/com/tangem/data/pay/DefaultDataForReceiveFactory.kt
+++ b/data/visa/src/main/kotlin/com/tangem/data/pay/DefaultTangemPayTopUpDataFactory.kt
@@ -3,7 +3,6 @@ package com.tangem.data.pay
import arrow.core.Either
import com.squareup.moshi.Moshi
import com.tangem.blockchain.blockchains.ethereum.Chain
-import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.core.error.UniversalError
import com.tangem.data.common.currency.CryptoCurrencyFactory
@@ -14,13 +13,15 @@ import com.tangem.datasource.di.NetworkMoshi
import com.tangem.domain.models.ReceiveAddressModel
import com.tangem.domain.models.ReceiveAddressModel.NameService
import com.tangem.domain.models.currency.CryptoCurrency
-import com.tangem.domain.pay.DataForReceive
-import com.tangem.domain.pay.DataForReceiveFactory
+import com.tangem.domain.models.wallet.UserWallet
+import com.tangem.domain.pay.TangemPayTopUpData
+import com.tangem.domain.pay.TangemPayTopUpDataFactory
import kotlinx.coroutines.CancellationException
import timber.log.Timber
+import java.math.BigDecimal
import javax.inject.Inject
-private const val TAG = "TangemPay: TokenReceiveConfigFactory"
+private const val TAG = "TangemPay: DefaultDataForTopUpFactory"
/**
* Custom token parameters. Will be used only for F&F.
*/
@@ -29,11 +30,11 @@ private const val TOKEN_NAME = "USDC"
private const val TOKEN_CONTRACT_ADDRESS = "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359"
private const val TOKEN_DECIMALS = 6
-internal class DefaultDataForReceiveFactory @Inject constructor(
+internal class DefaultTangemPayTopUpDataFactory @Inject constructor(
@NetworkMoshi moshi: Moshi,
private val tangemPayWalletsManager: TangemPayWalletsManager,
excludedBlockchains: ExcludedBlockchains,
-) : DataForReceiveFactory {
+) : TangemPayTopUpDataFactory {
private val cryptoCurrencyFactory by lazy(mode = LazyThreadSafetyMode.NONE) {
CryptoCurrencyFactory(excludedBlockchains)
@@ -43,26 +44,39 @@ internal class DefaultDataForReceiveFactory @Inject constructor(
}
private val errorConverter by lazy(mode = LazyThreadSafetyMode.NONE) { TangemPayErrorConverter(moshi) }
- override fun getDataForReceive(depositAddress: String, chainId: Int): Either {
+ private fun getCurrency(userWallet: UserWallet, chainId: Int): CryptoCurrency {
+ val chain = requireNotNull(Chain.entries.find { it.id == chainId }) { "Can not find chain with $chainId" }
+ val blockchain = requireNotNull(chain.blockchain)
+ val network = networkFactory.create(
+ blockchain = blockchain,
+ extraDerivationPath = null,
+ userWallet = userWallet,
+ )
+ return cryptoCurrencyFactory.createToken(
+ network = requireNotNull(network),
+ rawId = CryptoCurrency.RawID(TOKEN_ID),
+ name = TOKEN_NAME,
+ symbol = TOKEN_NAME,
+ contractAddress = TOKEN_CONTRACT_ADDRESS,
+ decimals = TOKEN_DECIMALS,
+ )
+ }
+
+ override fun create(
+ depositAddress: String,
+ chainId: Int,
+ cryptoBalance: BigDecimal,
+ fiatBalance: BigDecimal,
+ ): Either {
return try {
val wallet = tangemPayWalletsManager.getDefaultWalletForTangemPayBlocking()
- val blockchain: Blockchain? = Chain.entries.find { it.id == chainId }?.blockchain
- val network = networkFactory.create(
- blockchain = requireNotNull(blockchain),
- extraDerivationPath = null,
- userWallet = wallet,
- )
- val currency = cryptoCurrencyFactory.createToken(
- network = requireNotNull(network),
- rawId = CryptoCurrency.RawID(TOKEN_ID),
- name = TOKEN_NAME,
- symbol = TOKEN_NAME,
- contractAddress = TOKEN_CONTRACT_ADDRESS,
- decimals = TOKEN_DECIMALS,
- )
- val result = DataForReceive(
+ val currency = getCurrency(wallet, chainId)
+ val result = TangemPayTopUpData(
currency = currency,
walletId = wallet.walletId,
+ cryptoBalance = cryptoBalance,
+ fiatBalance = fiatBalance,
+ depositAddress = depositAddress,
receiveAddress = listOf(ReceiveAddressModel(nameService = NameService.Default, value = depositAddress)),
)
diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/di/TangemPayDataModule.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/di/TangemPayDataModule.kt
index 40c402edf8..43a2e87cc9 100644
--- a/data/visa/src/main/kotlin/com/tangem/data/pay/di/TangemPayDataModule.kt
+++ b/data/visa/src/main/kotlin/com/tangem/data/pay/di/TangemPayDataModule.kt
@@ -1,17 +1,19 @@
package com.tangem.data.pay.di
-import com.tangem.data.pay.DefaultDataForReceiveFactory
+import com.tangem.data.pay.DefaultTangemPayTopUpDataFactory
import com.tangem.data.pay.repository.DefaultCardDetailsRepository
import com.tangem.data.pay.repository.DefaultKycRepository
import com.tangem.data.pay.repository.DefaultTangemPayTxHistoryRepository
import com.tangem.data.pay.repository.DefaultOnboardingRepository
-import com.tangem.domain.pay.DataForReceiveFactory
+import com.tangem.data.pay.usecase.DefaultGetTangemPayCurrencyStatusUseCase
+import com.tangem.domain.pay.TangemPayTopUpDataFactory
import com.tangem.domain.pay.repository.CardDetailsRepository
import com.tangem.domain.pay.repository.KycRepository
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.pay.usecase.ProduceTangemPayInitialDataUseCase
import com.tangem.domain.pay.usecase.TangemPayIssueOrderUseCase
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
+import com.tangem.domain.tangempay.GetTangemPayCurrencyStatusUseCase
import com.tangem.domain.tangempay.repository.TangemPayTxHistoryRepository
import dagger.Binds
import dagger.Module
@@ -42,7 +44,13 @@ internal interface TangemPayDataModule {
@Binds
@Singleton
- fun bindDataForReceiveFactory(factory: DefaultDataForReceiveFactory): DataForReceiveFactory
+ fun bindDataForReceiveFactory(factory: DefaultTangemPayTopUpDataFactory): TangemPayTopUpDataFactory
+
+ @Binds
+ @Singleton
+ fun bindGetTangemPayCurrencyStatusUseCase(
+ impl: DefaultGetTangemPayCurrencyStatusUseCase,
+ ): GetTangemPayCurrencyStatusUseCase
companion object {
@Provides
diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/usecase/DefaultGetTangemPayCurrencyStatusUseCase.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/usecase/DefaultGetTangemPayCurrencyStatusUseCase.kt
new file mode 100644
index 0000000000..53ca506151
--- /dev/null
+++ b/data/visa/src/main/kotlin/com/tangem/data/pay/usecase/DefaultGetTangemPayCurrencyStatusUseCase.kt
@@ -0,0 +1,62 @@
+package com.tangem.data.pay.usecase
+
+import com.tangem.domain.models.currency.CryptoCurrency
+import com.tangem.domain.models.currency.CryptoCurrencyStatus
+import com.tangem.domain.models.network.NetworkAddress
+import com.tangem.domain.models.quote.QuoteStatus
+import com.tangem.domain.quotes.single.SingleQuoteStatusFetcher
+import com.tangem.domain.quotes.single.SingleQuoteStatusProducer
+import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
+import com.tangem.domain.tangempay.GetTangemPayCurrencyStatusUseCase
+import java.math.BigDecimal
+import javax.inject.Inject
+
+internal class DefaultGetTangemPayCurrencyStatusUseCase @Inject constructor(
+ private val singleQuoteStatusSupplier: SingleQuoteStatusSupplier,
+ private val singleQuoteStatusFetcher: SingleQuoteStatusFetcher,
+) : GetTangemPayCurrencyStatusUseCase {
+
+ override suspend fun invoke(
+ currency: CryptoCurrency,
+ cryptoAmount: BigDecimal,
+ fiatAmount: BigDecimal,
+ depositAddress: String,
+ ): CryptoCurrencyStatus? {
+ val rawCurrencyId = currency.id.rawCurrencyId ?: return null
+ val quoteStatus = singleQuoteStatusSupplier.getSyncOrNull(
+ params = SingleQuoteStatusProducer.Params(rawCurrencyId = rawCurrencyId),
+ )?.value
+ val quoteStatusData: QuoteStatus.Data = when (quoteStatus) {
+ is QuoteStatus.Data -> quoteStatus
+ else -> {
+ singleQuoteStatusFetcher.invoke(
+ params = SingleQuoteStatusFetcher.Params(rawCurrencyId = rawCurrencyId, appCurrencyId = null),
+ )
+ singleQuoteStatusSupplier.getSyncOrNull(
+ params = SingleQuoteStatusProducer.Params(rawCurrencyId = rawCurrencyId),
+ )?.value as? QuoteStatus.Data
+ }
+ } ?: return null
+
+ return CryptoCurrencyStatus(
+ currency = currency,
+ value = CryptoCurrencyStatus.Loaded(
+ amount = cryptoAmount,
+ fiatRate = quoteStatusData.fiatRate,
+ priceChange = quoteStatusData.priceChange,
+ fiatAmount = fiatAmount,
+ networkAddress = NetworkAddress.Single(
+ defaultAddress = NetworkAddress.Address(
+ type = NetworkAddress.Address.Type.Primary,
+ value = depositAddress,
+ ),
+ ),
+ sources = CryptoCurrencyStatus.Sources(),
+ pendingTransactions = emptySet(),
+ yieldBalance = null,
+ yieldSupplyStatus = null,
+ hasCurrentNetworkTransactions = false,
+ ),
+ )
+ }
+}
\ No newline at end of file
diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/pay/DataForReceiveFactory.kt b/domain/visa/src/main/kotlin/com/tangem/domain/pay/DataForReceiveFactory.kt
deleted file mode 100644
index 1b78f76580..0000000000
--- a/domain/visa/src/main/kotlin/com/tangem/domain/pay/DataForReceiveFactory.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.tangem.domain.pay
-
-import arrow.core.Either
-import com.tangem.core.error.UniversalError
-import com.tangem.domain.models.ReceiveAddressModel
-import com.tangem.domain.models.currency.CryptoCurrency
-import com.tangem.domain.models.wallet.UserWalletId
-
-interface DataForReceiveFactory {
-
- fun getDataForReceive(depositAddress: String, chainId: Int): Either
-}
-
-data class DataForReceive(
- val walletId: UserWalletId,
- val currency: CryptoCurrency,
- val receiveAddress: List,
-)
\ No newline at end of file
diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/pay/TangemPayTopUpDataFactory.kt b/domain/visa/src/main/kotlin/com/tangem/domain/pay/TangemPayTopUpDataFactory.kt
new file mode 100644
index 0000000000..887df24ef0
--- /dev/null
+++ b/domain/visa/src/main/kotlin/com/tangem/domain/pay/TangemPayTopUpDataFactory.kt
@@ -0,0 +1,27 @@
+package com.tangem.domain.pay
+
+import arrow.core.Either
+import com.tangem.core.error.UniversalError
+import com.tangem.domain.models.ReceiveAddressModel
+import com.tangem.domain.models.currency.CryptoCurrency
+import com.tangem.domain.models.wallet.UserWalletId
+import java.math.BigDecimal
+
+interface TangemPayTopUpDataFactory {
+
+ fun create(
+ depositAddress: String,
+ chainId: Int,
+ cryptoBalance: BigDecimal,
+ fiatBalance: BigDecimal,
+ ): Either
+}
+
+data class TangemPayTopUpData(
+ val walletId: UserWalletId,
+ val currency: CryptoCurrency,
+ val cryptoBalance: BigDecimal,
+ val fiatBalance: BigDecimal,
+ val depositAddress: String,
+ val receiveAddress: List,
+)
\ No newline at end of file
diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/tangempay/GetTangemPayCurrencyStatusUseCase.kt b/domain/visa/src/main/kotlin/com/tangem/domain/tangempay/GetTangemPayCurrencyStatusUseCase.kt
new file mode 100644
index 0000000000..a6f4b0baf1
--- /dev/null
+++ b/domain/visa/src/main/kotlin/com/tangem/domain/tangempay/GetTangemPayCurrencyStatusUseCase.kt
@@ -0,0 +1,15 @@
+package com.tangem.domain.tangempay
+
+import com.tangem.domain.models.currency.CryptoCurrency
+import com.tangem.domain.models.currency.CryptoCurrencyStatus
+import java.math.BigDecimal
+
+interface GetTangemPayCurrencyStatusUseCase {
+
+ suspend operator fun invoke(
+ currency: CryptoCurrency,
+ cryptoAmount: BigDecimal,
+ fiatAmount: BigDecimal,
+ depositAddress: String,
+ ): CryptoCurrencyStatus?
+}
\ No newline at end of file
diff --git a/features/swap/api/src/main/kotlin/com/tangem/features/swap/SwapComponent.kt b/features/swap/api/src/main/kotlin/com/tangem/features/swap/SwapComponent.kt
index 368cdeec5b..3f33c58822 100644
--- a/features/swap/api/src/main/kotlin/com/tangem/features/swap/SwapComponent.kt
+++ b/features/swap/api/src/main/kotlin/com/tangem/features/swap/SwapComponent.kt
@@ -4,6 +4,7 @@ import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
+import java.math.BigDecimal
interface SwapComponent : ComposableContentComponent {
@@ -13,7 +14,14 @@ interface SwapComponent : ComposableContentComponent {
val userWalletId: UserWalletId,
val isInitialReverseOrder: Boolean = false,
val screenSource: String,
- )
+ val tangemPayInput: TangemPayInput? = null,
+ ) {
+ data class TangemPayInput(
+ val cryptoAmount: BigDecimal,
+ val fiatAmount: BigDecimal,
+ val depositAddress: String,
+ )
+ }
interface Factory : ComponentFactory
}
\ No newline at end of file
diff --git a/features/swap/impl/build.gradle.kts b/features/swap/impl/build.gradle.kts
index 4d5d733b5e..b1c706c900 100644
--- a/features/swap/impl/build.gradle.kts
+++ b/features/swap/impl/build.gradle.kts
@@ -46,6 +46,7 @@ dependencies {
implementation(projects.domain.express.models)
implementation(projects.domain.account)
implementation(projects.domain.account.status)
+ implementation(projects.domain.visa)
/** Feature modules */
implementation(projects.features.swap.domain)
diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt
index ce0227385f..d3091f9242 100644
--- a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt
+++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt
@@ -44,6 +44,7 @@ import com.tangem.domain.promo.models.StoryContentIds
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
import com.tangem.domain.settings.usercountry.models.UserCountry
import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions
+import com.tangem.domain.tangempay.GetTangemPayCurrencyStatusUseCase
import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
@@ -113,6 +114,7 @@ internal class SwapModel @Inject constructor(
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase,
private val accountsFeatureToggles: AccountsFeatureToggles,
+ private val getTangemPayCurrencyStatusUseCase: GetTangemPayCurrencyStatusUseCase,
) : Model() {
private val params = paramsContainer.require()
@@ -121,6 +123,7 @@ internal class SwapModel @Inject constructor(
private val initialCurrencyTo = params.currencyTo
private val userWalletId = params.userWalletId
private val isInitiallyReversed = params.isInitialReverseOrder
+ private val tangemPayInput = params.tangemPayInput
private val userWallet by lazy {
requireNotNull(
@@ -196,7 +199,7 @@ internal class SwapModel @Inject constructor(
}
modelScope.launch(dispatchers.io) {
- if (accountsFeatureToggles.isFeatureEnabled) {
+ if (accountsFeatureToggles.isFeatureEnabled && tangemPayInput == null) {
isAccountsMode = isAccountsModeEnabledUseCase.invokeSync()
val fromAccountStatus = getAccountCurrencyStatusUseCase.invokeSync(
@@ -220,10 +223,19 @@ internal class SwapModel @Inject constructor(
initTokens(isInitiallyReversed)
}
} else {
- val fromStatus = getSingleCryptoCurrencyStatusUseCase.invokeMultiWalletSync(
- userWalletId = userWalletId,
- cryptoCurrencyId = initialCurrencyFrom.id,
- ).getOrNull()
+ val fromStatus = if (tangemPayInput != null) {
+ getTangemPayCurrencyStatusUseCase(
+ currency = initialCurrencyFrom,
+ cryptoAmount = tangemPayInput.cryptoAmount,
+ fiatAmount = tangemPayInput.fiatAmount,
+ depositAddress = tangemPayInput.depositAddress,
+ )
+ } else {
+ getSingleCryptoCurrencyStatusUseCase.invokeMultiWalletSync(
+ userWalletId = userWalletId,
+ cryptoCurrencyId = initialCurrencyFrom.id,
+ ).getOrNull()
+ }
val toStatus = initialCurrencyTo?.let {
getSingleCryptoCurrencyStatusUseCase.invokeMultiWalletSync(
userWalletId = userWalletId,
@@ -555,6 +567,7 @@ internal class SwapModel @Inject constructor(
selectedFeeType = dataState.selectedFee?.feeType ?: FeeType.NORMAL,
isReverseSwapPossible = isReverseSwapPossible(),
needApplyFCARestrictions = userCountry.needApplyFCARestrictions(),
+ isForTangemPaySwap = tangemPayInput != null,
)
if (uiState.notifications.any { it is SwapNotificationUM.Error.UnableToCoverFeeWarning }) {
analyticsEventHandler.send(
@@ -574,6 +587,7 @@ internal class SwapModel @Inject constructor(
toTokenStatus = toTokenStatus,
isReverseSwapPossible = isReverseSwapPossible(),
toAccount = dataState.toAccount,
+ isForTangemPaySwap = tangemPayInput != null,
)
}
is SwapState.SwapError -> {
@@ -588,6 +602,7 @@ internal class SwapModel @Inject constructor(
isReverseSwapPossible = isReverseSwapPossible(),
needApplyFCARestrictions = userCountry.needApplyFCARestrictions(),
toAccount = dataState.toAccount,
+ isForTangemPaySwap = tangemPayInput != null,
)
sendErrorAnalyticsEvent(state.error, provider)
}
diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt
index 8387c1b9e1..81772e801c 100644
--- a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt
+++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt
@@ -277,6 +277,7 @@ internal class StateBuilder(
selectedFeeType: FeeType,
isReverseSwapPossible: Boolean,
needApplyFCARestrictions: Boolean,
+ isForTangemPaySwap: Boolean,
): SwapStateHolder {
if (uiStateHolder.sendCardData !is SwapCardState.SwapCardData) return uiStateHolder
if (uiStateHolder.receiveCardData !is SwapCardState.SwapCardData) return uiStateHolder
@@ -369,7 +370,7 @@ internal class StateBuilder(
enabled = getSwapButtonEnabled(notifications),
onClick = actions.onSwapClick,
),
- changeCardsButtonState = if (isReverseSwapPossible) {
+ changeCardsButtonState = if (isReverseSwapPossible && !isForTangemPaySwap) {
ChangeCardsButtonState.ENABLED
} else {
ChangeCardsButtonState.DISABLED
@@ -442,6 +443,7 @@ internal class StateBuilder(
expressDataError: ExpressDataError,
isReverseSwapPossible: Boolean,
needApplyFCARestrictions: Boolean,
+ isForTangemPaySwap: Boolean,
): SwapStateHolder {
if (uiStateHolder.sendCardData !is SwapCardState.SwapCardData) return uiStateHolder
if (uiStateHolder.receiveCardData !is SwapCardState.SwapCardData) return uiStateHolder
@@ -500,7 +502,7 @@ internal class StateBuilder(
enabled = false,
onClick = actions.onSwapClick,
),
- changeCardsButtonState = if (isReverseSwapPossible) {
+ changeCardsButtonState = if (isReverseSwapPossible && !isForTangemPaySwap) {
ChangeCardsButtonState.ENABLED
} else {
ChangeCardsButtonState.DISABLED
@@ -559,6 +561,7 @@ internal class StateBuilder(
toTokenStatus: CryptoCurrencyStatus?,
toAccount: Account.CryptoPortfolio?,
isReverseSwapPossible: Boolean,
+ isForTangemPaySwap: Boolean,
): SwapStateHolder {
if (uiStateHolder.sendCardData !is SwapCardState.SwapCardData) return uiStateHolder
if (uiStateHolder.receiveCardData !is SwapCardState.SwapCardData) return uiStateHolder
@@ -601,7 +604,7 @@ internal class StateBuilder(
enabled = false,
onClick = { },
),
- changeCardsButtonState = if (isReverseSwapPossible) {
+ changeCardsButtonState = if (isReverseSwapPossible && !isForTangemPaySwap) {
ChangeCardsButtonState.ENABLED
} else {
ChangeCardsButtonState.DISABLED
diff --git a/features/tangempay/details/impl/build.gradle.kts b/features/tangempay/details/impl/build.gradle.kts
index f026e5a44f..6a0e6c5358 100644
--- a/features/tangempay/details/impl/build.gradle.kts
+++ b/features/tangempay/details/impl/build.gradle.kts
@@ -18,9 +18,11 @@ dependencies {
implementation(projects.core.error)
implementation(projects.core.navigation)
implementation(projects.core.ui)
+ implementation(projects.core.analytics.models)
/** Common */
implementation(projects.common.ui)
+ implementation(projects.common.routing)
/** Features api */
implementation(projects.features.tangempay.details.api)
diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayAddFundsComponent.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayAddFundsComponent.kt
new file mode 100644
index 0000000000..db43708e08
--- /dev/null
+++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayAddFundsComponent.kt
@@ -0,0 +1,43 @@
+package com.tangem.features.tangempay.components
+
+import androidx.compose.runtime.Composable
+import com.tangem.core.decompose.context.AppComponentContext
+import com.tangem.core.decompose.model.getOrCreateModel
+import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
+import com.tangem.domain.models.wallet.UserWalletId
+import com.tangem.domain.pay.TangemPayTopUpData
+import com.tangem.features.tangempay.model.TangemPayAddFundsModel
+import com.tangem.features.tangempay.ui.TangemPayAddFundsContent
+import java.math.BigDecimal
+
+internal class TangemPayAddFundsComponent(
+ appComponentContext: AppComponentContext,
+ params: Params,
+) : ComposableBottomSheetComponent, AppComponentContext by appComponentContext {
+
+ private val model: TangemPayAddFundsModel = getOrCreateModel(params = params)
+
+ override fun dismiss() {
+ model.onDismiss()
+ }
+
+ @Composable
+ override fun BottomSheet() {
+ TangemPayAddFundsContent(state = model.uiState)
+ }
+
+ data class Params(
+ val listener: AddFundsListener,
+ val walletId: UserWalletId,
+ val cryptoBalance: BigDecimal,
+ val fiatBalance: BigDecimal,
+ val depositAddress: String,
+ val chainId: Int,
+ )
+}
+
+internal interface AddFundsListener {
+ fun onClickReceive(data: TangemPayTopUpData)
+ fun onClickSwap(data: TangemPayTopUpData)
+ fun onDismissAddFunds()
+}
\ No newline at end of file
diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayDetailsComponent.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayDetailsComponent.kt
index f5e03302ab..6f3d6490e8 100644
--- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayDetailsComponent.kt
+++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayDetailsComponent.kt
@@ -87,6 +87,17 @@ internal class TangemPayDetailsComponent(
onDismiss = model.bottomSheetNavigation::dismiss,
),
)
+ is TangemPayDetailsNavigation.AddFunds -> TangemPayAddFundsComponent(
+ appComponentContext = context,
+ params = TangemPayAddFundsComponent.Params(
+ walletId = navigation.walletId,
+ cryptoBalance = navigation.cryptoBalance,
+ fiatBalance = navigation.fiatBalance,
+ depositAddress = navigation.depositAddress,
+ chainId = navigation.chainId,
+ listener = model,
+ ),
+ )
}
}
}
\ No newline at end of file
diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/di/TangemPayModelModule.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/di/TangemPayModelModule.kt
index 912654abc7..2d4297613c 100644
--- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/di/TangemPayModelModule.kt
+++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/di/TangemPayModelModule.kt
@@ -2,6 +2,7 @@ package com.tangem.features.tangempay.di
import com.tangem.core.decompose.di.ModelComponent
import com.tangem.core.decompose.model.Model
+import com.tangem.features.tangempay.model.TangemPayAddFundsModel
import com.tangem.features.tangempay.model.TangemPayAddToWalletModel
import com.tangem.features.tangempay.model.TangemPayCardDetailsBlockModel
import com.tangem.features.tangempay.model.TangemPayChangePinModel
@@ -47,4 +48,9 @@ internal interface TangemPayModelModule {
@IntoMap
@ClassKey(TangemPayAddToWalletModel::class)
fun bindTangemPayAddToWalletModel(model: TangemPayAddToWalletModel): Model
+
+ @Binds
+ @IntoMap
+ @ClassKey(TangemPayAddFundsModel::class)
+ fun bindTangemPayAddFundsModel(model: TangemPayAddFundsModel): Model
}
\ No newline at end of file
diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayAddFundsUM.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayAddFundsUM.kt
new file mode 100644
index 0000000000..701f12b092
--- /dev/null
+++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayAddFundsUM.kt
@@ -0,0 +1,19 @@
+package com.tangem.features.tangempay.entity
+
+import androidx.annotation.DrawableRes
+import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetUMV2
+import com.tangem.core.ui.extensions.TextReference
+import kotlinx.collections.immutable.ImmutableList
+
+internal data class TangemPayAddFundsUM(
+ val items: ImmutableList,
+ val dismiss: () -> Unit,
+ val errorMessage: MessageBottomSheetUMV2?,
+)
+
+internal data class TangemPayAddFundsItemUM(
+ @DrawableRes val iconRes: Int,
+ val title: TextReference,
+ val description: TextReference,
+ val onClick: () -> Unit,
+)
\ No newline at end of file
diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsNavigation.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsNavigation.kt
index 4bf153edca..9e4c9f8854 100644
--- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsNavigation.kt
+++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsNavigation.kt
@@ -1,6 +1,8 @@
package com.tangem.features.tangempay.entity
import com.tangem.domain.models.TokenReceiveConfig
+import com.tangem.domain.models.serialization.SerializedBigDecimal
+import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
import kotlinx.serialization.Serializable
@@ -10,6 +12,15 @@ internal sealed class TangemPayDetailsNavigation {
@Serializable
data class Receive(val config: TokenReceiveConfig) : TangemPayDetailsNavigation()
+ @Serializable
+ data class AddFunds(
+ val walletId: UserWalletId,
+ val cryptoBalance: SerializedBigDecimal,
+ val fiatBalance: SerializedBigDecimal,
+ val depositAddress: String,
+ val chainId: Int,
+ ) : TangemPayDetailsNavigation()
+
@Serializable
data class TransactionDetails(val transaction: TangemPayTxHistoryItem) : TangemPayDetailsNavigation()
}
\ No newline at end of file
diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt
index 16de26202e..102e05307a 100644
--- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt
+++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt
@@ -14,7 +14,7 @@ import kotlinx.collections.immutable.persistentListOf
internal class TangemPayDetailsStateFactory(
private val onBack: () -> Unit,
private val onRefresh: (ShowRefreshState) -> Unit,
- private val onReceive: () -> Unit,
+ private val onAddFunds: () -> Unit,
private val onClickChangePin: () -> Unit,
private val onClickFreezeCard: () -> Unit,
) {
@@ -39,13 +39,14 @@ internal class TangemPayDetailsStateFactory(
balanceBlockState = TangemPayDetailsBalanceBlockState.Loading(
actionButtons = persistentListOf(
ActionButtonConfig(
- text = resourceReference(id = R.string.common_receive),
+ text = resourceReference(id = R.string.tangempay_card_details_add_funds),
iconResId = R.drawable.ic_arrow_down_24,
- onClick = onReceive,
+ onClick = onAddFunds,
),
),
),
addToWalletBlockState = null,
isBalanceHidden = false,
+ addFundsEnabled = true,
)
}
\ No newline at end of file
diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsUM.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsUM.kt
index 9ab8efa762..2e59edf389 100644
--- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsUM.kt
+++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsUM.kt
@@ -11,6 +11,7 @@ internal data class TangemPayDetailsUM(
val balanceBlockState: TangemPayDetailsBalanceBlockState,
val addToWalletBlockState: AddToWalletBlockState?,
val isBalanceHidden: Boolean,
+ val addFundsEnabled: Boolean,
)
internal data class TangemPayCardDetailsUM(
diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayAddFundsModel.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayAddFundsModel.kt
new file mode 100644
index 0000000000..4caef18f17
--- /dev/null
+++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayAddFundsModel.kt
@@ -0,0 +1,40 @@
+package com.tangem.features.tangempay.model
+
+import androidx.compose.runtime.Stable
+import com.tangem.core.decompose.di.ModelScoped
+import com.tangem.core.decompose.model.Model
+import com.tangem.core.decompose.model.ParamsContainer
+import com.tangem.domain.pay.TangemPayTopUpDataFactory
+import com.tangem.features.tangempay.components.TangemPayAddFundsComponent
+import com.tangem.features.tangempay.entity.TangemPayAddFundsUM
+import com.tangem.features.tangempay.model.transformers.TangemPayAddFundsUMConverter
+import com.tangem.utils.coroutines.CoroutineDispatcherProvider
+import javax.inject.Inject
+
+@Stable
+@ModelScoped
+internal class TangemPayAddFundsModel @Inject constructor(
+ paramsContainer: ParamsContainer,
+ override val dispatchers: CoroutineDispatcherProvider,
+ private val tangemPayTopUpDataFactory: TangemPayTopUpDataFactory,
+) : Model() {
+
+ private val params = paramsContainer.require()
+
+ val uiState: TangemPayAddFundsUM = getInitialState()
+
+ private fun getInitialState(): TangemPayAddFundsUM {
+ val data = tangemPayTopUpDataFactory.create(
+ depositAddress = params.depositAddress,
+ chainId = params.chainId,
+ cryptoBalance = params.cryptoBalance,
+ fiatBalance = params.fiatBalance,
+ ).getOrNull()
+
+ return TangemPayAddFundsUMConverter(listener = params.listener).convert(data)
+ }
+
+ fun onDismiss() {
+ params.listener.onDismissAddFunds()
+ }
+}
\ No newline at end of file
diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt
index 3b4c752852..c77a21d442 100644
--- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt
+++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt
@@ -3,6 +3,9 @@ package com.tangem.features.tangempay.model
import androidx.compose.runtime.Stable
import com.arkivanov.decompose.router.slot.SlotNavigation
import com.arkivanov.decompose.router.slot.activate
+import com.arkivanov.decompose.router.slot.dismiss
+import com.tangem.common.routing.AppRoute
+import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
@@ -10,9 +13,11 @@ import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig.ShowRefreshState
import com.tangem.domain.models.TokenReceiveConfig
-import com.tangem.domain.pay.DataForReceiveFactory
+import com.tangem.domain.pay.TangemPayTopUpData
+import com.tangem.domain.pay.model.TangemPayCardBalance
import com.tangem.domain.pay.repository.CardDetailsRepository
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
+import com.tangem.features.tangempay.components.AddFundsListener
import com.tangem.features.tangempay.components.TangemPayDetailsContainerComponent
import com.tangem.features.tangempay.entity.TangemPayDetailsErrorType
import com.tangem.features.tangempay.entity.TangemPayDetailsNavigation
@@ -44,17 +49,16 @@ internal class TangemPayDetailsModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
private val cardDetailsRepository: CardDetailsRepository,
- private val dataForReceiveFactory: DataForReceiveFactory,
private val uiMessageSender: UiMessageSender,
private val cardDetailsEventListener: CardDetailsEventListener,
-) : Model(), TangemPayTxHistoryUiActions {
+) : Model(), TangemPayTxHistoryUiActions, AddFundsListener {
private val params: TangemPayDetailsContainerComponent.Params = paramsContainer.require()
private val stateFactory = TangemPayDetailsStateFactory(
onBack = router::pop,
onRefresh = ::onRefreshSwipe,
- onReceive = ::onClickReceive,
+ onAddFunds = ::onClickAddFunds,
onClickChangePin = ::onClickChangePin,
onClickFreezeCard = ::onClickFreezeCard,
)
@@ -66,6 +70,8 @@ internal class TangemPayDetailsModel @Inject constructor(
private val fetchBalanceJobHolder = JobHolder()
private val addToWalletBannerJobHolder = JobHolder()
+ private var balance: TangemPayCardBalance? = null
+
val bottomSheetNavigation: SlotNavigation = SlotNavigation()
init {
@@ -81,30 +87,27 @@ internal class TangemPayDetailsModel @Inject constructor(
// TODO [REDACTED_JIRA]
}
- private fun onClickReceive() {
- val depositAddress = params.config.depositAddress
- if (depositAddress == null) {
+ private fun onClickAddFunds() {
+ if (params.config.depositAddress == null || balance == null) {
showBottomSheetError(TangemPayDetailsErrorType.Receive)
} else {
- dataForReceiveFactory.getDataForReceive(depositAddress = depositAddress, chainId = params.config.chainId)
- .onRight {
- val config = TokenReceiveConfig(
- shouldShowWarning = false,
- cryptoCurrency = it.currency,
- userWalletId = it.walletId,
- showMemoDisclaimer = false,
- receiveAddress = it.receiveAddress,
- )
- bottomSheetNavigation.activate(TangemPayDetailsNavigation.Receive(config))
- }
- .onLeft { showBottomSheetError(TangemPayDetailsErrorType.Receive) }
+ bottomSheetNavigation.activate(
+ TangemPayDetailsNavigation.AddFunds(
+ walletId = params.userWalletId,
+ fiatBalance = requireNotNull(balance).balance,
+ // Using the fiat balance as crypto balance. This will be changed when back returns crypto balance
+ cryptoBalance = requireNotNull(balance).balance,
+ depositAddress = requireNotNull(params.config.depositAddress),
+ chainId = params.config.chainId,
+ ),
+ )
}
}
private fun fetchBalance(): Job {
return modelScope.launch {
- val result = cardDetailsRepository.getCardBalance()
- uiState.update(DetailsBalanceTransformer(result))
+ val result = cardDetailsRepository.getCardBalance().onRight { balance = it }
+ uiState.update(DetailsBalanceTransformer(balance = result))
}.saveIn(fetchBalanceJobHolder)
}
@@ -147,6 +150,39 @@ internal class TangemPayDetailsModel @Inject constructor(
}.saveIn(addToWalletBannerJobHolder)
}
+ override fun onClickSwap(data: TangemPayTopUpData) {
+ bottomSheetNavigation.dismiss()
+ router.push(
+ AppRoute.Swap(
+ currencyFrom = data.currency,
+ userWalletId = data.walletId,
+ isInitialReverseOrder = true,
+ screenSource = AnalyticsParam.ScreensSources.TangemPay.value,
+ tangemPayInput = AppRoute.Swap.TangemPayInput(
+ cryptoAmount = data.cryptoBalance,
+ fiatAmount = data.fiatBalance,
+ depositAddress = data.depositAddress,
+ ),
+ ),
+ )
+ }
+
+ override fun onClickReceive(data: TangemPayTopUpData) {
+ bottomSheetNavigation.dismiss()
+ val config = TokenReceiveConfig(
+ shouldShowWarning = false,
+ cryptoCurrency = data.currency,
+ userWalletId = data.walletId,
+ showMemoDisclaimer = false,
+ receiveAddress = data.receiveAddress,
+ )
+ bottomSheetNavigation.activate(TangemPayDetailsNavigation.Receive(config))
+ }
+
+ override fun onDismissAddFunds() {
+ bottomSheetNavigation.dismiss()
+ }
+
override fun onTransactionClick(item: TangemPayTxHistoryItem) {
bottomSheetNavigation.activate(TangemPayDetailsNavigation.TransactionDetails(item))
}
diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayAddFundsUMConverter.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayAddFundsUMConverter.kt
new file mode 100644
index 0000000000..1f2c4478f2
--- /dev/null
+++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayAddFundsUMConverter.kt
@@ -0,0 +1,48 @@
+package com.tangem.features.tangempay.model.transformers
+
+import com.tangem.core.ui.extensions.TextReference
+import com.tangem.domain.pay.TangemPayTopUpData
+import com.tangem.features.tangempay.components.AddFundsListener
+import com.tangem.features.tangempay.details.impl.R
+import com.tangem.features.tangempay.entity.TangemPayAddFundsItemUM
+import com.tangem.features.tangempay.entity.TangemPayAddFundsUM
+import com.tangem.features.tangempay.entity.TangemPayDetailsErrorType
+import com.tangem.features.tangempay.utils.TangemPayErrorMessageFactory
+import com.tangem.utils.converter.Converter
+import kotlinx.collections.immutable.persistentListOf
+
+internal class TangemPayAddFundsUMConverter(
+ val listener: AddFundsListener,
+) : Converter {
+
+ override fun convert(value: TangemPayTopUpData?): TangemPayAddFundsUM {
+ return if (value == null) {
+ TangemPayAddFundsUM(
+ items = persistentListOf(),
+ dismiss = listener::onDismissAddFunds,
+ errorMessage = TangemPayErrorMessageFactory.createErrorMessage(
+ type = TangemPayDetailsErrorType.Receive,
+ ).messageBottomSheetUMV2,
+ )
+ } else {
+ TangemPayAddFundsUM(
+ items = persistentListOf(
+ TangemPayAddFundsItemUM(
+ iconRes = R.drawable.ic_exchange_vertical_24,
+ title = TextReference.Res(R.string.common_exchange),
+ description = TextReference.Res(R.string.tangempay_card_details_swap_description),
+ onClick = { listener.onClickSwap(value) },
+ ),
+ TangemPayAddFundsItemUM(
+ iconRes = R.drawable.ic_arrow_down_24,
+ title = TextReference.Res(R.string.common_receive),
+ description = TextReference.Res(R.string.tangempay_card_details_receive_description),
+ onClick = { listener.onClickReceive(value) },
+ ),
+ ),
+ dismiss = listener::onDismissAddFunds,
+ errorMessage = null,
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayAddFundsContent.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayAddFundsContent.kt
new file mode 100644
index 0000000000..badb84e945
--- /dev/null
+++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayAddFundsContent.kt
@@ -0,0 +1,154 @@
+package com.tangem.features.tangempay.ui
+
+import android.content.res.Configuration
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+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.runtime.key
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.vectorResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.tangem.core.ui.components.SpacerH16
+import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
+import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
+import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetV2Content
+import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
+import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
+import com.tangem.core.ui.extensions.TextReference.Res
+import com.tangem.core.ui.extensions.resolveReference
+import com.tangem.core.ui.extensions.resourceReference
+import com.tangem.core.ui.res.TangemTheme
+import com.tangem.core.ui.res.TangemThemePreview
+import com.tangem.features.tangempay.details.impl.R
+import com.tangem.features.tangempay.entity.TangemPayAddFundsItemUM
+import com.tangem.features.tangempay.entity.TangemPayAddFundsUM
+import kotlinx.collections.immutable.persistentListOf
+
+@Composable
+internal fun TangemPayAddFundsContent(state: TangemPayAddFundsUM) {
+ TangemModalBottomSheet(
+ config = TangemBottomSheetConfig(
+ isShown = true,
+ onDismissRequest = state.dismiss,
+ content = TangemBottomSheetConfigContent.Empty,
+ ),
+ onBack = state.dismiss,
+ containerColor = TangemTheme.colors.background.tertiary,
+ title = {
+ TangemModalBottomSheetTitle(
+ title = resourceReference(R.string.tangempay_card_details_add_funds),
+ endIconRes = R.drawable.ic_close_24,
+ onEndClick = state.dismiss,
+ )
+ },
+ ) {
+ if (state.errorMessage != null) {
+ MessageBottomSheetV2Content(state.errorMessage)
+ } else {
+ Surface(
+ modifier = Modifier
+ .padding(horizontal = 16.dp)
+ .fillMaxWidth(),
+ shape = TangemTheme.shapes.roundedCornersXMedium,
+ color = TangemTheme.colors.background.primary,
+ ) {
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.Start,
+ ) {
+ Text(
+ modifier = Modifier.padding(top = 12.dp, start = 16.dp, end = 16.dp),
+ text = resourceReference(R.string.tangempay_card_details_add_funds_subtitle).resolveReference(),
+ color = TangemTheme.colors.text.tertiary,
+ style = TangemTheme.typography.subtitle2,
+ )
+ state.items.forEach {
+ key(it.title) {
+ TangemPayTopUpItem(state = it)
+ }
+ }
+ }
+ }
+ SpacerH16()
+ }
+ }
+}
+
+@Composable
+private fun TangemPayTopUpItem(state: TangemPayAddFundsItemUM, modifier: Modifier = Modifier) {
+ Row(
+ modifier = modifier
+ .fillMaxWidth()
+ .clickable(onClick = state.onClick)
+ .padding(horizontal = 12.dp, vertical = 16.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(12.dp),
+ ) {
+ Box(
+ contentAlignment = Alignment.Center,
+ modifier = Modifier
+ .background(
+ color = TangemTheme.colors.icon.accent.copy(alpha = 0.1f),
+ shape = CircleShape,
+ )
+ .size(36.dp),
+ ) {
+ Icon(
+ modifier = Modifier.size(16.dp),
+ imageVector = ImageVector.vectorResource(id = state.iconRes),
+ contentDescription = null,
+ tint = TangemTheme.colors.icon.accent,
+ )
+ }
+ Column(
+ verticalArrangement = Arrangement.spacedBy(2.dp),
+ ) {
+ Text(
+ text = state.title.resolveReference(),
+ style = TangemTheme.typography.subtitle2,
+ color = TangemTheme.colors.text.primary1,
+ )
+ Text(
+ text = state.description.resolveReference(),
+ style = TangemTheme.typography.caption2,
+ color = TangemTheme.colors.text.tertiary,
+ )
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun TangemPayAddFundsContentPreview() {
+ TangemThemePreview {
+ TangemPayAddFundsContent(
+ state = TangemPayAddFundsUM(
+ items = persistentListOf(
+ TangemPayAddFundsItemUM(
+ iconRes = R.drawable.ic_exchange_vertical_24,
+ title = Res(R.string.common_exchange),
+ description = Res(R.string.exсhange_token_description),
+ onClick = {},
+ ),
+ TangemPayAddFundsItemUM(
+ iconRes = R.drawable.ic_arrow_down_24,
+ title = Res(R.string.common_receive),
+ description = Res(R.string.receive_token_description),
+ onClick = {},
+ ),
+ ),
+ dismiss = {},
+ errorMessage = null,
+ ),
+ )
+ }
+}
\ No newline at end of file
diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreen.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreen.kt
index 3cf7c2e7a6..e3b08338ea 100644
--- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreen.kt
+++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreen.kt
@@ -313,6 +313,7 @@ private class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider