diff --git a/.gitignore b/.gitignore
index 0144ddc6e7..a4d9c4c73d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,14 @@
# Built application files
/build
/buildSrc
+**/build/.transforms/**
+**/build/classes/**
+**/build/generated/**
+**/build/intermediates/**
+**/build/kotlin/**
+**/build/libs/**
+**/build/outputs/**
+**/build/tmp/**
# Local configuration file (sdk path, etc)
local.properties
diff --git a/app/src/main/java/com/tangem/tap/di/domain/OnrampDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/OnrampDomainModule.kt
index 335ce4196c..f5c4fda538 100644
--- a/app/src/main/java/com/tangem/tap/di/domain/OnrampDomainModule.kt
+++ b/app/src/main/java/com/tangem/tap/di/domain/OnrampDomainModule.kt
@@ -120,6 +120,12 @@ internal object OnrampDomainModule {
return OnrampSaveTransactionUseCase(onrampTransactionRepository, onrampErrorResolver)
}
+ @Provides
+ @Singleton
+ fun provideOnrampSepaAvailableUseCase(onrampRepository: OnrampRepository): OnrampSepaAvailableUseCase {
+ return OnrampSepaAvailableUseCase(onrampRepository)
+ }
+
@Provides
@Singleton
fun provideOnrampUpdateTransactionStatusUseCase(
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 9a80508e7b..708e4bc745 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
@@ -207,6 +207,7 @@ internal class ChildFactory @Inject constructor(
userWalletId = route.userWalletId,
cryptoCurrency = route.currency,
source = route.source,
+ launchSepa = route.launchSepa,
),
componentFactory = onrampComponentFactory,
)
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 20d91d8960..d2204b5cd1 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
@@ -238,6 +238,7 @@ sealed class AppRoute(val path: String) : Route {
val source: OnrampSource,
val userWalletId: UserWalletId,
val currency: CryptoCurrency,
+ val launchSepa: Boolean = false,
) : AppRoute(path = "/onramp/${userWalletId.stringValue}/${currency.symbol}"), RouteBundleParams {
override fun getBundle(): Bundle = bundle(serializer())
}
diff --git a/core/res/src/main/res/values-es/strings.xml b/core/res/src/main/res/values-es/strings.xml
index c70f5846b2..170d0a6610 100644
--- a/core/res/src/main/res/values-es/strings.xml
+++ b/core/res/src/main/res/values-es/strings.xml
@@ -878,7 +878,7 @@
Memo
Compruebe su conexión de red
Información de tarifa de red no accesible
- Tú envías
+ Envía
Desde
De %s
Límite de gas
@@ -951,10 +951,13 @@
Intercambiar y enviar
¿Continuar con la conversión? Esto borrará sus datos anteriores.
Confirmar Conversión
+ El envío de cualquier otra moneda supondrá su pérdida irreversible.
+ Seleccione la red de destino correcta
Envía cualquier token y lo convertiremos en el camino. Su destinatario obtiene exactamente lo que necesita, sin problemas.
El destinatario recibirá
Al destinatario
Cantidad a recibir
+ El destinatario recibe %s
¿Seguro que desea cancelar la conversión? Se borrarán sus datos anteriores.
Enviar con swap
Transacción enviada
diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml
index 76ddd053e5..ff93a67733 100644
--- a/core/res/src/main/res/values-ja/strings.xml
+++ b/core/res/src/main/res/values-ja/strings.xml
@@ -1043,13 +1043,13 @@
受信トークンを変更してもよろしいですか? 変更すると、以前入力したデータがリセットされます。
トークンの変更
スワップして送信
- 変換を続行しますか? これにより以前のデータは消去されます。
+ スワップを実行しますか?これにより以前のデータは消去されます。
変換を確定
その他の通貨を送信すると、取り返しのつかない損失が発生します。
正しい受信者ネットワークを選択してください
- 受け取るトークンを自由に選んでください。受信者は、あなたが選択したトークンをシームレスに受け取ります。
- 受信者は受け取ります
- 受取人へ
+ 受け取るトークンを選んでください。受信者は、あなたが選択したトークンをシームレスに受け取ります。
+ 受信者が受け取ります
+ 受信者へ
受取金額
受信者は%sを取得します
変換をキャンセルしてもよろしいですか?以前のデータは消去されます。
diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index 9fda11100a..f21a1bdd98 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -1,5 +1,11 @@
+ Архив
+ Вы архивируете свой аккаунт, но в любое время можете вернуть его обратно
+ Аккаунт
+ Новый аккаунт
+ Продолжить редактирование
+ Несохраненные изменения
Не нашли свой токен? Перейдите в раздел «Рынок» на главной странице и добавьте его в свой портфель для покупки.
Не нашли свой токен? Перейдите в раздел «Рынок» на главной странице и добавьте его в свой портфель для продажи.
Продать
@@ -309,6 +315,7 @@
Legacy адрес
Получить активы
Отправка средств в другой сети может повлечь потерю средств.
+ %s сети
Отправляйте средства, используя только
Привет, команда поддержки, у меня возникла ошибка с кодом: %s
Ошибка WalletConnect
@@ -410,7 +417,16 @@
Сканировать
в %s
В сети %s
+ Вы уверены, что хотите прервать процесс создания кода доступа?
+ Аппаратный кошелёк
Будьте в курсе новых функций и новостей
+ Это устройство не может быть использовано для апгрейда, оно уже содержит другой кошелек.
+ Во время операции произошла ошибка.
+ Приватные ключи будут перемещены из приложения в вашу Tangem карту или кольцо
+ Миграция ключей
+ Начать апгрейд
+ Tangem кошелек
+ Сделать апгрейд кошелька до аппаратной версии.
Эта информация была сгенерирована ИИ.\nНажмите здесь, если обнаружили ошибку.
Чтобы изменить код доступа, приложите карту или кольцо как показано выше и не убирайте до окончания операции
Чтобы изменить пароль, приложите карту как показано выше и не убирайте до окончания операции
@@ -928,7 +944,7 @@
Вы уверены, что хотите изменить токен для получения? Это действие сбросит ранее введённые данные.
Изменение токена
Обмен и отправка
- Продолжить с конвертацией? Это действие удалит предыдущие данные
+ Продолжить с обменом? Это действие удалит предыдущие данные
Подтвердить конвертацию
Выберите любой токен к получению. Ваш адресат получит ровно то, что вы выбрали — без лишних сложностей.
Будет получено
@@ -1100,6 +1116,7 @@
Ошибка расчета комиссии. Пожалуйста, отправьте информацию в поддержку.
Вы отправляете
Обмен этой суммы выбранных токенов может вызвать значительные колебания цены и уменьшить получаемую сумму.
+ Высокое влияние на цену
Недостаточно средств
Дать разрешение
Обменять
@@ -1228,9 +1245,11 @@
Получить с 10% скидкой
Получите доступ к более чем 13 000 криптовалют. Покупайте, продавайте, обменивайте и стейкайте в один клик. Свяжите до трех карт для резервного копирования.
Откройте Tangem Wallet
+ Изменить Код Доступа
Получайте уведомления о входящих транзакциях в кошельке и обновлениях Tangem.
В настоящий момент push-уведомления могут не работать на устройствах Huawei. Мы уже работаем над решением этой проблемы и планируем исправление в ближайших обновлениях. Спасибо за понимание!
Уведомления о транзакциях
+ Установить Код Доступа
Настройки кошелька
Tangem
Используйте %s или отсканируйте карту/кольцо, чтобы разблокировать доступ к вашему кошельку
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 14ac369e2e..5fa3e94450 100644
--- a/core/res/src/main/res/values-uk-rUA/strings.xml
+++ b/core/res/src/main/res/values-uk-rUA/strings.xml
@@ -1367,6 +1367,7 @@
Будь ласка, згенеруйте новий URI та спробуйте ще раз
Термін для з’єднання минув
Прогнозовані зміни
+ Пропоновано %s
Поповніть баланс, щоб покрити комісію мережі
Недостатньо %1$s
Додайте %s мережі до вашого портфелю для цього гаманця
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index 1a6894683e..f24d713213 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -1060,7 +1060,7 @@
Are you sure you want to change the receiving token? This will reset your previously entered data.
Changing token
Swap and send
- Proceed with conversion? This will clear your previous data.
+ Proceed with swap? This will clear your previous data.
Confirm Conversion
Sending any other currency will result in its irreversible loss.
Select the correct recipient network
diff --git a/core/ui/src/main/res/drawable/img_notification_sepa.webp b/core/ui/src/main/res/drawable/img_notification_sepa.webp
new file mode 100644
index 0000000000..3ca547ac85
Binary files /dev/null and b/core/ui/src/main/res/drawable/img_notification_sepa.webp differ
diff --git a/core/utils/src/main/java/com/tangem/utils/coroutines/CoroutineExt.kt b/core/utils/src/main/java/com/tangem/utils/coroutines/CoroutineExt.kt
index 901cf071d6..b57c8e0015 100644
--- a/core/utils/src/main/java/com/tangem/utils/coroutines/CoroutineExt.kt
+++ b/core/utils/src/main/java/com/tangem/utils/coroutines/CoroutineExt.kt
@@ -1,6 +1,8 @@
package com.tangem.utils.coroutines
import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
@@ -54,4 +56,25 @@ fun CoroutineScope.launchOnCancellation(block: suspend () -> Unit) {
}
}
}
+}
+
+@Suppress("LongParameterList", "MagicNumber")
+inline fun combine6(
+ flow1: Flow,
+ flow2: Flow,
+ flow3: Flow,
+ flow4: Flow,
+ flow5: Flow,
+ flow6: Flow,
+ crossinline transform: suspend (T1, T2, T3, T4, T5, T6) -> R,
+): Flow = combine(flow1, flow2, flow3, flow4, flow5, flow6) { arr ->
+ @Suppress("UNCHECKED_CAST")
+ transform(
+ arr[0] as T1,
+ arr[1] as T2,
+ arr[2] as T3,
+ arr[3] as T4,
+ arr[4] as T5,
+ arr[5] as T6,
+ )
}
\ No newline at end of file
diff --git a/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt b/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt
index a4216c1fd9..1bbb60d6ea 100644
--- a/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt
+++ b/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt
@@ -261,6 +261,48 @@ internal class DefaultOnrampRepository(
storeOnrampPairs(pairs = onrampPairs.await(), providers = providers.await())
}
+ override suspend fun hasMercuryoSepaMethod(
+ userWallet: UserWallet,
+ currency: OnrampCurrency,
+ country: OnrampCountry,
+ cryptoCurrency: CryptoCurrency,
+ ): Boolean {
+ return withContext(dispatchers.io) {
+ val onrampPairs =
+ safeApiCall(
+ call = {
+ onrampApi.getPairs(
+ userWalletId = userWallet.walletId.stringValue,
+ refCode = ExpressUtils.getRefCode(
+ userWallet = userWallet,
+ appPreferencesStore = appPreferencesStore,
+ ),
+ body = OnrampPairsRequest(
+ fromCurrencyCode = currency.code,
+ countryCode = country.code,
+ to = listOf(
+ OnrampDestinationDTO(
+ contractAddress = cryptoCurrency.getContractAddress(),
+ network = cryptoCurrency.network.backendId,
+ ),
+ ),
+ ),
+ ).bind()
+ },
+ onError = {
+ Timber.w(it, "Unable to fetch onramp pairs")
+ throw it
+ },
+ )
+
+ val mercuryoProvider = onrampPairs.map { it.providers }.flatten()
+ .find { it.providerId == MERCURYO_PROVIDER_ID }
+ val hasSepaMethod = mercuryoProvider?.paymentMethods?.any { it == SEPA_METHOD_ID } ?: false
+
+ hasSepaMethod
+ }
+ }
+
override suspend fun fetchQuotes(userWallet: UserWallet, cryptoCurrency: CryptoCurrency, amount: Amount) =
withContext(dispatchers.io) {
val pairs = requireNotNull(pairsStore.getSyncOrNull(PAIRS_KEY)) {
@@ -554,5 +596,8 @@ internal class DefaultOnrampRepository(
const val PROVIDER_THEME_LIGHT = "light"
const val REDIRECT_URL = "https://tangem.com/onramp"
+
+ const val SEPA_METHOD_ID = "sepa"
+ const val MERCURYO_PROVIDER_ID = "mercuryo"
}
}
\ No newline at end of file
diff --git a/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt b/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt
index efdaae18b9..f58b3d0613 100644
--- a/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt
+++ b/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt
@@ -38,24 +38,19 @@ internal class DefaultPromoRepository(
key = PreferencesKeys.getShouldShowPromoKey(promoId = promoId.name),
default = true,
).map { shouldShow ->
- if (promoId == PromoId.Referral) {
- runCatching {
+ when (promoId) {
+ PromoId.Referral -> runCatching {
!referralRepository.isReferralParticipant(userWalletId) && shouldShow
}.getOrDefault(false)
- } else {
- shouldShow
+ PromoId.Sepa -> shouldShow
}
}
}
override fun isReadyToShowTokenPromo(promoId: PromoId): Flow {
- return if (promoId == PromoId.Referral) {
- flowOf(false)
- } else {
- appPreferencesStore.get(
- PreferencesKeys.getShouldShowPromoKey(promoId = promoId.name),
- default = false,
- )
+ return when (promoId) {
+ PromoId.Referral -> flowOf(false)
+ PromoId.Sepa -> flowOf(false)
}
}
diff --git a/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampSource.kt b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampSource.kt
index 86b72907ad..e193dbc3b2 100644
--- a/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampSource.kt
+++ b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampSource.kt
@@ -6,4 +6,5 @@ enum class OnrampSource(val analyticsName: String) {
TOKEN_LONG_TAP("Long Tap"),
TOKEN_DETAILS("Token"),
MARKETS("Markets"),
+ SEPA_BANNER("SEPA Banner"),
}
\ No newline at end of file
diff --git a/domain/onramp/src/main/java/com/tangem/domain/onramp/OnrampSepaAvailableUseCase.kt b/domain/onramp/src/main/java/com/tangem/domain/onramp/OnrampSepaAvailableUseCase.kt
new file mode 100644
index 0000000000..fb43156695
--- /dev/null
+++ b/domain/onramp/src/main/java/com/tangem/domain/onramp/OnrampSepaAvailableUseCase.kt
@@ -0,0 +1,80 @@
+package com.tangem.domain.onramp
+
+import arrow.core.Either
+import arrow.core.getOrElse
+import com.tangem.domain.models.currency.CryptoCurrency
+import com.tangem.domain.onramp.repositories.OnrampRepository
+import com.tangem.domain.models.wallet.UserWallet
+import com.tangem.domain.onramp.model.OnrampCountry
+import com.tangem.domain.onramp.model.OnrampCurrency
+
+class OnrampSepaAvailableUseCase(
+ private val repository: OnrampRepository,
+) {
+
+ suspend operator fun invoke(
+ userWallet: UserWallet,
+ currency: OnrampCurrency,
+ country: OnrampCountry,
+ cryptoCurrency: CryptoCurrency,
+ ): Boolean {
+ if (country.code !in SEPA_AVAILABLE_COUNTRY_CODES) {
+ return false
+ }
+
+ return Either.catch {
+ repository.hasMercuryoSepaMethod(
+ userWallet = userWallet,
+ currency = currency,
+ country = country,
+ cryptoCurrency = cryptoCurrency,
+ )
+ }.getOrElse { false }
+ }
+
+ companion object {
+ val SEPA_AVAILABLE_COUNTRY_CODES = listOf(
+ "AL", // Albania
+ "AD", // Andorra
+ "AT", // Austria
+ "BE", // Belgium
+ "BG", // Bulgaria
+ "HR", // Croatia
+ "CY", // Cyprus
+ "CZ", // Czech Republic
+ "DK", // Denmark
+ "EE", // Estonia
+ "FI", // Finland
+ "FR", // France
+ "DE", // Germany
+ "GR", // Greece
+ "HU", // Hungary
+ "IS", // Iceland
+ "IE", // Ireland
+ "IT", // Italy
+ "LV", // Latvia
+ "LI", // Liechtenstein
+ "LT", // Lithuania
+ "LU", // Luxembourg
+ "MT", // Malta
+ "MD", // Moldova
+ "MC", // Monaco
+ "ME", // Montenegro
+ "NL", // Netherlands
+ "MK", // North Macedonia
+ "NO", // Norway
+ "PL", // Poland
+ "PT", // Portugal
+ "RO", // Romania
+ "SM", // San Marino
+ "RS", // Serbia
+ "SK", // Slovakia
+ "SI", // Slovenia
+ "ES", // Spain
+ "SE", // Sweden
+ "CH", // Switzerland
+ "GB", // United Kingdom
+ "VA", // Vatican City
+ )
+ }
+}
\ No newline at end of file
diff --git a/domain/onramp/src/main/java/com/tangem/domain/onramp/repositories/OnrampRepository.kt b/domain/onramp/src/main/java/com/tangem/domain/onramp/repositories/OnrampRepository.kt
index b4879d8cc7..0a7ad02041 100644
--- a/domain/onramp/src/main/java/com/tangem/domain/onramp/repositories/OnrampRepository.kt
+++ b/domain/onramp/src/main/java/com/tangem/domain/onramp/repositories/OnrampRepository.kt
@@ -15,6 +15,12 @@ interface OnrampRepository {
suspend fun getCountriesSync(): List?
suspend fun getCountryByIp(userWallet: UserWallet): OnrampCountry
suspend fun getStatus(userWallet: UserWallet, txId: String): OnrampStatus
+ suspend fun hasMercuryoSepaMethod(
+ userWallet: UserWallet,
+ currency: OnrampCurrency,
+ country: OnrampCountry,
+ cryptoCurrency: CryptoCurrency,
+ ): Boolean
suspend fun fetchCurrencies(userWallet: UserWallet)
suspend fun fetchCountries(userWallet: UserWallet): List
suspend fun fetchPaymentMethodsIfAbsent(userWallet: UserWallet)
diff --git a/domain/promo/models/src/main/java/com/tangem/domain/promo/models/PromoBanner.kt b/domain/promo/models/src/main/java/com/tangem/domain/promo/models/PromoBanner.kt
index 81f592f146..7605016350 100644
--- a/domain/promo/models/src/main/java/com/tangem/domain/promo/models/PromoBanner.kt
+++ b/domain/promo/models/src/main/java/com/tangem/domain/promo/models/PromoBanner.kt
@@ -27,4 +27,5 @@ data class PromoBanner(
enum class PromoId {
Referral,
+ Sepa,
}
\ No newline at end of file
diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenSwapPromoAnalyticsEvent.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenSwapPromoAnalyticsEvent.kt
index 7ed6c36cc2..f460d229ff 100644
--- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenSwapPromoAnalyticsEvent.kt
+++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenSwapPromoAnalyticsEvent.kt
@@ -9,24 +9,24 @@ sealed class TokenSwapPromoAnalyticsEvent(
) : AnalyticsEvent(category = "Promotion", event = event, params = params) {
class NoticePromotionBanner(
source: AnalyticsParam.ScreensSources,
- programName: ProgramName,
+ program: Program,
) : TokenSwapPromoAnalyticsEvent(
event = "Notice - Promotion Banner",
params = mapOf(
AnalyticsParam.SOURCE to source.value,
- "Program Name" to programName.name,
+ "Program Name" to program.programName,
),
)
class PromotionBannerClicked(
source: AnalyticsParam.ScreensSources,
- programName: ProgramName,
+ program: Program,
action: BannerAction,
) : TokenSwapPromoAnalyticsEvent(
event = "Promo Banner Clicked",
params = mapOf(
AnalyticsParam.SOURCE to source.value,
- "Program Name" to programName.name,
+ "Program Name" to program.programName,
"Action" to action.action,
),
) {
@@ -37,7 +37,8 @@ sealed class TokenSwapPromoAnalyticsEvent(
}
// Use it on new promo action
- enum class ProgramName {
- Empty,
+ enum class Program(val programName: String) {
+ Empty("Empty"),
+ Sepa("Sepa"),
}
}
\ No newline at end of file
diff --git a/features/onramp/api/src/main/kotlin/com/tangem/features/onramp/component/OnrampComponent.kt b/features/onramp/api/src/main/kotlin/com/tangem/features/onramp/component/OnrampComponent.kt
index 7bdb6d8901..3de1a19fb7 100644
--- a/features/onramp/api/src/main/kotlin/com/tangem/features/onramp/component/OnrampComponent.kt
+++ b/features/onramp/api/src/main/kotlin/com/tangem/features/onramp/component/OnrampComponent.kt
@@ -8,7 +8,12 @@ import com.tangem.domain.models.wallet.UserWalletId
interface OnrampComponent : ComposableContentComponent {
- data class Params(val userWalletId: UserWalletId, val cryptoCurrency: CryptoCurrency, val source: OnrampSource)
+ data class Params(
+ val userWalletId: UserWalletId,
+ val cryptoCurrency: CryptoCurrency,
+ val source: OnrampSource,
+ val launchSepa: Boolean = false,
+ )
interface Factory : ComponentFactory
}
\ No newline at end of file
diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/OnrampMainComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/OnrampMainComponent.kt
index 57c298a510..fa5cf89457 100644
--- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/OnrampMainComponent.kt
+++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/OnrampMainComponent.kt
@@ -15,6 +15,7 @@ internal interface OnrampMainComponent : ComposableContentComponent {
val source: OnrampSource,
val openSettings: () -> Unit,
val openRedirectPage: (quote: OnrampProviderWithQuote.Data) -> Unit,
+ val launchSepa: Boolean,
)
interface Factory : ComponentFactory
diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/OnrampStateFactory.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/OnrampStateFactory.kt
index 8eb611e8fe..bfd3db959d 100644
--- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/OnrampStateFactory.kt
+++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/OnrampStateFactory.kt
@@ -148,4 +148,8 @@ internal class OnrampStateFactory(
decimals = currency.precision,
type = AmountType.FiatType(currency.code),
)
+
+ companion object {
+ const val PREDEFINED_SEPA_AMOUNT = "100"
+ }
}
\ No newline at end of file
diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt
index 50acb8a5a6..f78daf8e79 100644
--- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt
+++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt
@@ -28,6 +28,7 @@ import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.features.onramp.main.OnrampMainComponent
import com.tangem.features.onramp.main.entity.*
import com.tangem.features.onramp.main.entity.factory.OnrampStateFactory
+import com.tangem.features.onramp.main.entity.factory.OnrampStateFactory.Companion.PREDEFINED_SEPA_AMOUNT
import com.tangem.features.onramp.main.entity.factory.amount.OnrampAmountStateFactory
import com.tangem.features.onramp.providers.entity.SelectProviderResult
import com.tangem.features.onramp.utils.sendOnrampErrorEvent
@@ -64,6 +65,8 @@ internal class OnrampMainComponentModel @Inject constructor(
private val params: OnrampMainComponent.Params = paramsContainer.require()
+ private var isSepaLaunched = false
+
val userWallet = getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId }
private val stateFactory = OnrampStateFactory(
@@ -148,11 +151,16 @@ internal class OnrampMainComponentModel @Inject constructor(
if (country == null) return@onEach
_state.update {
if (it is OnrampMainComponentUM.InitialLoading) {
- stateFactory.getReadyState(country.defaultCurrency)
+ val state = stateFactory.getReadyState(country.defaultCurrency)
+ if (params.launchSepa) {
+ onAmountValueChanged(PREDEFINED_SEPA_AMOUNT)
+ }
+ state
} else {
amountStateFactory.getUpdatedCurrencyState(country.defaultCurrency)
}
}
+
updatePairsAndQuotes()
},
)
@@ -317,8 +325,18 @@ internal class OnrampMainComponentModel @Inject constructor(
private fun selectOrUpdateQuote(quotes: List): OnrampQuote? {
val quoteToCheck = quotes.firstOrNull { it !is OnrampQuote.Error }
+ val sepaQuote = if (params.launchSepa && !isSepaLaunched) {
+ isSepaLaunched = true
+
+ quotes.filterIsInstance().firstOrNull {
+ it.provider.id == MERCURYO_PROVIDER_ID && it.paymentMethod.id == SEPA_METHOD_ID
+ }
+ } else {
+ null
+ }
+
// Check if amount, country or currency has changed
- val newQuote = if (checkLastInputState(quoteToCheck)) {
+ val newQuote = sepaQuote ?: if (checkLastInputState(quoteToCheck)) {
quoteToCheck
} else {
val state = state.value as? OnrampMainComponentUM.Content
@@ -420,5 +438,8 @@ internal class OnrampMainComponentModel @Inject constructor(
private companion object {
const val UPDATE_DELAY = 10_000L
+
+ const val MERCURYO_PROVIDER_ID = "mercuryo"
+ const val SEPA_METHOD_ID = "sepa"
}
}
\ No newline at end of file
diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/root/DefaultOnrampComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/root/DefaultOnrampComponent.kt
index e08b4a4477..e9ee2b3295 100644
--- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/root/DefaultOnrampComponent.kt
+++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/root/DefaultOnrampComponent.kt
@@ -81,6 +81,7 @@ internal class DefaultOnrampComponent @AssistedInject constructor(
),
)
},
+ launchSepa = params.launchSepa,
),
)
is OnrampChild.RedirectPage -> onrampRedirectComponentFactory.create(
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/analytics/TokenDetailsNotificationsAnalyticsSender.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/analytics/TokenDetailsNotificationsAnalyticsSender.kt
index dbe31e24b9..c02b4801b9 100644
--- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/analytics/TokenDetailsNotificationsAnalyticsSender.kt
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/analytics/TokenDetailsNotificationsAnalyticsSender.kt
@@ -38,7 +38,7 @@ internal class TokenDetailsNotificationsAnalyticsSender(
currency = cryptoCurrency,
)
is TokenDetailsNotification.SwapPromo -> TokenSwapPromoAnalyticsEvent.NoticePromotionBanner(
- programName = TokenSwapPromoAnalyticsEvent.ProgramName.Empty, // Use it on new promo action
+ program = TokenSwapPromoAnalyticsEvent.Program.Empty, // Use it on new promo action
source = AnalyticsParam.ScreensSources.Token,
)
is TokenDetailsNotification.KaspaIncompleteTransactionWarning -> TokenDetailsAnalyticsEvent.Notice.Reveal(
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt
index d0e4d0f29c..795dbe542e 100644
--- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt
@@ -850,7 +850,7 @@ internal class TokenDetailsModel @Inject constructor(
analyticsEventsHandler.send(
TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
source = AnalyticsParam.ScreensSources.Token,
- programName = TokenSwapPromoAnalyticsEvent.ProgramName.Empty, // Use it on new promo action
+ program = TokenSwapPromoAnalyticsEvent.Program.Empty, // Use it on new promo action
action = TokenSwapPromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Closed,
),
)
@@ -863,7 +863,7 @@ internal class TokenDetailsModel @Inject constructor(
analyticsEventsHandler.send(
TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
source = AnalyticsParam.ScreensSources.Token,
- programName = TokenSwapPromoAnalyticsEvent.ProgramName.Empty, // Use it on new promo action
+ program = TokenSwapPromoAnalyticsEvent.Program.Empty, // Use it on new promo action
action = TokenSwapPromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Clicked,
),
)
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt
index 7fae09097d..28d03a317a 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt
@@ -19,6 +19,7 @@ import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
+import com.tangem.domain.onramp.model.OnrampSource
import com.tangem.domain.promo.ShouldShowPromoWalletUseCase
import com.tangem.domain.promo.models.PromoId
import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
@@ -75,7 +76,7 @@ internal interface WalletWarningsClickIntents {
fun onClosePromoClick(promoId: PromoId)
- fun onPromoClick(promoId: PromoId)
+ fun onPromoClick(promoId: PromoId, cryptoCurrency: CryptoCurrency? = null)
fun onSupportClick()
@@ -92,7 +93,7 @@ internal interface WalletWarningsClickIntents {
fun onFinishWalletActivationClick()
}
-@Suppress("LongParameterList")
+@Suppress("LargeClass", "LongParameterList")
@ModelScoped
internal class WalletWarningsClickIntentsImplementor @Inject constructor(
private val stateHolder: WalletStateController,
@@ -260,26 +261,46 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
override fun onClosePromoClick(promoId: PromoId) {
analyticsEventHandler.send(
- if (promoId == PromoId.Referral) {
- MainScreen.ReferralPromoButtonDismiss
- } else {
- TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
+ when (promoId) {
+ PromoId.Referral -> MainScreen.ReferralPromoButtonDismiss
+ PromoId.Sepa -> TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
source = AnalyticsParam.ScreensSources.Main,
- programName = TokenSwapPromoAnalyticsEvent.ProgramName.Empty, // Use it on new promo action
+ program = TokenSwapPromoAnalyticsEvent.Program.Sepa,
action = TokenSwapPromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Closed,
)
},
+
)
modelScope.launch(dispatchers.main) {
shouldShowPromoWalletUseCase.neverToShow(promoId)
}
}
- override fun onPromoClick(promoId: PromoId) {
- if (promoId == PromoId.Referral) {
- val userWallet = getSelectedUserWallet() ?: return
- analyticsEventHandler.send(MainScreen.ReferralPromoButtonParticipate)
- appRouter.push(AppRoute.ReferralProgram(userWalletId = userWallet.walletId))
+ override fun onPromoClick(promoId: PromoId, cryptoCurrency: CryptoCurrency?) {
+ val userWallet = getSelectedUserWallet() ?: return
+ when (promoId) {
+ PromoId.Referral -> {
+ analyticsEventHandler.send(MainScreen.ReferralPromoButtonParticipate)
+ appRouter.push(AppRoute.ReferralProgram(userWalletId = userWallet.walletId))
+ }
+ PromoId.Sepa -> {
+ analyticsEventHandler.send(
+ TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
+ source = AnalyticsParam.ScreensSources.Main,
+ program = TokenSwapPromoAnalyticsEvent.Program.Sepa,
+ action = TokenSwapPromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Clicked,
+ ),
+ )
+ cryptoCurrency ?: return
+ appRouter.push(
+ AppRoute.Onramp(
+ userWalletId = userWallet.walletId,
+ currency = cryptoCurrency,
+ source = OnrampSource.SEPA_BANNER,
+ launchSepa = true,
+ ),
+ )
+ }
}
}
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt
index d75237b768..bd539e7397 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt
@@ -5,7 +5,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.tokens.model.analytics.TokenSwapPromoAnalyticsEvent
-import com.tangem.domain.tokens.model.analytics.TokenSwapPromoAnalyticsEvent.ProgramName
+import com.tangem.domain.tokens.model.analytics.TokenSwapPromoAnalyticsEvent.Program
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
@@ -51,7 +51,11 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
is WalletNotification.NoteMigration -> MainScreen.NotePromo
is WalletNotification.SwapPromo -> TokenSwapPromoAnalyticsEvent.NoticePromotionBanner(
source = AnalyticsParam.ScreensSources.Main,
- programName = ProgramName.Empty, // Use it on new promo action
+ program = Program.Empty, // Use it on new promo action
+ )
+ is WalletNotification.Sepa -> TokenSwapPromoAnalyticsEvent.NoticePromotionBanner(
+ source = AnalyticsParam.ScreensSources.Main,
+ program = Program.Sepa,
)
is WalletNotification.ReferralPromo -> MainScreen.ReferralPromo
is WalletNotification.UnlockWallets -> null // See [SelectedWalletAnalyticsSender]
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt
index a7e173397b..5bd5ebfce1 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt
@@ -1,5 +1,8 @@
+@file:Suppress("MaximumLineLength")
+
package com.tangem.feature.wallet.presentation.wallet.domain
+import arrow.core.getOrElse
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsState
import com.tangem.core.ui.components.notifications.NotificationConfig.IconTint
@@ -14,9 +17,12 @@ import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.tokenlist.TokenList
import com.tangem.domain.models.wallet.UserWallet
+import com.tangem.domain.onramp.GetOnrampCountryUseCase
+import com.tangem.domain.onramp.OnrampSepaAvailableUseCase
import com.tangem.domain.promo.ShouldShowPromoWalletUseCase
import com.tangem.domain.promo.models.PromoId
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
+import com.tangem.domain.tokens.GetCryptoCurrenciesUseCase
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.wallets.models.SeedPhraseNotificationsStatus
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
@@ -24,11 +30,13 @@ import com.tangem.domain.wallets.usecase.SeedPhraseNotificationUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
+import com.tangem.lib.crypto.BlockchainUtils.isBitcoin
+import com.tangem.utils.coroutines.combine6
import com.tangem.utils.extensions.isPositive
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
+import timber.log.Timber
import javax.inject.Inject
@Suppress("LongParameterList")
@@ -41,19 +49,22 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
private val backupValidator: BackupValidator,
private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase,
private val shouldShowPromoWalletUseCase: ShouldShowPromoWalletUseCase,
+ private val getCryptoCurrenciesUseCase: GetCryptoCurrenciesUseCase,
+ private val onrampSepaAvailableUseCase: OnrampSepaAvailableUseCase,
+ private val getOnrampCountryUseCase: GetOnrampCountryUseCase,
) {
- @Suppress("MagicNumber", "MaximumLineLength")
fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): Flow> {
val cardTypesResolver = (userWallet as? UserWallet.Cold)?.scanResponse?.cardTypesResolver
- return combine(
- flow = tokenListStore.getOrThrow(userWallet.walletId),
+ return combine6(
+ flow1 = tokenListStore.getOrThrow(userWallet.walletId),
flow2 = isReadyToShowRateAppUseCase(),
flow3 = isNeedToBackupUseCase(userWallet.walletId),
flow4 = seedPhraseNotificationUseCase(userWalletId = userWallet.walletId),
flow5 = shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.Referral),
- ) { maybeTokenList, isReadyToShowRating, isNeedToBackup, seedPhraseIssueStatus, shouldShowReferralPromo ->
+ flow6 = shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.Sepa),
+ ) { maybeTokenList, isReadyToShowRating, isNeedToBackup, seedPhraseIssueStatus, shouldShowReferralPromo, shouldShowSepaBanner ->
buildList {
addUsedOutdatedDataNotification(maybeTokenList)
@@ -63,6 +74,8 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
addReferralPromoNotification(cardTypesResolver, clickIntents, shouldShowReferralPromo)
+ addSepaPromoNotification(userWallet, clickIntents, shouldShowSepaBanner)
+
addInformationalNotifications(cardTypesResolver, maybeTokenList, clickIntents)
addWarningNotifications(cardTypesResolver, maybeTokenList, isNeedToBackup, clickIntents)
@@ -217,6 +230,39 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
)
}
+ private suspend fun MutableList.addSepaPromoNotification(
+ userWallet: UserWallet,
+ clickIntents: WalletClickIntents,
+ shouldShowSepaPromo: Boolean,
+ ) {
+ val currencies = getCryptoCurrenciesUseCase(userWalletId = userWallet.walletId).getOrElse {
+ Timber.e("Error on getting crypto currency list")
+ return
+ }
+
+ val bitcoinCurrency = currencies.find { isBitcoin(it.network.rawId) } ?: return
+
+ val country = getOnrampCountryUseCase.invokeSync(userWallet).getOrElse {
+ Timber.e("Error on getting onramp country")
+ return
+ }
+
+ val isSepaAvailable = onrampSepaAvailableUseCase(
+ userWallet = userWallet,
+ country = country,
+ currency = country.defaultCurrency,
+ cryptoCurrency = bitcoinCurrency,
+ )
+
+ addIf(
+ element = WalletNotification.Sepa(
+ onCloseClick = { clickIntents.onClosePromoClick(promoId = PromoId.Sepa) },
+ onClick = { clickIntents.onPromoClick(promoId = PromoId.Sepa, bitcoinCurrency) },
+ ),
+ condition = shouldShowSepaPromo && isSepaAvailable,
+ )
+ }
+
private fun MutableList.addWarningNotifications(
cardTypesResolver: CardTypesResolver?,
tokenList: Lce,
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt
index dd6ae1ec2b..f1459ac5d1 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt
@@ -287,4 +287,21 @@ sealed class WalletNotification(val config: NotificationConfig) {
iconSize = 54.dp,
),
)
+
+ data class Sepa(
+ val onCloseClick: () -> Unit,
+ val onClick: () -> Unit,
+ ) : WalletNotification(
+ config = NotificationConfig(
+ title = resourceReference(R.string.notification_sepa_title),
+ subtitle = resourceReference(R.string.notification_sepa_text),
+ iconResId = R.drawable.img_notification_sepa,
+ onCloseClick = onCloseClick,
+ buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
+ text = resourceReference(R.string.notification_sepa_button),
+ onClick = onClick,
+ ),
+ iconSize = 54.dp,
+ ),
+ )
}
\ No newline at end of file