From 2934b42af5fd5548760d0a209b6c00598ba45743 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 2 Sep 2025 12:17:12 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .gitignore | 8 ++ .../tap/di/domain/OnrampDomainModule.kt | 6 ++ .../tangem/tap/routing/utils/ChildFactory.kt | 1 + .../com/tangem/common/routing/AppRoute.kt | 1 + core/res/src/main/res/values-es/strings.xml | 5 +- core/res/src/main/res/values-ja/strings.xml | 8 +- core/res/src/main/res/values-ru/strings.xml | 21 ++++- .../src/main/res/values-uk-rUA/strings.xml | 1 + core/res/src/main/res/values/strings.xml | 2 +- .../res/drawable/img_notification_sepa.webp | Bin 0 -> 14202 bytes .../tangem/utils/coroutines/CoroutineExt.kt | 23 +++++ .../data/onramp/DefaultOnrampRepository.kt | 45 ++++++++++ .../data/promo/DefaultPromoRepository.kt | 17 ++-- .../domain/onramp/model/OnrampSource.kt | 1 + .../onramp/OnrampSepaAvailableUseCase.kt | 80 ++++++++++++++++++ .../onramp/repositories/OnrampRepository.kt | 6 ++ .../tangem/domain/promo/models/PromoBanner.kt | 1 + .../analytics/TokenSwapPromoAnalyticsEvent.kt | 13 +-- .../onramp/component/OnrampComponent.kt | 7 +- .../onramp/main/OnrampMainComponent.kt | 1 + .../main/entity/factory/OnrampStateFactory.kt | 4 + .../main/model/OnrampMainComponentModel.kt | 25 +++++- .../onramp/root/DefaultOnrampComponent.kt | 1 + ...okenDetailsNotificationsAnalyticsSender.kt | 2 +- .../tokendetails/model/TokenDetailsModel.kt | 4 +- .../intents/WalletWarningsClickIntents.kt | 45 +++++++--- .../utils/WalletWarningsAnalyticsSender.kt | 8 +- .../domain/GetMultiWalletWarningsFactory.kt | 56 ++++++++++-- .../wallet/state/model/WalletNotification.kt | 17 ++++ 29 files changed, 360 insertions(+), 49 deletions(-) create mode 100644 core/ui/src/main/res/drawable/img_notification_sepa.webp create mode 100644 domain/onramp/src/main/java/com/tangem/domain/onramp/OnrampSepaAvailableUseCase.kt 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 0000000000000000000000000000000000000000..3ca547ac85f2750c4f514dc03b4342568167571f GIT binary patch literal 14202 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 literal 0 HcmV?d00001 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