diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 810024d550..12ecacf838 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -142,6 +142,17 @@
android:scheme="tangem" />
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/com/tangem/tap/common/pushes/TangemPushNotificationService.kt b/app/src/main/java/com/tangem/tap/common/pushes/TangemPushNotificationService.kt
index 9bdca56b66..8f984c76f0 100644
--- a/app/src/main/java/com/tangem/tap/common/pushes/TangemPushNotificationService.kt
+++ b/app/src/main/java/com/tangem/tap/common/pushes/TangemPushNotificationService.kt
@@ -15,6 +15,7 @@ import coil.executeBlocking
import coil.request.ImageRequest
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
+import com.tangem.core.deeplink.DEEPLINK_KEY
import com.tangem.domain.common.LogConfig
import com.tangem.tap.MainActivity
import com.tangem.tap.common.images.createCoilImageLoader
@@ -36,10 +37,11 @@ internal class TangemPushNotificationService : FirebaseMessagingService() {
val notification = message.notification ?: return
val channelId = notification.channelId ?: TANGEM_CHANNEL_ID
- // TODO refactoring: [REDACTED_JIRA]
- val intent = Intent(applicationContext, MainActivity::class.java)
- intent.putExtra(OnPushClickedIntentHandler.OPENED_FROM_GCM_PUSH, true)
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ val intent = Intent(applicationContext, MainActivity::class.java).apply {
+ putExtra(DEEPLINK_KEY, message.data[DEEPLINK_KEY])
+ putExtra(OnPushClickedIntentHandler.OPENED_FROM_GCM_PUSH, true)
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ }
val pendingIntent = PendingIntent.getActivity(
/* context = */ this,
/* requestCode = */ PUSH_NOTIFICATION_REQUEST_CODE,
diff --git a/app/src/main/java/com/tangem/tap/di/domain/PromoDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/PromoDomainModule.kt
index 9e969c71de..e35089b9e9 100644
--- a/app/src/main/java/com/tangem/tap/di/domain/PromoDomainModule.kt
+++ b/app/src/main/java/com/tangem/tap/di/domain/PromoDomainModule.kt
@@ -1,6 +1,7 @@
package com.tangem.tap.di.domain
import com.tangem.domain.promo.*
+import com.tangem.domain.settings.repositories.SettingsRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@@ -15,14 +16,14 @@ internal object PromoDomainModule {
@Singleton
fun provideShouldShowSwapPromoWalletUseCase(
promoSettingsRepository: PromoRepository,
- ): ShouldShowSwapPromoWalletUseCase {
- return ShouldShowSwapPromoWalletUseCase(promoSettingsRepository)
+ ): ShouldShowPromoWalletUseCase {
+ return ShouldShowPromoWalletUseCase(promoSettingsRepository)
}
@Provides
@Singleton
- fun provideShouldShowSwapPromoTokenUseCase(promoRepository: PromoRepository): ShouldShowSwapPromoTokenUseCase {
- return ShouldShowSwapPromoTokenUseCase(promoRepository)
+ fun provideShouldShowSwapPromoTokenUseCase(promoRepository: PromoRepository): ShouldShowPromoTokenUseCase {
+ return ShouldShowPromoTokenUseCase(promoRepository)
}
@Provides
@@ -33,7 +34,10 @@ internal object PromoDomainModule {
@Provides
@Singleton
- fun provideGetStoryContentUseCase(promoRepository: PromoRepository): GetStoryContentUseCase {
- return GetStoryContentUseCase(promoRepository)
+ fun provideGetStoryContentUseCase(
+ promoRepository: PromoRepository,
+ settingsRepository: SettingsRepository,
+ ): GetStoryContentUseCase {
+ return GetStoryContentUseCase(promoRepository, settingsRepository)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeModel.kt b/app/src/main/java/com/tangem/tap/features/home/HomeModel.kt
index 58f8a67687..91c45b832a 100644
--- a/app/src/main/java/com/tangem/tap/features/home/HomeModel.kt
+++ b/app/src/main/java/com/tangem/tap/features/home/HomeModel.kt
@@ -17,6 +17,8 @@ import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.settings.repositories.SettingsRepository
+import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
+import com.tangem.domain.settings.usercountry.models.UserCountry
import com.tangem.domain.tokens.TokensAction
import com.tangem.domain.wallets.builder.UserWalletBuilder
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
@@ -24,6 +26,7 @@ import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter
import com.tangem.tap.common.analytics.events.IntroductionProcess
import com.tangem.tap.common.analytics.events.Shop
import com.tangem.tap.common.extensions.dispatchNavigationAction
+import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.extensions.onUserWalletSelected
import com.tangem.tap.features.home.redux.HIDE_PROGRESS_DELAY
@@ -33,8 +36,10 @@ import com.tangem.tap.store
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import timber.log.Timber
+import java.util.Locale
import javax.inject.Inject
@Suppress("LongParameterList")
@@ -44,6 +49,7 @@ internal class HomeModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val scanCardProcessor: ScanCardProcessor,
private val saveWalletUseCase: SaveWalletUseCase,
+ private val getUserCountryUseCase: GetUserCountryUseCase,
private val cardSdkConfigRepository: CardSdkConfigRepository,
private val settingsRepository: SettingsRepository,
private val urlOpener: UrlOpener,
@@ -53,6 +59,18 @@ internal class HomeModel @Inject constructor(
private val tangemErrorHandler = TangemTangemErrorsHandler(store)
+ init {
+ getUserCountryUseCase.invoke()
+ .distinctUntilChanged()
+ .filterNotNull()
+ .onEach {
+ val userCountry = it.getOrNull() ?: UserCountry.Other(Locale.getDefault().country)
+ store.dispatchOnMain(HomeAction.UserCountryLoaded(userCountry))
+ }
+ .flowOn(dispatchers.io)
+ .launchIn(modelScope)
+ }
+
fun onScanClick() {
analyticsEventHandler.send(IntroductionProcess.ButtonScanCard())
scanCard()
diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt
index 05ba17df63..9a449e938e 100644
--- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt
+++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt
@@ -1,5 +1,6 @@
package com.tangem.tap.features.home.redux
+import com.tangem.domain.settings.usercountry.models.UserCountry
import kotlinx.coroutines.CoroutineScope
import org.rekotlin.Action
@@ -15,4 +16,6 @@ sealed class HomeAction : Action {
data class ReadCard(val scope: CoroutineScope) : HomeAction()
data class ScanInProgress(val scanInProgress: Boolean) : HomeAction()
+
+ data class UserCountryLoaded(val userCountry: UserCountry) : HomeAction()
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt
index ab41e3d89f..678de45d76 100644
--- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt
+++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt
@@ -1,6 +1,8 @@
package com.tangem.tap.features.home.redux
+import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions
import com.tangem.tap.common.redux.AppState
+import kotlinx.collections.immutable.toImmutableList
import org.rekotlin.Action
object HomeReducer {
@@ -14,6 +16,16 @@ private fun internalReduce(action: Action, appState: AppState): HomeState {
is HomeAction.ScanInProgress -> {
appState.homeState.copy(scanInProgress = action.scanInProgress)
}
+ is HomeAction.UserCountryLoaded -> {
+ val stories = if (action.userCountry.needApplyFCARestrictions()) {
+ getRestrictedStories()
+ } else {
+ Stories.entries
+ }
+ appState.homeState.copy(
+ stories = stories.toImmutableList(),
+ )
+ }
else -> appState.homeState
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt
index 89d3c728a6..b1d45779ba 100644
--- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt
+++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt
@@ -7,7 +7,7 @@ import org.rekotlin.StateType
// todo refactor [REDACTED_TASK_KEY]
data class HomeState(
val scanInProgress: Boolean = false,
- val stories: ImmutableList = Stories.entries.toImmutableList(),
+ val stories: ImmutableList = getRestrictedStories().toImmutableList(),
) : StateType {
val firstStory: Stories get() = stories[0]
@@ -22,4 +22,11 @@ enum class Stories(val duration: Int = 6000) {
Currencies,
Web3,
WalletForEveryone,
+}
+
+/**
+ * For FCA restriction stories
+ */
+fun getRestrictedStories(): List {
+ return Stories.entries.filterNot { it == Stories.Currencies }
}
\ No newline at end of file
diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt
index 83c7a513f0..becb93bd80 100644
--- a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt
+++ b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt
@@ -129,8 +129,12 @@ object PreferencesKeys {
val NOTIFICATIONS_ENABLED_STATES_KEY by lazy { stringPreferencesKey(name = "notificationsEnabledStates") }
// endregion
+ // region Promo
fun getShouldShowStoriesKey(storyId: String) = booleanPreferencesKey("shouldShowStories_$storyId")
+ fun getShouldShowPromoKey(promoId: String) = booleanPreferencesKey("shouldShowPromo_$promoId")
+ // endregion
+
// region Permission
fun getShouldShowPermission(permission: String) = booleanPreferencesKey("shouldShowPushPermission_$permission")
diff --git a/core/deep-links/global/src/main/kotlin/com/tangem/core/deeplink/global/ReferralDeepLink.kt b/core/deep-links/global/src/main/kotlin/com/tangem/core/deeplink/global/ReferralDeepLink.kt
new file mode 100644
index 0000000000..763a6d80f7
--- /dev/null
+++ b/core/deep-links/global/src/main/kotlin/com/tangem/core/deeplink/global/ReferralDeepLink.kt
@@ -0,0 +1,13 @@
+package com.tangem.core.deeplink.global
+
+import com.tangem.core.deeplink.DeepLink
+
+class ReferralDeepLink(
+ val onReceive: () -> Unit,
+) : DeepLink(shouldHandleDelayed = true) {
+ override val uri: String = "tangem://referral"
+
+ override fun onReceive(params: Map) {
+ onReceive()
+ }
+}
\ No newline at end of file
diff --git a/core/deep-links/src/main/kotlin/com/tangem/core/deeplink/DeepLinksRegistry.kt b/core/deep-links/src/main/kotlin/com/tangem/core/deeplink/DeepLinksRegistry.kt
index fc56ae10a0..1d3ceb8718 100644
--- a/core/deep-links/src/main/kotlin/com/tangem/core/deeplink/DeepLinksRegistry.kt
+++ b/core/deep-links/src/main/kotlin/com/tangem/core/deeplink/DeepLinksRegistry.kt
@@ -2,6 +2,11 @@ package com.tangem.core.deeplink
import android.content.Intent
+/**
+ * Key to pass deeplink via intent
+ */
+const val DEEPLINK_KEY = "deeplink"
+
// TODO: Add tests
/**
* Provides functionality to handle deep links.
@@ -44,9 +49,9 @@ interface DeepLinksRegistry {
/**
* Triggers run last launched [Intent] with deeplink handlers that can handle delayed deeplink
- * after handle [Intent] clear that and second time no intent will be handled
+ * of specific [deepLinkClass] after handle [Intent] clear that and second time no intent will be handled
*/
- fun triggerDelayedDeeplink()
+ fun triggerDelayedDeeplink(deepLinkClass: Class)
fun cancelDelayedDeeplink()
}
\ No newline at end of file
diff --git a/core/deep-links/src/main/kotlin/com/tangem/core/deeplink/impl/DefaultDeepLinksRegistry.kt b/core/deep-links/src/main/kotlin/com/tangem/core/deeplink/impl/DefaultDeepLinksRegistry.kt
index 63f6f60d78..ff0c60033b 100644
--- a/core/deep-links/src/main/kotlin/com/tangem/core/deeplink/impl/DefaultDeepLinksRegistry.kt
+++ b/core/deep-links/src/main/kotlin/com/tangem/core/deeplink/impl/DefaultDeepLinksRegistry.kt
@@ -3,6 +3,7 @@ package com.tangem.core.deeplink.impl
import android.content.Intent
import android.net.Uri
import androidx.core.net.toUri
+import com.tangem.core.deeplink.DEEPLINK_KEY
import com.tangem.core.deeplink.DeepLink
import com.tangem.core.deeplink.DeepLinksRegistry
import timber.log.Timber
@@ -10,11 +11,14 @@ import timber.log.Timber
internal class DefaultDeepLinksRegistry : DeepLinksRegistry {
private var registries: List = emptyList()
- private var lastIntent: Intent? = null
+ private var lastDeepLink: Uri? = null
override fun launch(intent: Intent): Boolean {
- lastIntent = intent
- val received = intent.data ?: return false
+ // Try to get deeplink from data (direct deeplink flow)
+ // Otherwise, try to get from extras (notification deeplink flow)
+ val deepLinkExtras = intent.getStringExtra(DEEPLINK_KEY)?.toUri()
+ val received = intent.data ?: deepLinkExtras ?: return false
+ lastDeepLink = received
var hasMatch = false
Timber.i(
@@ -35,7 +39,7 @@ internal class DefaultDeepLinksRegistry : DeepLinksRegistry {
logMatch(hasMatch, expected, received, params)
deepLink.onReceive(params)
- lastIntent = null // clear intent if it was handled
+ lastDeepLink = null // clear deeplink if it was handled
}
if (!hasMatch) {
@@ -100,31 +104,32 @@ internal class DefaultDeepLinksRegistry : DeepLinksRegistry {
)
}
- override fun triggerDelayedDeeplink() {
- if (lastIntent != null) {
- val intent = lastIntent
- val received = intent?.data ?: return
+ override fun triggerDelayedDeeplink(deepLinkClass: Class) {
+ val received = lastDeepLink
+ if (received != null) {
var hasMatch = false
- registries.forEach { deepLink ->
- if (!deepLink.shouldHandleDelayed) return@forEach
- val expected = deepLink.uri.toUri()
- if (!isMatches(expected, received)) return@forEach
- hasMatch = true
+ registries
+ .filterIsInstance(deepLinkClass)
+ .forEach { deepLink ->
+ if (!deepLink.shouldHandleDelayed) return@forEach
+ val expected = deepLink.uri.toUri()
+ if (!isMatches(expected, received)) return@forEach
+ hasMatch = true
- val params = getParams(expected, received)
- logMatch(hasMatch, expected, received, params)
- deepLink.onReceive(params)
- }
+ val params = getParams(expected, received)
+ logMatch(hasMatch, expected, received, params)
+ deepLink.onReceive(params)
+ }
if (!hasMatch) {
logMatch(hasMatch, null, received, null)
}
- lastIntent = null // clear intent in any case handle or not
+ lastDeepLink = null // clear deeplink in any case handle or not
}
}
override fun cancelDelayedDeeplink() {
- lastIntent = null
+ lastDeepLink = null
}
private fun logMatch(hasMatch: Boolean, expected: Uri?, received: Uri?, params: Map?) {
diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml
index cecd9a6bbb..aabccb9ff7 100644
--- a/core/res/src/main/res/values-de/strings.xml
+++ b/core/res/src/main/res/values-de/strings.xml
@@ -310,6 +310,7 @@
Einzahlung wird erwartet
Warten auf Einzahlung...
Rückerstattet
+ Rückerstattung
An dich gesendet
wird an dich versendet...
Gesendet
@@ -514,6 +515,8 @@
Rufe dies auf oder tippe auf die Suchleiste, um Token direkt vom Markt hinzuzufügen
Token hinzufügen
NFC ist auf deinem Gerät nicht verfügbar
+ Über NFT
+ NFT-Vermögenswert
- %d Stück
- %d Stücke
@@ -535,8 +538,14 @@
Token-Standard
Eigenschaften
Keine Ergebnisse. Bitte versuche eine andere Anfrage.
+ Keine Abholung
+ Verfügbar
Netzwerk auswählen
NFT erhalten
+ Du hast dieses Netzwerk noch nicht hinzugefügt. Um NFTs zu empfangen, füge es zum Hauptbildschirm hinzu!
+ Netzwerk nicht hinzugefügt
+ Nicht hinzugefügt
+ NFT senden
Eigenschaften
%1$d NFTs in der %2$d Sammlung
Tippe hier, um das erste NFT zu erhalten
@@ -546,6 +555,9 @@
Das Zielkonto ist nicht aktiv. Sende %s oder mehr, um das Konto zu aktivieren.
Senden Sie Geld an diese Andresse um ein Konto zu erstellen
Das Zielkonto verfügt nicht über eine Vertrauensstellung für das gesendete Asset.
+ Jetzt beitreten
+ Teile Deinen Code – verdiene 5 USDT pro Verkauf. Deine Freunde erhalten 10 % Rabatt.
+ Erhalte BELOHNUNGEN für jeden Freund!
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
@@ -894,6 +906,11 @@
Mit Restake kannst Du Dein Guthaben von einem Validator zu einem anderen verschieben, ohne dass Du den Stake aufheben musst.
Du bist dabei, Dein gesamtes Guthaben zu staken. Wir empfehlen, einen kleinen Betrag übrig zu lassen, um die Netzwerkgebühren für die Aufhebung des Stakes oder das Einfordern von Prämien abzudecken.
Um mit dem Staking bei TON zu beginnen, führe zunächst eine ausgehende Transaktion in beliebiger Höhe durch – dadurch wird Deine Wallet aktiviert.
+ Zusätzlich zur Netzwerkgebühr werden bis zu 0,2 TON benötigt, um alle Schritte der Transaktion abzuschließen. Nicht genutzte Beträge werden nach Abschluss der Transaktion zurückerstattet.
+ Für diesen Vorgang sind zusätzlich zur Netzwerkgebühr 0,2 TON erforderlich. Bitte lade Dein Guthaben auf.
+ 0,2 Ton reserviert
+ Durch diese Aktion werden andere Positionen geschlossen oder gemäß den Netzwerkregeln in den Auszahlungsstatus versetzt.
+ Positionsstatus
Entsperre dein Geld, um es aus dem Staking-Prozess abzuheben. Das Freischalten nimmt %s.
Nach Ablauf der 21-tägigen Bindungsfrist kannst Du über Dein Guthaben verfügen. Die Prämie wird zusammen mit dem ungestaketen Guthaben abgehoben.
Deine Assets stehen Dir nach Ablauf der Frist für die Aufhebung der Bindung %s zur Verfügung.
@@ -955,7 +972,7 @@
Verwende es unterwegs, überall und jederzeit. Keine Kabel oder Batterien. Tippe einfach mit der Karte oder Ring auf dein Telefon, wenn du Kryptowährung benötigst.
Die Wallet für jeden
Lerne Tangem kennen
- Tausche, kaufen Sie NFTs, vergebe Kredite und tätige Einlagen bei mehr als 100 verschiedenen dezentralen Diensten
+ Mehr als 100 Integrationen dezentraler Dienste sind verfügbar
Web 3.0-kompatibel
Zum Fortfahren ist eine eingehende Transaktion von mindestens %1$s erforderlich
Unzureichende Mittel
@@ -1010,7 +1027,7 @@
Blende %s aus
Token ausblenden
Durch das Staking kannst du alle %2$s Tage %1$s verdienen und Belohnungen erhalten
- Verdiene bis zu %s Stakingprämie pro Jahr
+ Staking-Dienst
%1$s Token in %%image%% %2$s Netzwerk
Token in %%image%% %1$s Netzwerk
Der %1$s (%2$s) Token ist die Hauptwährung im %3$s Netzwerk und kann nicht versteckt werden, solange du andere Token dieses Netzwerks in der Liste aktiv hast.
@@ -1215,13 +1232,36 @@
Dies ist eine Testnet-Karte. Sie kann keine Transaktionen verarbeiten und sollte nur zu Test- und Entwicklungszwecken verwendet werden.
Nur für Testzwecke
Der Kontostand ist möglicherweise veraltet. Aktualisiere bittre die Seite.
+ Unbekannte Domäne
+ Trotzdem verbinden
+ Zeitüberschreitungsfehler. Bitte versuche es später erneut.
+ WalletConnect konnte nicht hergestellt werden
+ Diese Domäne kann nicht verifiziert werden. Überprüfe die Anfrage sorgfältig und bestätige diese dann.
+ WalletConnect-Sitzung wurde getrennt
+ Fehlercode: %s. Wenn das Problem weiterhin besteht, wende Dich bitte an unseren Support.
+ Wir haben einen unbekannten Fehler festgestellt.
+ Tangem unterstützt derzeit das erforderliches Netzwerk von %s nicht.
+ Nicht unterstützte Netzwerke
+ Tangem unterstützt ein erforderliches Netzwerk um %s.
+ Verifizierte Domain
+ Wir haben eine Art Problem
+ Netzwerk
+ Netzwerke
+ Wallet
+ Signiere die Transaktionen ohne eine Vorankündigung
+ Genehmigung für Transaktionen anfordern
+ Wird nicht in der Lage sein,
Verbindungen
Alle trennen
Text über die Trennung aller dApps
Alle dApps trennen
+ Füge Deinem Profil für dieses Wallet das Netzwerk %s hinzu
+ Die Wallet verfügt über keine erforderlichen Netzwerke
Neue Verbindung
Verbinde Deine Wallet mit einer anderen dApp
Keine Sitzungen
+ Diese Domain wird von mehreren Sicherheitsanbietern als unsicher eingestuft. Verlasse diese umgehend, um Dein Vermögen zu schützen.
+ Bekanntes Sicherheitsrisiko
Verwerfen
Du hast eine unterbrochene Sicherung. Möchtest du diese fortsetzen?
Ja, fortsetzen
diff --git a/core/res/src/main/res/values-es/strings.xml b/core/res/src/main/res/values-es/strings.xml
index 128949fd13..9bb0a97146 100644
--- a/core/res/src/main/res/values-es/strings.xml
+++ b/core/res/src/main/res/values-es/strings.xml
@@ -514,6 +514,9 @@
La cuenta de destino no está activa. Envíe %s o más para activar la cuenta.
Para crear una cuenta, envíe fondos a esta dirección
La cuenta de destino no tiene una Trustline para el activo que se envía.
+ Únete ahora
+ Comparte tu código y gana 5 USDT por venta. Tu amigo obtiene un 10% de descuento.
+ ¡Obtén RECOMPENSAS por cada amigo!
Deba configurar un único código de acceso para proteger todss sus dispositivos.
Proteger
Puede configurar un código de acceso individual en cada tarjeta más adelante
@@ -922,7 +925,7 @@
Úselo en cualquier lugar, en cualquier momento. Sin cables ni baterías. Solo toque la tarjeta con su teléfono cuando necesites su cripto.
La billetera para todos
Descubra Tangem
- Intercambie, compre NFT, haga préstamos y depósitos en más de 100 servicios descentralizados diferentes
+ Más de 100 integraciones de servicios descentralizados están disponibles
Compatible con Web 3.0
Se requiere una transacción entrante de al menos %1$s para proceder
Fondos insuficientes
@@ -977,7 +980,7 @@
Ocultar %s
Ocultar el token
El staking le permite ganar %1$s y obtener recompensas cada %2$s días
- Gane hasta %s recompensa del staking por año
+ Servicio de Staking
Token de %1$s en la red %%image%% %2$s
Token en la %%image%% red %1$s
El token %1$s (%2$s) es la moneda principal en la red %3$s y no se puede ocultar mientras tengas otros tokens de esta red en la lista
diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml
index 9d695df7c1..3866456cd5 100644
--- a/core/res/src/main/res/values-fr/strings.xml
+++ b/core/res/src/main/res/values-fr/strings.xml
@@ -524,6 +524,9 @@
Le compte de destination est inactif. Envoyez %s ou plus pour activer le compte.
Pour créer un compte, envoyez des fonds à cette adresse
Le compte destinataire n\'a pas de Trustline pour l\'actif envoyé qu\'il tente d\'envoyer.
+ Rejoignez maintenant
+ Partagez votre code et gagnez 5 USDT par vente. Votre ami bénéficie de 10 % de réduction.
+ Recevez des RÉCOMPENSES pour chaque ami !
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
@@ -932,7 +935,7 @@
Utilisez-la en déplacement, n\'importe où, n\'importe quand. Pas de fils ni de piles. Il suffit de taper la carte sur votre téléphone lorsque vous avez besoin de votre crypto.
Le portefeuille pour tous
Découvrez Tangem
- Échangez, achetez des NFT, faites des prêts et des dépôts dans plus de 100 services décentralisés différents
+ Plus de 100 intégrations de services décentralisés sont disponibles
Compatible avec Web 3.0
Une transaction entrante d\'au moins de %1$s est requise pour continuer
Fonds insuffisants
@@ -986,7 +989,7 @@
Masquer %s
Masquer le jeton
Le Staking vous permet d\'en gagner %1$s et d\'obtenir des récompenses tous les %2$s jours
- Gagnez jusqu\'à %s récompense de mise par an
+ Service de Staking
%1$s jeton dans %%image%% %2$s le réseau
Jeton dans le %%image%% %1$s réseau
Le jeton %1$s (%2$s) est la principale devise du réseau %3$s et ne peut pas être masqué tant que vous avez d\'autres jetons de ce réseau dans la liste
diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml
index e41ea53407..31ae60add2 100644
--- a/core/res/src/main/res/values-ja/strings.xml
+++ b/core/res/src/main/res/values-ja/strings.xml
@@ -275,7 +275,7 @@
%sとの交換はできません
提供元
ステータス
- Tangemは、各プロバイダーの条件に従って、サードパーティプロバイダーを介してトークンスワップを提供します。
+ プロバイダーが取引を促進します
プロバイダー
エラーが発生しました。コード: %s
エラー%1$s 。選択したプロバイダーは指定された取引を処理できません。値を%2$sに切り上げるか、変更してください。
@@ -316,11 +316,11 @@
確認が必要です
取引ハッシュを待機中
ウォレットに追加されたすべてのトークンのリスト
- 最良のレートを取得しています...
+ 現在のレートを取得中...
変動レート
スワップ機能を使用すると、プロバイダーの%sに同意したことになります。
スワップ機能を使用すると、プロバイダーの%1$sおよび%2$sに同意したことになります。
- さらに多くのプロバイダーを追加予定です。 \nお楽しみに。
+ さらに多くのプロバイダーが利用可能になる予定です
プロバイダー
ベストレート
最大 %s まで使用可能
@@ -415,6 +415,7 @@
このアセットは現在ウォレットで利用できません
このアセットはこのウォレットでは使用できません。
追加
+ APY %s
利用可能なネットワーク
私のポートフォリオ
マーケット
@@ -439,11 +440,14 @@
7d
全部
経験豊富な買い手
- 格付け
+ 時価総額
並べ替え
上昇率上位
下落率上位
トレンド
+ ステーキングは暗号資産で報酬を受け取る最も簡単な方法です。 %s
+ もっと見る
+ 最大%s APYを獲得
%sについて
- %d取引所
@@ -548,6 +552,7 @@
送信先アカウントが有効ではありません。%s 以上を送信してアカウントを有効にしてください。
アカウントを作成するには、このアドレスに資金を送金してください
送信先アカウントには、送金されるアセットのトラストラインがありません。
+ 今すぐ参加
コードを共有すると、販売ごとに5 USDTを獲得できます。お友達は10%割引になります。
友達への紹介で報酬を獲得しよう!
すべてのデバイスを保護するには、単一のアクセスコードを設定してください。
@@ -646,7 +651,7 @@
同一のカード
アクセスコード
%sで利用可能
- Tangemでの暗号通貨の買付は、サードパーティプロバイダーの条件に基づいて行われます。
+ プロバイダーが取引を促進します
国で検索
利用不可
その他の通貨
@@ -835,16 +840,16 @@
ネットワークルールにより、ステーキング解除の量は%1$s TRX に切り上げられます。
ステーキング解除分を請求する
ステーキングアカウント手数料
- ステーキングアカウントは、ステーキングされたSOLトークンが保管される特別なアカウントです。取引の検証に参加して報酬を得るために、トークンをバリデーターに委任すると、このアカウントが作成されます。ステーキングアカウントの作成には少額の手数料がかかりますが、この手数料はステーキングが完了すると返金されます。
+ ステーキングアカウントとは、ステーキングされたSOLが保管される特別なアカウントです。トークンをバリデーターに委任し、取引の検証に参加して報酬を受け取る際に作成されます。ステーキングアカウントの作成には少額の手数料がかかりますが、ステーキング完了後に返金されます。
年率
ステーキングに参加することで得られる年間収益率。
APR
報酬は毎日自動的にステーキング残高に蓄積されます。
利用可能
平均報酬率
- ステーキングとは?
+ ステーキングの仕組み
%s 推定利益
- 市場評価
+ 市場格付け
指標
%1$sネットワークルールによれば、 %2$sからの請求が可能です。以下の金額は、ステーキング解除時にアカウントに入金されます。
最低要件
@@ -870,15 +875,15 @@
Cardanoネットワークでステーキングする場合、残高全体が使用されます。追加の2ADAは確保され、ステーキング解除後に返却されます。ステーキング中、ADAはロック解除されたままです。
ADAステーキングの詳細
獲得した報酬はあなたのアドレスに直接送られ、すぐ使用可能です。
- 安全にステーキングして報酬を獲得しましょう
- 安全にステーキングして、報酬を毎日獲得しましょう
- 安全にステーキングして、報酬を毎時間獲得しましょう
- 安全にステーキングして、報酬を毎月獲得しましょう
- ステーキングにより%1$sを獲得できます。ステーキング報酬は毎日受け取れます。
- ステーキングにより%1$sを獲得できます。ステーキング報酬は毎時間受け取れます。
- ステーキングにより%1$sを獲得できます。ステーキング報酬は毎月受け取れます。
- ステーキングにより%1$sを獲得できます。ステーキング報酬は毎週受け取れます。
- 安全にステーキングして、報酬を毎週獲得しましょう
+ Tangemでは暗号資産をステーキングできます
+ Tangemでは暗号資産をステーキングできます
+ Tangemでは暗号資産をステーキングできます
+ Tangemでは暗号資産をステーキングできます
+ ステーキングを行うと%1$sを受け取ることができます。ステーキング報酬は毎日受け取れます。
+ ステーキングを行うと%1$sを受け取ることができます。ステーキング報酬は1時間ごとに受け取れます。
+ ステーキングを行うと%1$sを受け取ることができます。ステーキング報酬は毎月受け取れます。
+ ステーキングを行うと%1$sを受け取ることができます。ステーキング報酬は毎週受け取れます。
+ Tangemでは暗号資産をステーキングできます
ステーキング報酬を獲得
残りのステーキング残高が少なすぎてステーキングを解除できません。最小のステーキング解除残高を満たすには、さらにステーキングする必要があります。
ステーキング残高が低いです
@@ -960,7 +965,7 @@
外出先でも、いつでもどこでも使用できます。コードや電池は不要です。暗号資産が必要なときに、カードまたはリングをスマートフォンにタップするだけです。
すべての人のためのウォレット
Tangemのご紹介
- 100種類以上の分散型サービスで、NFTの交換・購入や、借入・預金を行うことができます。
+ 100以上の分散型サービスが利用可能
Web3.0対応
続行するには少なくとも%1$sの受信取引が必要です
残高不足
@@ -1014,8 +1019,8 @@
このトークンをメイン画面から非表示にします。トークンの管理ページからいつでも再度追加できます。
%sを非表示
トークンを非表示
- ステーキングにより、 %1$sを獲得し、 %2$s日ごとに報酬を受け取ることができます。
- 年間最大%sのステーキング報酬を獲得
+ ステーキングすると%1$sを受け取り、 %2$s日ごとに報酬を獲得できます
+ ステーキングサービス
%%image%% %2$s ネットワークの %1$s トークン
%%image%% %1$sネットワーク上のトークン
%1$s ( %2$s ) トークンは%3$sネットワークの主要通貨であり、このネットワーク上の他のトークンがリストにある限り、非表示にすることはできません。
@@ -1039,6 +1044,8 @@
バリデーター: %s
最小%s
最小取引金額は%1$sです。
+ Tronネットワークの人気トークンの手数料は高めです。TRXをステーキングすると、より安く、あるいは無料で取引できます。
+ Tronネットワーク手数料を節約
もう一度やり直してください
同じカードをスキャンしました。ツインウォレットを作成するには、番号%dのカードをスキャンする必要があります。
間違ったツインカードをスキャンしました。別のカードをお試しください。
@@ -1234,6 +1241,7 @@
ネットワーク
ネットワーク
ウォレット
+ 接続されたネットワーク
ウォレットの残高とアクティビティを表示する
通知なしに取引に署名する
取引の承認をリクエストする
@@ -1249,6 +1257,8 @@
新しい接続
ウォレットを別のdAppに接続する
セッションなし
+ このドメインは複数のセキュリティプロバイダーから安全でないとの警告を受けています。あなたの資産を守るため、直ちにアクセスを中止してください。
+ 既知のセキュリティリスク
ウォレットコネクト
破棄
バックアップが中断されました。再開しますか?
diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index c599f5bead..a4e05ae190 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -445,7 +445,7 @@
7д
Все
Опытные трейдеры
- Рейтинг
+ Капитализация
Сортировать по
Лидеры роста
Лидеры падения
@@ -945,7 +945,7 @@
Используйте его на ходу, в любом месте, в любое время. Без проводов и батареек. Как только понадобится крипта, просто приложите карту или кольцо к телефону.
Кошелек для каждого
Встречайте Tangem
- Обменивайте, покупайте NFT, получайте займы и делайте вклады в более чем 100 различных децентрализованных сервисах
+ Более 100 децентрализованных сервисов уже доступны для интеграции
Поддержка Web 3.0
Для отправки требуется входящая транзакция на сумму не менее %1$s
Недостаточно средств
@@ -1000,7 +1000,7 @@
Скрыть %s
Скрыть токен
Стейкинг позволяет вам зарабатывать %1$s и получать вознаграждения каждые %2$s дней
- Зарабатывайте до %s вознаграждений за стейкинг ежегодно
+ Сервис стейкинга
%1$s токен в сети %%image%% %2$s
Токен в сети %%image%% %1$s
Токен %1$s (%2$s) является основной валютой в сети %3$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 130fc7559d..46d6b20fe8 100644
--- a/core/res/src/main/res/values-uk-rUA/strings.xml
+++ b/core/res/src/main/res/values-uk-rUA/strings.xml
@@ -526,6 +526,9 @@
Обліковий запис одержувача не активовано. Надішліть %s або більше, щоб активувати обліковий запис.
Для створення акаунту надішліть кошти на цю адресу
Акаунт одержувача не містить трастлайну для активу, що надсилається.
+ Приєднатися
+ Поділіться промокодом — заробіть 5 USDT з кожної покупки. Ваші друзі отримають знижку 10% на картку Tangem!
+ Отримуй бонуси за кожного друга!
Налаштуйте єдиний код доступу для захисту всіх ваших карток або кілець
Захист
Встановіть індивідуальний код доступу для кожної картки або кільця пізніше.
@@ -942,7 +945,7 @@
Використовуйте його в дорозі, будь-де і будь-коли. Ніяких дротів чи батарейок. Просто прикладіть картку або кільце до телефону, коли вам потрібна криптовалюта.
Гаманець для кожного
Зустрічайте Tangem
- Обмінюйте, купуйте NFT, отримуйте позики та робіть депозити у понад 100 різних децентралізованих сервісах
+ Доступно понад 100 інтеграцій з децентралізованими сервісами
Web 3.0 сумісність
Для відправки потрібна вхідна транзакція на суму не менше %1$s
Недостатньо коштів
@@ -997,7 +1000,7 @@
Приховати %s
Приховати токен
Стейкінг дозволяє заробляти %1$s і отримувати винагороду кожні %2$s днів
- Заробляйте до %s винагород за стейкінг щороку
+ Сервіс стейкінгу
%1$s токен в мережі %%image%% %2$s
Токен у мережі %%image%% %1$s
Токен %1$s (%2$s) є основною валютою в мережі %3$s і не може бути прихований до тих пір, поки у вас в списку є інші токени цієї мережі
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index 3d4d6f5d2a..b4b174998e 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -279,7 +279,7 @@
Cannot be swapped for %s
Provided by
Status
- Tangem offers token swaps via 3rd-party providers according to each provider\'s terms
+ Providers facilitate transactions
Provider
An error occurred. Code: %s
Error %1$s. The selected provider cannot process the specified transaction. Please round the value up to %2$s or change it
@@ -320,11 +320,11 @@
Verification required
Awaiting transaction hash
List of all tokens added to your wallet
- Fetching best rates...
+ Fetching current rates...
Floating rate
By using swap functionality, you agree with provider’s %s
By using swap functionality, you agree with provider’s %1$s and %2$s
- More providers are coming soon.\nStay tuned!
+ More providers will be available soon
Provider
Best rate
Available up to %s
@@ -420,6 +420,7 @@
This asset is currently not supported in the wallet
This asset is not available for this wallet
Add
+ APY %s
Available networks
My portfolio
Market
@@ -444,11 +445,14 @@
7d
All
Experienced buyers
- Rating
+ Capitalization
Sort By
Top Gainers
Top Losers
Trending
+ Staking is the easiest way to receive rewards on your crypto. %s
+ Show more
+ Earn up to %s APY
About %s
- %d exchange
@@ -493,9 +497,9 @@
Market cap
The total market value of a cryptocurrency, calculated by multiplying the current price of the coin by the total number of coins in circulation
Market cap
- Market rating
+ Market position
Position in crypto rating between all coins based on market capitalization
- Market rating
+ Market position
Max supply
The maximum number of coins or tokens that can ever exist for a particular cryptocurrency
Max supply
@@ -531,6 +535,13 @@
Base information
Chain
Contract Address
+ stub
+ stub
+ stub
+ stub
+ stub
+ stub
+ stub
Last sale price
Rarity label
Rarity rank
@@ -538,13 +549,6 @@
Token ID
Token Standard
Traits
-
-
-
-
-
-
-
No results. Please try another request.
No collection
Available
@@ -664,7 +668,7 @@
Identical cards
Access code
Available with %s
- Buying crypto in Tangem is powered by third-party providers on their terms.
+ Providers facilitate transactions
Search by country
Unavailable
Other currencies
@@ -855,16 +859,16 @@
Unstaking amount will be rounded to %1$s TRX due to network rules.
Claim unstaked
Stake account fee
- A staking account is a special account where staked SOL tokens are stored. It is created when you delegate your tokens to a validator to participate in transaction validation and earn rewards. A small fee is charged for creating the staking account, which is returned after the staking is completed.
+ A staking account is a special account where staked SOL tokens are stored. It is created when you delegate your tokens to a validator to participate in transaction validation and receive rewards. A small fee is charged for creating the staking account, which is returned after the staking is completed.
Annual percentage rate
The annual percentage return you can earn from participating in staking.
APR
Rewards automatically accumulate in your staking balance daily.
Available
Average Reward Rate
- What is Staking?
+ How Staking Works?
%s est. profit
- Market rating
+ Market position
Metrics
According to %1$s network rules, claims are possible from %2$s. Amounts below will be credited to your account upon unstaking.
Minimum Requirement
@@ -890,15 +894,15 @@
When staking on the Cardano network, your entire balance is used. An additional 2 ADA will be reserved and returned after unstaking. Your ADA remains unlocked while staking.
ADA Staking Details
Earned rewards will be sent to your wallet and available for use immediately
- Stake securely and start earning your rewards
- Stake securely and start earning daily rewards
- Stake securely and start earning hourly rewards
- Stake securely and start earning monthly rewards
- Staking allows you to earn %1$s. Your staking rewards arrive every day.
- Staking allows you to earn %1$s. Your staking rewards arrive every hour.
- Staking allows you to earn %1$s. Your staking rewards arrive every month.
- Staking allows you to earn %1$s. Your staking rewards arrive every week.
- Stake securely and start earning weekly rewards
+ Tangem allows users to stake their crypto
+ Tangem allows users to stake their crypto
+ Tangem allows users to stake their crypto
+ Tangem allows users to stake their crypto
+ Staking allows you to receive %1$s. Your staking rewards arrive every day.
+ Staking allows you to receive %1$s. Your staking rewards arrive every hour.
+ Staking allows you to receive %1$s. Your staking rewards arrive every month.
+ Staking allows you to receive %1$s. Your staking rewards arrive every week.
+ Tangem allows users to stake their crypto
Earn staking rewards
Your remaining staked balance will be too low to unstake. You’ll need to stake more to meet the minimum unstake amount.
Low staked balance
@@ -980,7 +984,7 @@
Use it on the go, anywhere, anytime. No wires or batteries. Just tap the card or ring to your phone when you need your crypto.
The Wallet for Everyone
Meet Tangem
- Exchange, buy NFT\'s, make loans and deposits in more than 100 different decentralized services
+ More than 100 decentralized service integrations are available
Web 3.0 Compatible
An incoming transaction of at least %1$s is required to proceed
Insufficient funds
@@ -1034,8 +1038,8 @@
You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page.
Hide %s
Hide token
- Staking allows you to earn %1$s and get rewards every %2$s days
- Earn up to %s staking rewards yearly
+ Staking allows you to receive %1$s and get rewards every %2$s days
+ Staking Service
%1$s token in %%image%% %2$s network
Token in %%image%% %1$s network
The %1$s (%2$s) token is the main currency on the %3$s network and cannot be hidden as long as you have other tokens on this network in the list
@@ -1059,6 +1063,8 @@
validator: %s
Minimum %s
The minimum transaction amount is %1$s.
+ Tron network fees for popular tokens are higher. Stake some TRX for cheaper or free transactions.
+ Save on Tron network fees
Try again
You\'ve scanned the same card. To create a twin wallet you need to scan the card with number %d
You\'ve scanned wrong twin card. Please try another one
@@ -1310,8 +1316,9 @@
Will not be able to
Would like to
Connection request
- Transaction request
Connections
+ Contents
+ Copy data
Disconnect all
Text about discnected all dApps
Disconect All dApps
@@ -1322,13 +1329,11 @@
No sessions
This domain is flagged as unsafe by multiple security providers. Leave immediately to protect your assets
Known security risk
- Wallet connect
Request from
- Sign
- Copy data
- Transaction request
Signature Type
- Contents
+ Transaction request
+ Transaction request
+ Wallet connect
Discard
You have an interrupted backup. Do you want to resume?
Yes, resume
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt
index cf66eead2c..4050ff267f 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt
@@ -25,6 +25,8 @@ import androidx.compose.ui.semantics.Role
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
import com.tangem.core.ui.R
import com.tangem.core.ui.components.*
import com.tangem.core.ui.components.buttons.common.TangemButtonSize
@@ -56,6 +58,7 @@ fun Notification(
subtitleColor: Color = TangemTheme.colors.text.tertiary,
containerColor: Color? = null,
iconTint: Color? = null,
+ iconSize: Dp = 20.dp,
isEnabled: Boolean = true,
) {
NotificationBaseContainer(
@@ -69,6 +72,7 @@ fun Notification(
MainContent(
iconResId = config.iconResId,
iconTint = iconTint,
+ iconSize = iconSize,
title = config.title,
titleColor = titleColor,
subtitle = config.subtitle,
@@ -129,6 +133,7 @@ internal fun NotificationBaseContainer(
private fun MainContent(
iconResId: Int,
iconTint: Color?,
+ iconSize: Dp,
title: TextReference?,
subtitle: TextReference,
titleColor: Color,
@@ -140,7 +145,7 @@ private fun MainContent(
iconResId = iconResId,
tint = iconTint,
modifier = Modifier
- .size(size = TangemTheme.dimens.size20)
+ .size(size = iconSize)
.align(alignment = Alignment.CenterVertically),
)
diff --git a/core/ui/src/main/res/drawable/img_referral_promo.webp b/core/ui/src/main/res/drawable/img_referral_promo.webp
new file mode 100644
index 0000000000..25a0af2e67
Binary files /dev/null and b/core/ui/src/main/res/drawable/img_referral_promo.webp differ
diff --git a/data/promo/build.gradle.kts b/data/promo/build.gradle.kts
index e2dd74f43c..7573a1e454 100644
--- a/data/promo/build.gradle.kts
+++ b/data/promo/build.gradle.kts
@@ -22,6 +22,7 @@ dependencies {
implementation(projects.domain.promo)
implementation(projects.domain.promo.models)
implementation(projects.domain.wallets.models)
+ implementation(projects.features.referral.domain)
implementation(projects.core.datasource)
implementation(projects.core.utils)
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 5b14b75e9a..f48446148f 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
@@ -4,16 +4,21 @@ import com.tangem.data.promo.converters.StoryContentResponseConverter
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.local.preferences.AppPreferencesStore
+import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.PreferencesKeys.getShouldShowStoriesKey
import com.tangem.datasource.local.preferences.utils.get
import com.tangem.datasource.local.preferences.utils.getSyncOrDefault
import com.tangem.datasource.local.preferences.utils.store
import com.tangem.datasource.local.promo.PromoStoriesStore
import com.tangem.domain.promo.PromoRepository
+import com.tangem.domain.promo.models.PromoId
import com.tangem.domain.promo.models.StoryContent
+import com.tangem.domain.wallets.models.UserWalletId
+import com.tangem.feature.referral.domain.ReferralRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
@@ -23,24 +28,43 @@ internal class DefaultPromoRepository(
private val appPreferencesStore: AppPreferencesStore,
private val promoStoriesStore: PromoStoriesStore,
private val dispatchers: CoroutineDispatcherProvider,
+ private val referralRepository: ReferralRepository,
) : PromoRepository {
private val storyContentConverter = StoryContentResponseConverter()
- override fun isReadyToShowWalletSwapPromo(): Flow {
- return flowOf(false) // Use it on new promo action
+ override fun isReadyToShowWalletPromo(userWalletId: UserWalletId, promoId: PromoId): Flow {
+ return appPreferencesStore.get(
+ key = PreferencesKeys.getShouldShowPromoKey(promoId = promoId.name),
+ default = true,
+ ).map { shouldShow ->
+ if (promoId == PromoId.Referral) {
+ runCatching {
+ !referralRepository.isReferralParticipant(userWalletId) && shouldShow
+ }.getOrDefault(false)
+ } else {
+ shouldShow
+ }
+ }
}
- override fun isReadyToShowTokenSwapPromo(): Flow {
- return flowOf(false) // Use it on new promo action
+ override fun isReadyToShowTokenPromo(promoId: PromoId): Flow {
+ return if (promoId == PromoId.Referral) {
+ flowOf(false)
+ } else {
+ appPreferencesStore.get(
+ PreferencesKeys.getShouldShowPromoKey(promoId = promoId.name),
+ default = false,
+ )
+ }
}
- override suspend fun setNeverToShowWalletSwapPromo() {
- // Use it on new promo action
+ override suspend fun setNeverToShowWalletPromo(promoId: PromoId) {
+ appPreferencesStore.store(PreferencesKeys.getShouldShowPromoKey(promoId = promoId.name), false)
}
- override suspend fun setNeverToShowTokenSwapPromo() {
- // Use it on new promo action
+ override suspend fun setNeverToShowTokenPromo(promoId: PromoId) {
+ appPreferencesStore.store(PreferencesKeys.getShouldShowPromoKey(promoId = promoId.name), false)
}
override fun getStoryById(id: String): Flow = isReadyToShowStories(id).mapLatest {
diff --git a/data/promo/src/main/java/com/tangem/data/promo/di/PromoDataModule.kt b/data/promo/src/main/java/com/tangem/data/promo/di/PromoDataModule.kt
index a564ef0509..69d489a2dc 100644
--- a/data/promo/src/main/java/com/tangem/data/promo/di/PromoDataModule.kt
+++ b/data/promo/src/main/java/com/tangem/data/promo/di/PromoDataModule.kt
@@ -5,6 +5,7 @@ import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.promo.PromoStoriesStore
import com.tangem.domain.promo.PromoRepository
+import com.tangem.feature.referral.domain.ReferralRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
@@ -23,12 +24,14 @@ internal object PromoDataModule {
appPreferencesStore: AppPreferencesStore,
promoStoriesStore: PromoStoriesStore,
dispatchers: CoroutineDispatcherProvider,
+ referralRepository: ReferralRepository,
): PromoRepository {
return DefaultPromoRepository(
tangemApi = tangemTechApi,
appPreferencesStore = appPreferencesStore,
promoStoriesStore = promoStoriesStore,
dispatchers = dispatchers,
+ referralRepository = referralRepository,
)
}
}
\ No newline at end of file
diff --git a/data/settings/src/main/java/com/tangem/data/settings/DefaultSettingsRepository.kt b/data/settings/src/main/java/com/tangem/data/settings/DefaultSettingsRepository.kt
index 1fdbc68282..87ea3f6639 100644
--- a/data/settings/src/main/java/com/tangem/data/settings/DefaultSettingsRepository.kt
+++ b/data/settings/src/main/java/com/tangem/data/settings/DefaultSettingsRepository.kt
@@ -8,12 +8,11 @@ import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getSyncOrDefault
import com.tangem.datasource.local.preferences.utils.store
import com.tangem.domain.settings.repositories.SettingsRepository
+import com.tangem.domain.settings.usercountry.models.GB_COUNTRY
import com.tangem.domain.settings.usercountry.models.UserCountry
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
-import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.util.Locale
@@ -108,15 +107,11 @@ internal class DefaultSettingsRepository(
appPreferencesStore.store(key = PreferencesKeys.SHOULD_SHOW_MARKETS_TOOLTIP_KEY, value = !value)
}
- override suspend fun getUserCountryCodeSync(): UserCountry? {
+ override fun getUserCountryCodeSync(): UserCountry? {
// If user country code is already set, return it
val countryCode = userCountryFlow.value
if (countryCode != null) return countryCode
- coroutineScope {
- launch { fetchUserCountryCode() }
- }
-
return null
}
@@ -125,6 +120,12 @@ internal class DefaultSettingsRepository(
override suspend fun fetchUserCountryCode() {
Timber.i("Start fetching user country code")
+ // for GB locale avoid request geo and use device default (FCA fixes)
+ if (Locale.getDefault().country == GB_COUNTRY.code) {
+ userCountryFlow.value = GB_COUNTRY
+ return
+ }
+
withContext(dispatchers.io) {
val country = runCatching { tangemTechApi.getUserCountryCode() }
.fold(
diff --git a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt
index ba16d9ceb5..0c83963864 100644
--- a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt
+++ b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt
@@ -46,15 +46,16 @@ class SaveManagedTokensUseCase(
userWalletId = userWalletId,
networks = currenciesToAdd.values.flatten(),
)
- val newCurrenciesList = currenciesRepository
- .getMultiCurrencyWalletCurrenciesSync(userWalletId)
+
+ val existingCurrencies = currenciesRepository.getMultiCurrencyWalletCurrenciesSync(userWalletId)
+
+ val newCurrenciesList = existingCurrencies
.filterNot(removingCurrencies::contains)
.toMutableList()
.also { it.addAll(addingCurrencies) }
currenciesRepository.saveNewCurrenciesList(userWalletId, newCurrenciesList)
- val existingCurrencies = currenciesRepository.getMultiCurrencyWalletCurrenciesSync(userWalletId)
removeCurrenciesFromWalletManager(
userWalletId = userWalletId,
currencies = removingCurrencies.filterNot(existingCurrencies::contains),
diff --git a/domain/promo/build.gradle.kts b/domain/promo/build.gradle.kts
index cefe592d97..61d5cb9d12 100644
--- a/domain/promo/build.gradle.kts
+++ b/domain/promo/build.gradle.kts
@@ -6,6 +6,7 @@ plugins {
dependencies {
implementation(projects.domain.promo.models)
implementation(projects.domain.wallets.models)
+ implementation(projects.domain.settings)
implementation(deps.kotlin.coroutines)
implementation(deps.arrow.core)
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 42ecbd01d8..81f592f146 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
@@ -23,4 +23,8 @@ data class PromoBanner(
private companion object {
const val ACTIVE_STATUS = "active"
}
+}
+
+enum class PromoId {
+ Referral,
}
\ No newline at end of file
diff --git a/domain/promo/src/main/java/com/tangem/domain/promo/GetStoryContentUseCase.kt b/domain/promo/src/main/java/com/tangem/domain/promo/GetStoryContentUseCase.kt
index 786f1e9552..03a93bc23a 100644
--- a/domain/promo/src/main/java/com/tangem/domain/promo/GetStoryContentUseCase.kt
+++ b/domain/promo/src/main/java/com/tangem/domain/promo/GetStoryContentUseCase.kt
@@ -4,21 +4,49 @@ import arrow.core.Either
import arrow.core.left
import arrow.core.right
import com.tangem.domain.promo.models.StoryContent
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.catch
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onEmpty
+import com.tangem.domain.promo.models.StoryContentIds
+import com.tangem.domain.settings.repositories.SettingsRepository
+import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions
+import kotlinx.coroutines.flow.*
+import kotlin.time.Duration.Companion.seconds
class GetStoryContentUseCase(
private val promoRepository: PromoRepository,
+ private val settingsRepository: SettingsRepository,
) {
- operator fun invoke(id: String): Flow> = promoRepository.getStoryById(id)
- .map> { it.right() }
- .catch { emit(it.left()) }
- .onEmpty { emit(null.right()) }
+ operator fun invoke(id: String): Flow> {
+ return isFCAAllowed(id).transform { isAllowed ->
+ if (isAllowed) {
+ emitAll(
+ promoRepository.getStoryById(id)
+ .map> { it.right() }
+ .catch { emit(it.left()) }
+ .onEmpty { emit(null.right()) },
+ )
+ } else {
+ emit(null.right())
+ }
+ }
+ }
suspend fun invokeSync(id: String, refresh: Boolean = false): Either = Either.catch {
- promoRepository.getStoryByIdSync(id, refresh)
+ val isFCAAllowed = isFCAAllowed(id).firstOrNull() ?: false
+ return@catch if (isFCAAllowed) {
+ promoRepository.getStoryByIdSync(id, refresh)
+ } else {
+ null
+ }
+ }
+
+ private fun isFCAAllowed(id: String): Flow {
+ return if (id == StoryContentIds.STORY_FIRST_TIME_SWAP.id) {
+ settingsRepository.getUserCountryCode()
+ .filterNotNull()
+ .timeout(5.seconds)
+ .map { !it.needApplyFCARestrictions() }
+ } else {
+ flowOf(true)
+ }
}
}
\ No newline at end of file
diff --git a/domain/promo/src/main/java/com/tangem/domain/promo/PromoRepository.kt b/domain/promo/src/main/java/com/tangem/domain/promo/PromoRepository.kt
index 4db7b9bf87..88682deae2 100644
--- a/domain/promo/src/main/java/com/tangem/domain/promo/PromoRepository.kt
+++ b/domain/promo/src/main/java/com/tangem/domain/promo/PromoRepository.kt
@@ -1,18 +1,20 @@
package com.tangem.domain.promo
+import com.tangem.domain.promo.models.PromoId
import com.tangem.domain.promo.models.StoryContent
+import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.flow.Flow
interface PromoRepository {
// region Promo
- fun isReadyToShowWalletSwapPromo(): Flow
+ fun isReadyToShowWalletPromo(userWalletId: UserWalletId, promoId: PromoId): Flow
- fun isReadyToShowTokenSwapPromo(): Flow
+ fun isReadyToShowTokenPromo(promoId: PromoId): Flow
- suspend fun setNeverToShowWalletSwapPromo()
+ suspend fun setNeverToShowWalletPromo(promoId: PromoId)
- suspend fun setNeverToShowTokenSwapPromo()
+ suspend fun setNeverToShowTokenPromo(promoId: PromoId)
// endregion
// region Stories
diff --git a/domain/promo/src/main/java/com/tangem/domain/promo/ShouldShowPromoTokenUseCase.kt b/domain/promo/src/main/java/com/tangem/domain/promo/ShouldShowPromoTokenUseCase.kt
new file mode 100644
index 0000000000..e6ad0e7580
--- /dev/null
+++ b/domain/promo/src/main/java/com/tangem/domain/promo/ShouldShowPromoTokenUseCase.kt
@@ -0,0 +1,11 @@
+package com.tangem.domain.promo
+
+import com.tangem.domain.promo.models.PromoId
+import kotlinx.coroutines.flow.Flow
+
+class ShouldShowPromoTokenUseCase(private val promoRepository: PromoRepository) {
+
+ operator fun invoke(promoId: PromoId): Flow = promoRepository.isReadyToShowTokenPromo(promoId)
+
+ suspend fun neverToShow(promoId: PromoId) = promoRepository.setNeverToShowTokenPromo(promoId)
+}
\ No newline at end of file
diff --git a/domain/promo/src/main/java/com/tangem/domain/promo/ShouldShowPromoWalletUseCase.kt b/domain/promo/src/main/java/com/tangem/domain/promo/ShouldShowPromoWalletUseCase.kt
new file mode 100644
index 0000000000..4afa0bd2a7
--- /dev/null
+++ b/domain/promo/src/main/java/com/tangem/domain/promo/ShouldShowPromoWalletUseCase.kt
@@ -0,0 +1,19 @@
+package com.tangem.domain.promo
+
+import com.tangem.domain.promo.models.PromoId
+import com.tangem.domain.wallets.models.UserWalletId
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emitAll
+import kotlinx.coroutines.flow.flow
+
+class ShouldShowPromoWalletUseCase(private val promoRepository: PromoRepository) {
+
+ operator fun invoke(userWalletId: UserWalletId, promoId: PromoId): Flow {
+ return flow {
+ emit(false)
+ emitAll(promoRepository.isReadyToShowWalletPromo(userWalletId, promoId))
+ }
+ }
+
+ suspend fun neverToShow(promoId: PromoId) = promoRepository.setNeverToShowWalletPromo(promoId)
+}
\ No newline at end of file
diff --git a/domain/promo/src/main/java/com/tangem/domain/promo/ShouldShowSwapPromoTokenUseCase.kt b/domain/promo/src/main/java/com/tangem/domain/promo/ShouldShowSwapPromoTokenUseCase.kt
deleted file mode 100644
index 9bd3a02d7f..0000000000
--- a/domain/promo/src/main/java/com/tangem/domain/promo/ShouldShowSwapPromoTokenUseCase.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.tangem.domain.promo
-
-import kotlinx.coroutines.flow.Flow
-
-class ShouldShowSwapPromoTokenUseCase(private val promoRepository: PromoRepository) {
-
- operator fun invoke(): Flow = promoRepository.isReadyToShowTokenSwapPromo()
-
- suspend fun neverToShow() = promoRepository.setNeverToShowTokenSwapPromo()
-}
\ No newline at end of file
diff --git a/domain/promo/src/main/java/com/tangem/domain/promo/ShouldShowSwapPromoWalletUseCase.kt b/domain/promo/src/main/java/com/tangem/domain/promo/ShouldShowSwapPromoWalletUseCase.kt
deleted file mode 100644
index 5ed815ec9c..0000000000
--- a/domain/promo/src/main/java/com/tangem/domain/promo/ShouldShowSwapPromoWalletUseCase.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.tangem.domain.promo
-
-import kotlinx.coroutines.flow.Flow
-
-class ShouldShowSwapPromoWalletUseCase(private val promoRepository: PromoRepository) {
-
- operator fun invoke(): Flow = promoRepository.isReadyToShowWalletSwapPromo()
-
- suspend fun neverToShow() = promoRepository.setNeverToShowWalletSwapPromo()
-}
\ No newline at end of file
diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SettingsRepository.kt b/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SettingsRepository.kt
index 047947701d..23b4a1756c 100644
--- a/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SettingsRepository.kt
+++ b/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SettingsRepository.kt
@@ -34,7 +34,7 @@ interface SettingsRepository {
suspend fun setMarketsTooltipShown(value: Boolean)
- suspend fun getUserCountryCodeSync(): UserCountry?
+ fun getUserCountryCodeSync(): UserCountry?
fun getUserCountryCode(): StateFlow
diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/usercountry/GetUserCountryUseCase.kt b/domain/settings/src/main/java/com/tangem/domain/settings/usercountry/GetUserCountryUseCase.kt
index cabd6677a9..3e7f731735 100644
--- a/domain/settings/src/main/java/com/tangem/domain/settings/usercountry/GetUserCountryUseCase.kt
+++ b/domain/settings/src/main/java/com/tangem/domain/settings/usercountry/GetUserCountryUseCase.kt
@@ -31,7 +31,7 @@ class GetUserCountryUseCase(
}
}
- suspend fun invokeSync(): Either {
+ fun invokeSync(): Either {
return either {
val userCountryCode = catch(
block = { settingsRepository.getUserCountryCodeSync() },
diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/usercountry/models/UserCountry.kt b/domain/settings/src/main/java/com/tangem/domain/settings/usercountry/models/UserCountry.kt
index e212b96917..c83164f088 100644
--- a/domain/settings/src/main/java/com/tangem/domain/settings/usercountry/models/UserCountry.kt
+++ b/domain/settings/src/main/java/com/tangem/domain/settings/usercountry/models/UserCountry.kt
@@ -1,5 +1,7 @@
package com.tangem.domain.settings.usercountry.models
+import java.util.Locale
+
/**
* User country
*
@@ -10,4 +12,12 @@ sealed class UserCountry(open val code: String) {
data object Russia : UserCountry("ru")
data class Other(override val code: String) : UserCountry(code)
-}
\ No newline at end of file
+}
+
+fun UserCountry?.needApplyFCARestrictions(): Boolean {
+ val local = Locale.getDefault().country
+ if (local == GB_COUNTRY.code) return true
+ return this?.code.equals(GB_COUNTRY.code, ignoreCase = true)
+}
+
+val GB_COUNTRY = UserCountry.Other(Locale.UK.country)
\ No newline at end of file
diff --git a/domain/tokens/models/build.gradle.kts b/domain/tokens/models/build.gradle.kts
index 9a926341a8..f618dc0f3f 100644
--- a/domain/tokens/models/build.gradle.kts
+++ b/domain/tokens/models/build.gradle.kts
@@ -12,6 +12,7 @@ dependencies {
implementation(projects.domain.models)
implementation(projects.domain.txhistory.models)
implementation(projects.domain.staking.models)
+ implementation(projects.domain.promo.models)
/** Other dependencies */
implementation(deps.kotlin.serialization)
diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyWarning.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyWarning.kt
index 623fbe5c1b..36cd8062dc 100644
--- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyWarning.kt
+++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyWarning.kt
@@ -1,5 +1,6 @@
package com.tangem.domain.tokens.model.warnings
+import com.tangem.domain.promo.models.PromoId
import com.tangem.domain.tokens.model.CryptoCurrency
import org.joda.time.DateTime
import java.math.BigDecimal
@@ -42,6 +43,7 @@ sealed class CryptoCurrencyWarning {
data class Rent(val rent: BigDecimal, val exemptionAmount: BigDecimal) : CryptoCurrencyWarning()
data class SwapPromo(
+ val promoId: PromoId,
val startDateTime: DateTime,
val endDateTime: DateTime,
) : CryptoCurrencyWarning()
diff --git a/features/markets/impl/build.gradle.kts b/features/markets/impl/build.gradle.kts
index 87db7e37d6..fded1c34bc 100644
--- a/features/markets/impl/build.gradle.kts
+++ b/features/markets/impl/build.gradle.kts
@@ -33,6 +33,7 @@ dependencies {
implementation(projects.domain.tokens.models)
implementation(projects.domain.wallets)
implementation(projects.domain.wallets.models)
+ implementation(projects.domain.settings)
// FIXME [REDACTED_TASK_KEY]
// Remove the "Buy" and "Sell" actions from the redux middleware.
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/MarketsTokenDetailsModel.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/MarketsTokenDetailsModel.kt
index 3ba4dd807e..5e4e7a4d4d 100644
--- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/MarketsTokenDetailsModel.kt
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/MarketsTokenDetailsModel.kt
@@ -26,6 +26,9 @@ import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.markets.*
+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.features.markets.details.MarketsTokenDetailsComponent
import com.tangem.features.markets.details.impl.analytics.MarketDetailsAnalyticsEvent
import com.tangem.features.markets.details.impl.model.converters.DescriptionConverter
@@ -49,6 +52,7 @@ import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import org.joda.time.DateTime
import java.math.BigDecimal
+import java.util.Locale
import javax.inject.Inject
@Suppress("LargeClass", "LongParameterList")
@@ -66,9 +70,11 @@ internal class MarketsTokenDetailsModel @Inject constructor(
private val urlOpener: UrlOpener,
private val analyticsEventHandler: AnalyticsEventHandler,
private val excludedBlockchains: ExcludedBlockchains,
+ private val getUserCountryUseCase: GetUserCountryUseCase,
) : Model() {
private var quotesJob = JobHolder()
+ private var userCountry: UserCountry? = null
private val params = paramsContainer.require()
private val analyticsEventBuilder = MarketDetailsAnalyticsEvent.EventBuilder(token = params.token)
@@ -121,6 +127,9 @@ internal class MarketsTokenDetailsModel @Inject constructor(
),
)
},
+ needApplyFCARestrictions = Provider {
+ userCountry.needApplyFCARestrictions()
+ },
// ==================
)
@@ -130,6 +139,9 @@ internal class MarketsTokenDetailsModel @Inject constructor(
// === Analytics ===
analyticsEventHandler.send(analyticsEventBuilder.readMoreClicked())
},
+ needApplyFCARestrictions = Provider {
+ userCountry.needApplyFCARestrictions()
+ },
onGeneratedAINotificationClick = {
modelScope.launch {
sendFeedbackEmailUseCase(
@@ -231,6 +243,8 @@ internal class MarketsTokenDetailsModel @Inject constructor(
private val loadChartJobHolder = JobHolder()
init {
+ userCountry = getUserCountryUseCase.invokeSync().getOrNull()
+ ?: UserCountry.Other(Locale.getDefault().country)
// reload screen if currency changed
modelScope.launch {
currentAppCurrency
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/converters/DescriptionConverter.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/converters/DescriptionConverter.kt
index e2e8e6f985..f1e55176d1 100644
--- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/converters/DescriptionConverter.kt
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/converters/DescriptionConverter.kt
@@ -8,15 +8,18 @@ import com.tangem.domain.markets.TokenMarketInfo
import com.tangem.features.markets.details.impl.ui.state.InfoBottomSheetContent
import com.tangem.features.markets.details.impl.ui.state.MarketsTokenDetailsUM
import com.tangem.features.markets.impl.R
+import com.tangem.utils.Provider
import com.tangem.utils.converter.Converter
@Stable
internal class DescriptionConverter(
private val onReadModeClicked: (InfoBottomSheetContent) -> Unit,
private val onGeneratedAINotificationClick: () -> Unit,
+ private val needApplyFCARestrictions: Provider,
) : Converter {
override fun convert(value: TokenMarketInfo): MarketsTokenDetailsUM.Description? {
+ if (needApplyFCARestrictions()) return null
return value.shortDescription?.let { desc ->
MarketsTokenDetailsUM.Description(
shortDescription = stringReference(desc),
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/converters/TokenMarketInfoConverter.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/converters/TokenMarketInfoConverter.kt
index 85c354906a..8f67567773 100644
--- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/converters/TokenMarketInfoConverter.kt
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/converters/TokenMarketInfoConverter.kt
@@ -16,6 +16,7 @@ import com.tangem.utils.converter.Converter
@Suppress("LongParameterList")
internal class TokenMarketInfoConverter(
private val appCurrency: Provider,
+ private val needApplyFCARestrictions: Provider,
private val onInfoClick: (TangemBottomSheetConfigContent) -> Unit,
private val onListedOnClick: (Int) -> Unit,
onSecurityScoreInfoClick: (SecurityScoreBottomSheetContent) -> Unit,
@@ -49,9 +50,19 @@ internal class TokenMarketInfoConverter(
)
val exchangesAmount = value.exchangesAmount
+ val insights = if (needApplyFCARestrictions()) {
+ null
+ } else {
+ value.insights?.let { insightsConverter.convert(it) }
+ }
+ val securityScore = if (needApplyFCARestrictions()) {
+ null
+ } else {
+ value.securityData?.let { securityScoreConverter.convert(it) }
+ }
return MarketsTokenDetailsUM.InformationBlocks(
- insights = value.insights?.let { insightsConverter.convert(it) },
- securityScore = value.securityData?.let { securityScoreConverter.convert(it) },
+ insights = insights,
+ securityScore = securityScore,
metrics = value.metrics?.let { metricsConverter.convert(it) },
pricePerformance = value.pricePerformance?.let {
pricePerformanceConverter.convert(
diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/SelectProviderModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/SelectProviderModel.kt
index ea142aa30f..6a367639cb 100644
--- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/SelectProviderModel.kt
+++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/SelectProviderModel.kt
@@ -21,6 +21,9 @@ import com.tangem.domain.onramp.analytics.OnrampAnalyticsEvent
import com.tangem.domain.onramp.model.OnrampPaymentMethod
import com.tangem.domain.onramp.model.OnrampProviderWithQuote
import com.tangem.domain.onramp.model.error.OnrampError
+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.features.onramp.impl.R
import com.tangem.features.onramp.paymentmethod.entity.PaymentMethodUM
import com.tangem.features.onramp.providers.SelectProviderComponent
@@ -37,6 +40,7 @@ import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import timber.log.Timber
import java.math.BigDecimal
+import java.util.Locale
import javax.inject.Inject
@Suppress("LongParameterList")
@@ -48,6 +52,7 @@ internal class SelectProviderModel @Inject constructor(
private val getOnrampSelectedPaymentMethodUseCase: GetOnrampSelectedPaymentMethodUseCase,
private val getOnrampProviderWithQuoteUseCase: GetOnrampProviderWithQuoteUseCase,
private val saveSelectedPaymentMethod: OnrampSaveSelectedPaymentMethod,
+ private val getUserCountryUseCase: GetUserCountryUseCase,
paramsContainer: ParamsContainer,
) : Model() {
@@ -56,7 +61,12 @@ internal class SelectProviderModel @Inject constructor(
private val params: SelectProviderComponent.Params = paramsContainer.require()
private val _state = MutableStateFlow(getInitialState())
+ private var userCountry: UserCountry? = null
+
init {
+ userCountry = getUserCountryUseCase.invokeSync().getOrNull()
+ ?: UserCountry.Other(Locale.getDefault().country)
+
analyticsEventHandler.send(OnrampAnalyticsEvent.ProvidersScreenOpened)
getPaymentMethods()
getProviders(params.selectedPaymentMethod)
@@ -202,7 +212,8 @@ internal class SelectProviderModel @Inject constructor(
val rateDiff = bestProvider?.toAmount?.value?.let { bestRate ->
BigDecimal.ONE - quote.toAmount.value / bestRate
}
- val isBestProvider = quote == bestProvider && hasBestProvider
+ val isBestProvider =
+ quote == bestProvider && hasBestProvider && !userCountry.needApplyFCARestrictions()
val providerResult = SelectProviderResult.ProviderWithQuote(
paymentMethod = quote.paymentMethod,
provider = quote.provider,
@@ -253,7 +264,7 @@ internal class SelectProviderModel @Inject constructor(
onClick = {
onProviderSelected(
result = providerResult,
- isBestRate = bestProvider == quote,
+ isBestRate = bestProvider == quote && !userCountry.needApplyFCARestrictions(),
)
params.onDismiss()
},
diff --git a/features/referral/data/src/main/java/com/tangem/feature/referral/data/ReferralRepositoryImpl.kt b/features/referral/data/src/main/java/com/tangem/feature/referral/data/ReferralRepositoryImpl.kt
index f33ab65ce4..279d0632bf 100644
--- a/features/referral/data/src/main/java/com/tangem/feature/referral/data/ReferralRepositoryImpl.kt
+++ b/features/referral/data/src/main/java/com/tangem/feature/referral/data/ReferralRepositoryImpl.kt
@@ -16,6 +16,7 @@ import com.tangem.feature.referral.domain.models.ReferralData
import com.tangem.feature.referral.domain.models.TokenData
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
+import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
@Suppress("LongParameterList")
@@ -29,16 +30,27 @@ internal class ReferralRepositoryImpl @Inject constructor(
private val cryptoCurrencyFactory = CryptoCurrencyFactory(excludedBlockchains)
+ // todo this quick fix of multiple api requests, make proper cache store
+ private val referralStatus: ConcurrentHashMap = ConcurrentHashMap()
+
override suspend fun getReferralData(walletId: String): ReferralData {
return withContext(coroutineDispatcher.io) {
- referralConverter.convert(
+ val referralData = referralConverter.convert(
referralApi.getReferralStatus(
walletId = walletId,
),
)
+
+ referralStatus[walletId] = referralData
+ referralData
}
}
+ override suspend fun isReferralParticipant(userWalletId: UserWalletId): Boolean {
+ val storedReferralData = referralStatus[userWalletId.stringValue] ?: getReferralData(userWalletId.stringValue)
+ return storedReferralData is ReferralData.ParticipantData
+ }
+
override suspend fun startReferral(
walletId: String,
networkId: String,
@@ -46,7 +58,7 @@ internal class ReferralRepositoryImpl @Inject constructor(
address: String,
): ReferralData {
return withContext(coroutineDispatcher.io) {
- referralConverter.convert(
+ val referralData = referralConverter.convert(
referralApi.startReferral(
startReferralBody = StartReferralBody(
walletId = walletId,
@@ -56,6 +68,8 @@ internal class ReferralRepositoryImpl @Inject constructor(
),
),
)
+ referralStatus[walletId] = referralData
+ referralData
}
}
diff --git a/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralRepository.kt b/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralRepository.kt
index 760277f9f8..9c890ff038 100644
--- a/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralRepository.kt
+++ b/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralRepository.kt
@@ -10,6 +10,9 @@ interface ReferralRepository {
/** Returns data object of [ReferralData] depends on user program status */
suspend fun getReferralData(walletId: String): ReferralData
+ /** Returns whether user is participating in referral program */
+ suspend fun isReferralParticipant(userWalletId: UserWalletId): Boolean
+
/** Starts user referral program */
suspend fun startReferral(walletId: String, networkId: String, tokenId: String, address: String): ReferralData
diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/common/SendConfirmAlertFactory.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/common/SendConfirmAlertFactory.kt
index 147ce18f67..78b805b69a 100644
--- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/common/SendConfirmAlertFactory.kt
+++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/common/SendConfirmAlertFactory.kt
@@ -16,11 +16,12 @@ internal class SendConfirmAlertFactory @Inject constructor(
private val messageSender: UiMessageSender,
) {
- fun getGenericErrorState(onFailedTxEmailClick: () -> Unit) {
+ fun getGenericErrorState(onFailedTxEmailClick: () -> Unit, popBack: () -> Unit = {}) {
messageSender.send(
DialogMessage(
title = resourceReference(id = R.string.send_alert_transaction_failed_title),
message = resourceReference(id = R.string.common_unknown_error),
+ onDismissRequest = popBack,
firstAction = EventMessageAction(
title = resourceReference(R.string.common_support),
onClick = onFailedTxEmailClick,
diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt
index af8eb70adc..895ac9ac94 100644
--- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt
+++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt
@@ -162,48 +162,65 @@ internal class DefaultSendComponent @AssistedInject constructor(
),
)
- private fun getAmountComponent(factoryContext: AppComponentContext, route: CommonSendRoute) = SendAmountComponent(
- appComponentContext = factoryContext,
- params = SendAmountComponentParams.AmountParams(
- state = model.uiState.value.amountUM,
- currentRoute = currentRoute.filterIsInstance(),
- isBalanceHidingFlow = model.isBalanceHiddenFlow,
- analyticsCategoryName = analyticCategoryName,
- userWallet = model.userWallet,
- appCurrency = model.appCurrency,
- cryptoCurrencyStatus = model.cryptoCurrencyStatus,
- callback = model,
- predefinedValues = model.predefinedValues,
- onBackClick = {
- if (route.isEditMode) {
- onChildBack()
- } else {
- analyticsEventHandler.send(
- CommonSendAnalyticEvents.CloseButtonClicked(
- categoryName = analyticCategoryName,
- source = SendScreenSource.Amount,
- isFromSummary = false,
- isValid = model.uiState.value.amountUM.isPrimaryButtonEnabled,
- ),
- )
- router.pop()
- }
- },
- onNextClick = {
- if (route.isEditMode) {
- onChildBack()
- } else {
- innerRouter.push(CommonSendRoute.Confirm)
- }
- },
- ),
- )
+ private fun getAmountComponent(
+ factoryContext: AppComponentContext,
+ route: CommonSendRoute,
+ ): ComposableContentComponent {
+ val cryptoCurrencyStatus = model.cryptoCurrencyStatus
+ return if (cryptoCurrencyStatus != null) {
+ SendAmountComponent(
+ appComponentContext = factoryContext,
+ params = SendAmountComponentParams.AmountParams(
+ state = model.uiState.value.amountUM,
+ currentRoute = currentRoute.filterIsInstance(),
+ isBalanceHidingFlow = model.isBalanceHiddenFlow,
+ analyticsCategoryName = analyticCategoryName,
+ userWallet = model.userWallet,
+ appCurrency = model.appCurrency,
+ cryptoCurrencyStatus = cryptoCurrencyStatus,
+ callback = model,
+ predefinedValues = model.predefinedValues,
+ onBackClick = {
+ if (route.isEditMode) {
+ onChildBack()
+ } else {
+ analyticsEventHandler.send(
+ CommonSendAnalyticEvents.CloseButtonClicked(
+ categoryName = analyticCategoryName,
+ source = SendScreenSource.Amount,
+ isFromSummary = false,
+ isValid = model.uiState.value.amountUM.isPrimaryButtonEnabled,
+ ),
+ )
+ router.pop()
+ }
+ },
+ onNextClick = {
+ if (route.isEditMode) {
+ onChildBack()
+ } else {
+ innerRouter.push(CommonSendRoute.Confirm)
+ }
+ },
+ ),
+ )
+ } else {
+ model.showAlertError()
+ getStubComponent()
+ }
+ }
+ @Suppress("ComplexCondition")
private fun getFeeComponent(factoryContext: AppComponentContext): ComposableContentComponent {
val state = model.uiState.value
val sendAmount = (state.amountUM as? AmountState.Data)?.amountTextField?.cryptoAmount?.value
val destinationAddress = (state.destinationUM as? DestinationUM.Content)?.addressTextField?.value
- return if (sendAmount != null && destinationAddress != null) {
+ val cryptoCurrencyStatus = model.cryptoCurrencyStatus
+ val feeCryptoCurrencyStatus = model.feeCryptoCurrencyStatus
+
+ return if (sendAmount != null && destinationAddress != null &&
+ feeCryptoCurrencyStatus != null && cryptoCurrencyStatus != null
+ ) {
SendFeeComponent(
appComponentContext = factoryContext,
params = SendFeeComponentParams.FeeParams(
@@ -211,8 +228,8 @@ internal class DefaultSendComponent @AssistedInject constructor(
currentRoute = currentRoute.filterIsInstance(),
analyticsCategoryName = analyticCategoryName,
userWallet = model.userWallet,
- cryptoCurrencyStatus = model.cryptoCurrencyStatus,
- feeCryptoCurrencyStatus = model.feeCryptoCurrencyStatus,
+ cryptoCurrencyStatus = cryptoCurrencyStatus,
+ feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
appCurrency = model.appCurrency,
onLoadFee = model::loadFee,
sendAmount = sendAmount,
@@ -222,11 +239,14 @@ internal class DefaultSendComponent @AssistedInject constructor(
),
)
} else {
+ model.showAlertError()
getStubComponent()
}
}
- private fun getConfirmComponent(factoryContext: AppComponentContext): SendConfirmComponent {
+ private fun getConfirmComponent(factoryContext: AppComponentContext): ComposableContentComponent {
+ val cryptoCurrencyStatus = model.cryptoCurrencyStatus
+ val feeCryptoCurrencyStatus = model.feeCryptoCurrencyStatus
val predefinedAmount = params.amount
val predefinedTxId = params.transactionId
val predefinedAddress = params.destinationAddress
@@ -241,32 +261,45 @@ internal class DefaultSendComponent @AssistedInject constructor(
} else {
PredefinedValues.Empty
}
- return SendConfirmComponent(
- appComponentContext = factoryContext,
- params = SendConfirmComponent.Params(
- state = model.uiState.value,
- userWallet = model.userWallet,
- currentRoute = currentRoute,
- isBalanceHidingFlow = model.isBalanceHiddenFlow,
- analyticsCategoryName = analyticCategoryName,
- cryptoCurrencyStatus = model.cryptoCurrencyStatus,
- feeCryptoCurrencyStatus = model.feeCryptoCurrencyStatus,
- appCurrency = model.appCurrency,
- callback = model,
- predefinedValues = predefinedValues,
- onLoadFee = model::loadFee,
- ),
- )
+ return if (cryptoCurrencyStatus != null && feeCryptoCurrencyStatus != null) {
+ SendConfirmComponent(
+ appComponentContext = factoryContext,
+ params = SendConfirmComponent.Params(
+ state = model.uiState.value,
+ userWallet = model.userWallet,
+ currentRoute = currentRoute,
+ isBalanceHidingFlow = model.isBalanceHiddenFlow,
+ analyticsCategoryName = analyticCategoryName,
+ cryptoCurrencyStatus = cryptoCurrencyStatus,
+ feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
+ appCurrency = model.appCurrency,
+ callback = model,
+ predefinedValues = predefinedValues,
+ onLoadFee = model::loadFee,
+ ),
+ )
+ } else {
+ model.showAlertError()
+ getStubComponent()
+ }
}
- private fun getStubComponent() = ComposableContentComponent { }
+ private fun getStubComponent() = StubComponent()
+
+ class StubComponent : ComposableContentComponent {
+ @Composable
+ override fun Content(modifier: Modifier) {
+ }
+ }
private fun onChildBack() {
val isEmptyRoute = childStack.value.active.configuration == CommonSendRoute.Empty
val isEmptyStack = childStack.value.backStack.isEmpty()
val isSuccess = model.uiState.value.confirmUM is ConfirmUM.Success
+ val isStubComponent = childStack.value.active.instance is StubComponent
- if (isEmptyRoute || isEmptyStack || isSuccess) {
+ val isPopSend = isEmptyRoute || isEmptyStack || isSuccess || isStubComponent
+ if (isPopSend) {
router.pop()
} else {
stackNavigation.pop()
diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt
index 51a5163719..f130895489 100644
--- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt
+++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt
@@ -337,9 +337,11 @@ internal class SendConfirmModel @Inject constructor(
ifLeft = { error ->
Timber.e(error)
_uiState.update(SendConfirmSendingStateTransformer(isSending = false))
- alertFactory.getGenericErrorState {
- onFailedTxEmailClick(error.localizedMessage.orEmpty())
- }
+ alertFactory.getGenericErrorState(
+ onFailedTxEmailClick = {
+ onFailedTxEmailClick(error.localizedMessage.orEmpty())
+ },
+ )
},
ifRight = { txData ->
sendTransaction(txData)
diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt
index ea1ca3146c..d379946a64 100644
--- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt
+++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt
@@ -51,6 +51,7 @@ import com.tangem.utils.coroutines.JobHolder
import com.tangem.utils.coroutines.saveIn
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
+import timber.log.Timber
import javax.inject.Inject
import kotlin.properties.Delegates
@@ -94,8 +95,8 @@ internal class SendModel @Inject constructor(
val isBalanceHiddenFlow = _isBalanceHiddenFlow.asStateFlow()
var userWallet: UserWallet by Delegates.notNull()
- var cryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull()
- var feeCryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull()
+ var cryptoCurrencyStatus: CryptoCurrencyStatus? = null
+ var feeCryptoCurrencyStatus: CryptoCurrencyStatus? = null
var appCurrency: AppCurrency = AppCurrency.Default
var predefinedValues: PredefinedValues = PredefinedValues.Empty
@@ -154,6 +155,13 @@ internal class SendModel @Inject constructor(
}
}
+ fun showAlertError() {
+ sendConfirmAlertFactory.getGenericErrorState(
+ onFailedTxEmailClick = ::onFailedTxEmailClick,
+ popBack = router::pop,
+ )
+ }
+
private fun initAppCurrency() {
modelScope.launch {
appCurrency = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default }
@@ -174,7 +182,8 @@ internal class SendModel @Inject constructor(
)
},
ifLeft = {
- sendConfirmAlertFactory.getGenericErrorState(::onFailedTxEmailClick)
+ Timber.w(it.toString())
+ showAlertError()
return@launch
},
)
@@ -205,9 +214,12 @@ internal class SendModel @Inject constructor(
)
},
ifLeft = {
- sendConfirmAlertFactory.getGenericErrorState {
- onFailedTxEmailClick(it.toString())
- }
+ sendConfirmAlertFactory.getGenericErrorState(
+ onFailedTxEmailClick = {
+ onFailedTxEmailClick(it.toString())
+ },
+ popBack = router::pop,
+ )
},
)
}.launchIn(modelScope)
diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/NFTSendConfirmModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/NFTSendConfirmModel.kt
index 0e89448a8f..aafd0448ea 100644
--- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/NFTSendConfirmModel.kt
+++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/NFTSendConfirmModel.kt
@@ -273,9 +273,9 @@ internal class NFTSendConfirmModel @Inject constructor(
ifLeft = { error ->
Timber.e(error)
_uiState.update(NFTSendConfirmSendingStateTransformer(isSending = false))
- alertFactory.getGenericErrorState {
- onFailedTxEmailClick(error.localizedMessage.orEmpty())
- }
+ alertFactory.getGenericErrorState(
+ onFailedTxEmailClick = { onFailedTxEmailClick(error.localizedMessage.orEmpty()) },
+ )
},
ifRight = { txData ->
sendTransaction(txData)
diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt
index 8146fab729..4e496e2810 100644
--- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt
+++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt
@@ -201,9 +201,10 @@ internal class NFTSendModel @Inject constructor(
}
},
ifLeft = {
- alertFactory.getGenericErrorState {
- onFailedTxEmailClick(it.toString())
- }
+ alertFactory.getGenericErrorState(
+ onFailedTxEmailClick = { onFailedTxEmailClick(it.toString()) },
+ popBack = { router.pop() },
+ )
},
)
}.launchIn(modelScope)
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt
index 45ee5db07e..c4c8f072a3 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt
@@ -117,7 +117,7 @@ internal class SetInitialDataStateTransformer(
startText = TextReference.Res(R.string.staking_details_annual_percentage_rate),
endText = getAprRange(validators),
iconClick = { clickIntents.onInfoClick(InfoType.ANNUAL_PERCENTAGE_RATE) },
- isEndTextHighlighted = true,
+ isEndTextHighlighted = false,
)
}
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingClaimRewardsValidatorContent.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingClaimRewardsValidatorContent.kt
index 99b5c966bd..ca16207365 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingClaimRewardsValidatorContent.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingClaimRewardsValidatorContent.kt
@@ -16,8 +16,9 @@ import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.format.bigdecimal.percent
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.staking.impl.R
-import com.tangem.features.staking.impl.presentation.state.StakingStates
import com.tangem.features.staking.impl.presentation.model.StakingClickIntents
+import com.tangem.features.staking.impl.presentation.state.BalanceState
+import com.tangem.features.staking.impl.presentation.state.StakingStates
import com.tangem.utils.extensions.orZero
@Composable
@@ -37,16 +38,7 @@ internal fun StakingClaimRewardsValidatorContent(
key(item.title.resolveReference() + index) {
InputRowImageInfo(
subtitle = item.title,
- caption = combinedReference(
- resourceReference(R.string.staking_details_apr),
- annotatedReference {
- appendSpace()
- appendColored(
- text = item.validator?.apr.orZero().format { percent() },
- color = TangemTheme.colors.text.accent,
- )
- },
- ),
+ caption = item.getAprTextNeutral(),
infoTitle = item.formattedFiatAmount,
infoSubtitle = item.formattedCryptoAmount,
imageUrl = item.validator?.image.orEmpty(),
@@ -64,4 +56,26 @@ internal fun StakingClaimRewardsValidatorContent(
}
}
}
-}
\ No newline at end of file
+}
+
+/**
+ * For FCA fixes remove coloring for now
+ */
+@Suppress("UnusedPrivateMember")
+@Composable
+private fun BalanceState.getAprTextColored() = combinedReference(
+ resourceReference(R.string.staking_details_apr),
+ annotatedReference {
+ appendSpace()
+ appendColored(
+ text = validator?.apr.orZero().format { percent() },
+ color = TangemTheme.colors.text.accent,
+ )
+ },
+)
+
+@Composable
+private fun BalanceState.getAprTextNeutral() = combinedReference(
+ resourceReference(R.string.staking_details_apr),
+ stringReference(" " + validator?.apr.orZero().format { percent() }),
+)
\ No newline at end of file
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt
index 5e8683d6a9..e984f97d3d 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt
@@ -264,7 +264,7 @@ private fun ActiveStakingBlock(
val (icon, iconTint) = balance.type.getIcon()
InputRowImageInfo(
subtitle = balance.title,
- caption = balance.subtitle ?: balance.getAprText(),
+ caption = balance.subtitle ?: balance.getAprTextNeutral(),
infoTitle = balance.formattedFiatAmount.orMaskWithStars(isBalanceHidden),
infoSubtitle = balance.formattedCryptoAmount.orMaskWithStars(isBalanceHidden),
imageUrl = balance.getImage(),
@@ -301,8 +301,12 @@ private fun StakeButtonBlock(buttonState: NavigationButtonsState) {
}
}
+/**
+ * For FCA fixes remove coloring for now
+ */
+@Suppress("UnusedPrivateMember")
@Composable
-private fun BalanceState.getAprText() = combinedReference(
+private fun BalanceState.getAprTextColored() = combinedReference(
resourceReference(R.string.staking_details_apr),
annotatedReference {
appendSpace()
@@ -313,6 +317,12 @@ private fun BalanceState.getAprText() = combinedReference(
},
)
+@Composable
+private fun BalanceState.getAprTextNeutral() = combinedReference(
+ resourceReference(R.string.staking_details_apr),
+ stringReference(" " + validator?.apr.orZero().format { percent() }),
+)
+
@Composable
private fun BalanceType.getIcon() = when (this) {
BalanceType.UNSTAKING -> R.drawable.ic_connection_18 to TangemTheme.colors.icon.accent
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingValidatorListContent.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingValidatorListContent.kt
index a525deb6df..3c842275b6 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingValidatorListContent.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingValidatorListContent.kt
@@ -27,11 +27,12 @@ import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.format.bigdecimal.percent
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
+import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.features.staking.impl.R
+import com.tangem.features.staking.impl.presentation.model.StakingClickIntents
import com.tangem.features.staking.impl.presentation.state.StakingStates
import com.tangem.features.staking.impl.presentation.state.previewdata.ValidatorStatePreviewData
import com.tangem.features.staking.impl.presentation.state.stub.StakingClickIntentsStub
-import com.tangem.features.staking.impl.presentation.model.StakingClickIntents
import com.tangem.utils.extensions.orZero
/**
@@ -62,16 +63,7 @@ internal fun StakingValidatorListContent(
InputRowImageSelector(
subtitle = stringReference(item.name),
- caption = combinedReference(
- resourceReference(R.string.staking_details_annual_percentage_rate),
- annotatedReference {
- appendSpace()
- appendColored(
- text = item.apr.orZero().format { percent() },
- color = TangemTheme.colors.text.accent,
- )
- },
- ),
+ caption = item.getAprTextNeutral(),
imageUrl = item.image.orEmpty(),
isSelected = item == state.chosenValidator,
onSelect = { clickIntents.onValidatorSelect(item) },
@@ -108,6 +100,28 @@ internal fun StakingValidatorListContent(
}
}
+/**
+ * For FCA fixes remove coloring for now
+ */
+@Suppress("UnusedPrivateMember")
+@Composable
+private fun Yield.Validator.getAprTextColored() = combinedReference(
+ resourceReference(R.string.staking_details_annual_percentage_rate),
+ annotatedReference {
+ appendSpace()
+ appendColored(
+ text = apr.orZero().format { percent() },
+ color = TangemTheme.colors.text.accent,
+ )
+ },
+)
+
+@Composable
+private fun Yield.Validator.getAprTextNeutral() = combinedReference(
+ resourceReference(R.string.staking_details_annual_percentage_rate),
+ stringReference(" " + apr.orZero().format { percent() }),
+)
+
@Composable
private fun RowScope.ValidatorLabel(isStrategicPartner: Boolean) {
if (isStrategicPartner) {
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/block/ValidatorBlock.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/block/ValidatorBlock.kt
index 3d5cc0310a..6e2d13e5e0 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/block/ValidatorBlock.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/block/ValidatorBlock.kt
@@ -40,16 +40,30 @@ internal fun ValidatorBlock(validatorState: StakingStates.ValidatorState, isClic
InputRowImageInfo(
title = resourceReference(R.string.staking_validator),
subtitle = stringReference(state.chosenValidator.name),
- infoTitle = annotatedReference {
- append(resourceReference(R.string.staking_details_apr).resolveReference())
- appendSpace()
- appendColored(
- text = state.chosenValidator.apr.orZero().format { percent() },
- color = TangemTheme.colors.text.accent,
- )
- },
+ infoTitle = state.getInfoTitleNeutral(),
imageUrl = state.chosenValidator.image.orEmpty(),
onImageError = { ValidatorImagePlaceholder() },
)
}
-}
\ No newline at end of file
+}
+
+/**
+ * For FCA fixes remove coloring for now
+ */
+@Suppress("UnusedPrivateMember")
+@Composable
+private fun StakingStates.ValidatorState.Data.getInfoTitleColored() = combinedReference(
+ annotatedReference {
+ append(resourceReference(R.string.staking_details_apr).resolveReference())
+ appendSpace()
+ appendColored(
+ text = chosenValidator.apr.orZero().format { percent() },
+ color = TangemTheme.colors.text.accent,
+ )
+ },
+)
+
+private fun StakingStates.ValidatorState.Data.getInfoTitleNeutral() = combinedReference(
+ resourceReference(R.string.staking_details_apr),
+ stringReference(" " + chosenValidator.apr.orZero().format { percent() }),
+)
\ No newline at end of file
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 38779307ed..0b3a1eb4b5 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
@@ -31,6 +31,9 @@ import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.promo.ShouldShowStoriesUseCase
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.tokens.*
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
@@ -95,6 +98,7 @@ internal class SwapModel @Inject constructor(
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
private val shouldShowStoriesUseCase: ShouldShowStoriesUseCase,
private val getStoryContentUseCase: GetStoryContentUseCase,
+ private val getUserCountryUseCase: GetUserCountryUseCase,
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
swapInteractorFactory: SwapInteractor.Factory,
private val urlOpener: UrlOpener,
@@ -145,6 +149,7 @@ internal class SwapModel @Inject constructor(
private val lastAmount = mutableStateOf(INITIAL_AMOUNT)
private val lastReducedBalanceBy = mutableStateOf(BigDecimal.ZERO)
private var swapRouter: SwapRouter = SwapRouter(router = router)
+ private var userCountry: UserCountry? = null
private val isUserResolvableError: (SwapState) -> Boolean = {
it is SwapState.SwapError &&
@@ -163,10 +168,13 @@ internal class SwapModel @Inject constructor(
get() = swapRouter.currentScreen
init {
+ userCountry = getUserCountryUseCase.invokeSync().getOrNull()
+ ?: UserCountry.Other(Locale.getDefault().country)
modelScope.launch {
initStories()
swapRouter.openScreen(SwapNavScreen.PromoStories)
}
+
modelScope.launch(dispatchers.io) {
val fromStatus = getCryptoCurrencyStatusUseCase(userWalletId, initialCurrencyFrom.id).getOrNull()
val toStatus = initialCurrencyTo?.let { getCryptoCurrencyStatusUseCase(userWalletId, it.id).getOrNull() }
@@ -439,6 +447,7 @@ internal class SwapModel @Inject constructor(
isNeedBestRateBadge = dataState.lastLoadedSwapStates.consideredProvidersStates().size > 1,
selectedFeeType = dataState.selectedFee?.feeType ?: FeeType.NORMAL,
isReverseSwapPossible = isReverseSwapPossible(),
+ needApplyFCARestrictions = userCountry.needApplyFCARestrictions(),
)
if (uiState.notifications.any { it is SwapNotificationUM.Error.UnableToCoverFeeWarning }) {
analyticsEventHandler.send(
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 f177785284..4be56e4101 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
@@ -253,6 +253,7 @@ internal class StateBuilder(
isNeedBestRateBadge: Boolean,
selectedFeeType: FeeType,
isReverseSwapPossible: Boolean,
+ needApplyFCARestrictions: Boolean,
): SwapStateHolder {
if (uiStateHolder.sendCardData !is SwapCardState.SwapCardData) return uiStateHolder
if (uiStateHolder.receiveCardData !is SwapCardState.SwapCardData) return uiStateHolder
@@ -346,6 +347,7 @@ internal class StateBuilder(
ChangeCardsButtonState.DISABLED
},
providerState = swapProvider.convertToContentClickableProviderState(
+ needApplyFCARestrictions = needApplyFCARestrictions,
isBestRate = bestRatedProviderId == swapProvider.providerId,
fromTokenInfo = quoteModel.fromTokenInfo,
toTokenInfo = quoteModel.toTokenInfo,
@@ -1138,6 +1140,7 @@ internal class StateBuilder(
@Suppress("LongParameterList")
private fun SwapProvider.convertToContentClickableProviderState(
+ needApplyFCARestrictions: Boolean,
isBestRate: Boolean,
fromTokenInfo: TokenSwapInfo,
toTokenInfo: TokenSwapInfo,
@@ -1158,7 +1161,7 @@ internal class StateBuilder(
// val rateString = "1 $fromCurrencySymbol ≈ $rate $toCurrencySymbol"
val badge = if (isRecommended) {
ProviderState.AdditionalBadge.Recommended
- } else if (isNeedBestRateBadge && isBestRate) {
+ } else if (isNeedBestRateBadge && isBestRate && !needApplyFCARestrictions) {
ProviderState.AdditionalBadge.BestTrade
} else {
ProviderState.AdditionalBadge.Empty
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt
index 7ce2014bc1..375e60ec26 100644
--- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt
@@ -144,10 +144,7 @@ internal object TokenDetailsPreviewData {
val stakingLoadingBlock = StakingBlockUM.Loading(iconState)
val stakingAvailableBlock = StakingBlockUM.StakeAvailable(
- titleText = resourceReference(
- id = R.string.token_details_staking_block_title,
- formatArgs = wrappedList("3.27%"),
- ),
+ titleText = resourceReference(id = R.string.token_details_staking_block_title),
subtitleText = resourceReference(
id = R.string.staking_notification_earn_rewards_text_period_day,
formatArgs = wrappedList("Solana"),
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsClickIntents.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsClickIntents.kt
index eeaaa1174a..c24e2e7736 100644
--- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsClickIntents.kt
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsClickIntents.kt
@@ -2,6 +2,7 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.model
import com.tangem.common.ui.bottomsheet.receive.AddressModel
import com.tangem.core.ui.extensions.TextReference
+import com.tangem.domain.promo.models.PromoId
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenBalanceSegmentedButtonConfig
@@ -49,9 +50,9 @@ interface TokenDetailsClickIntents {
fun onGoToProviderClick(url: String)
- fun onSwapPromoDismiss()
+ fun onSwapPromoDismiss(promoId: PromoId)
- fun onSwapPromoClick()
+ fun onSwapPromoClick(promoId: PromoId)
fun onGenerateExtendedKey()
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 c0ada618f5..4d4b8bc5e7 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
@@ -35,7 +35,8 @@ import com.tangem.domain.card.NetworkHasDerivationUseCase
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.onramp.model.OnrampSource
-import com.tangem.domain.promo.ShouldShowSwapPromoTokenUseCase
+import com.tangem.domain.promo.ShouldShowPromoTokenUseCase
+import com.tangem.domain.promo.models.PromoId
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.staking.GetStakingAvailabilityUseCase
import com.tangem.domain.staking.GetStakingEntryInfoUseCase
@@ -101,7 +102,7 @@ internal class TokenDetailsModel @Inject constructor(
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val getCurrencyWarningsUseCase: GetCurrencyWarningsUseCase,
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
- private val shouldShowSwapPromoTokenUseCase: ShouldShowSwapPromoTokenUseCase,
+ private val shouldShowPromoTokenUseCase: ShouldShowPromoTokenUseCase,
private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase,
private val getExtendedPublicKeyForCurrencyUseCase: GetExtendedPublicKeyForCurrencyUseCase,
private val getStakingEntryInfoUseCase: GetStakingEntryInfoUseCase,
@@ -807,9 +808,9 @@ internal class TokenDetailsModel @Inject constructor(
router.openUrl(url)
}
- override fun onSwapPromoDismiss() {
+ override fun onSwapPromoDismiss(promoId: PromoId) {
modelScope.launch(dispatchers.main) {
- shouldShowSwapPromoTokenUseCase.neverToShow()
+ shouldShowPromoTokenUseCase.neverToShow(promoId)
analyticsEventsHandler.send(
TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
source = AnalyticsParam.ScreensSources.Token,
@@ -820,9 +821,9 @@ internal class TokenDetailsModel @Inject constructor(
}
}
- override fun onSwapPromoClick() {
+ override fun onSwapPromoClick(promoId: PromoId) {
modelScope.launch(dispatchers.main) {
- shouldShowSwapPromoTokenUseCase.neverToShow()
+ shouldShowPromoTokenUseCase.neverToShow(promoId)
analyticsEventsHandler.send(
TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
source = AnalyticsParam.ScreensSources.Token,
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsNotificationConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsNotificationConverter.kt
index 50b4710dd8..1413bff703 100644
--- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsNotificationConverter.kt
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsNotificationConverter.kt
@@ -10,10 +10,10 @@ import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
import com.tangem.domain.tokens.model.warnings.HederaWarnings
import com.tangem.domain.tokens.model.warnings.KaspaWarnings
+import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification.*
-import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.features.tokendetails.impl.R
import com.tangem.utils.converter.Converter
import com.tangem.utils.extensions.removeBy
@@ -98,8 +98,8 @@ internal class TokenDetailsNotificationConverter(
is CryptoCurrencyWarning.SwapPromo -> SwapPromo(
startDateTime = warning.startDateTime,
endDateTime = warning.endDateTime,
- onSwapClick = clickIntents::onSwapPromoClick,
- onCloseClick = clickIntents::onSwapPromoDismiss,
+ onSwapClick = { clickIntents.onSwapPromoClick(warning.promoId) },
+ onCloseClick = { clickIntents.onSwapPromoDismiss(warning.promoId) },
)
is CryptoCurrencyWarning.BeaconChainShutdown -> NetworkShutdown(
title = resourceReference(R.string.warning_beacon_chain_retirement_title),
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStakingInfoConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStakingInfoConverter.kt
index 0f402c7631..9b5b230751 100644
--- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStakingInfoConverter.kt
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStakingInfoConverter.kt
@@ -7,7 +7,6 @@ import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
-import com.tangem.core.ui.format.bigdecimal.percent
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.staking.model.StakingAvailability
import com.tangem.domain.staking.model.StakingEntryInfo
@@ -102,12 +101,8 @@ internal class TokenDetailsStakingInfoConverter(
iconState: IconState,
isEnabled: Boolean,
): StakingBlockUM.StakeAvailable {
- val apr = stakingEntryInfo.apr.format { percent() }
return StakingBlockUM.StakeAvailable(
- titleText = resourceReference(
- id = R.string.token_details_staking_block_title,
- formatArgs = wrappedList(apr),
- ),
+ titleText = resourceReference(id = R.string.token_details_staking_block_title),
subtitleText = resourceReference(
id = R.string.staking_notification_earn_rewards_text,
formatArgs = wrappedList(stakingEntryInfo.tokenSymbol),
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt
index 808dd9238b..4922cf66c1 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt
@@ -4,11 +4,15 @@ import androidx.compose.runtime.Stable
import arrow.core.getOrElse
import com.arkivanov.decompose.router.slot.activate
import com.arkivanov.decompose.router.slot.dismiss
+import com.tangem.common.routing.AppRoute
+import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
+import com.tangem.core.deeplink.DeepLinksRegistry
+import com.tangem.core.deeplink.global.ReferralDeepLink
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.settings.*
@@ -75,7 +79,9 @@ internal class WalletModel @Inject constructor(
private val tokenListStore: MultiWalletTokenListStore,
private val onrampStatusFactory: OnrampStatusFactory,
private val analyticsEventsHandler: AnalyticsEventHandler,
+ private val deepLinksRegistry: DeepLinksRegistry,
private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase,
+ private val appRouter: AppRouter,
val screenLifecycleProvider: ScreenLifecycleProvider,
val innerWalletRouter: InnerWalletRouter,
) : Model() {
@@ -207,7 +213,9 @@ internal class WalletModel @Inject constructor(
if (selectedWallet.isMultiCurrency) {
selectedWalletAnalyticsSender.send(selectedWallet)
}
-
+ // Registering here, because `WalletDeepLinksHandler` unregisters deeplink when scope is cancelled
+ // This is temporary solution, will be removed with complete deeplink navigation overhaul
+ addReferralDeepLink(selectedWallet)
walletDeepLinksHandler.registerForWallet(scope = modelScope, userWallet = selectedWallet)
subscribeOnExpressTransactionsUpdates(selectedWallet)
subscribeToScreenBackgroundState(selectedWallet)
@@ -217,6 +225,20 @@ internal class WalletModel @Inject constructor(
}
}
+ private fun addReferralDeepLink(userWallet: UserWallet) {
+ deepLinksRegistry.register(
+ ReferralDeepLink(
+ onReceive = {
+ if (userWallet.cardTypesResolver.isTangemWallet()) {
+ appRouter.push(
+ AppRoute.ReferralProgram(userWalletId = userWallet.walletId),
+ )
+ }
+ },
+ ),
+ )
+ }
+
// We need to update the current wallet quotes if the application was in the background for more than 10 seconds
// and then returned to the foreground
private fun subscribeToScreenBackgroundState(userWallet: UserWallet) {
@@ -322,9 +344,7 @@ internal class WalletModel @Inject constructor(
coroutineScope = modelScope,
)
- if (action.selectedWallet.scanResponse.cardTypesResolver.isSingleWallet()) {
- fetchCurrencyStatusUseCase(userWalletId = action.selectedWallet.walletId)
- }
+ fetchIfSingleWallet(action.selectedWallet)
if (action.wallets.size > 1 && isWalletsScrollPreviewEnabled()) {
withContext(dispatchers.io) { delay(timeMillis = 1_800) }
@@ -351,6 +371,8 @@ internal class WalletModel @Inject constructor(
coroutineScope = modelScope,
)
+ fetchIfSingleWallet(userWallet = action.selectedWallet)
+
stateHolder.update(
ReinitializeWalletTransformer(
prevWalletId = action.prevWalletId,
@@ -368,12 +390,7 @@ internal class WalletModel @Inject constructor(
coroutineScope = modelScope,
)
- if (action.selectedWallet.scanResponse.cardTypesResolver.isSingleWallet()) {
- modelScope.launch {
- fetchCurrencyStatusUseCase(userWalletId = action.selectedWallet.walletId)
- .onLeft { Timber.e(it.toString()) }
- }
- }
+ fetchIfSingleWallet(userWallet = action.selectedWallet)
stateHolder.update(
AddWalletTransformer(
@@ -472,6 +489,15 @@ internal class WalletModel @Inject constructor(
}
}
+ private fun fetchIfSingleWallet(userWallet: UserWallet) {
+ if (userWallet.scanResponse.cardTypesResolver.isSingleWallet()) {
+ modelScope.launch {
+ fetchCurrencyStatusUseCase(userWalletId = userWallet.walletId)
+ .onLeft { Timber.e(it.toString()) }
+ }
+ }
+ }
+
inner class AskBiometryModelCallbacks : AskBiometryComponent.ModelCallbacks {
override fun onAllowed() {
analyticsEventsHandler.send(MainScreenAnalyticsEvent.EnableBiometrics(AnalyticsParam.OnOffState.On))
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 cb41f9c60a..fbe13e7f90 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
@@ -2,6 +2,8 @@ package com.tangem.feature.wallet.child.wallet.model.intents
import arrow.core.getOrElse
import com.tangem.common.TangemBlogUrlBuilder
+import com.tangem.common.routing.AppRoute
+import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.decompose.di.ModelScoped
@@ -13,7 +15,8 @@ import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
-import com.tangem.domain.promo.ShouldShowSwapPromoWalletUseCase
+import com.tangem.domain.promo.ShouldShowPromoWalletUseCase
+import com.tangem.domain.promo.models.PromoId
import com.tangem.domain.settings.NeverToSuggestRateAppUseCase
import com.tangem.domain.settings.RemindToRateAppLaterUseCase
import com.tangem.domain.tokens.FetchTokenListUseCase
@@ -65,7 +68,9 @@ internal interface WalletWarningsClickIntents {
fun onCloseRateAppWarningClick()
- fun onCloseSwapPromoClick()
+ fun onClosePromoClick(promoId: PromoId)
+
+ fun onPromoClick(promoId: PromoId)
fun onSupportClick()
@@ -95,13 +100,14 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
private val unlockWalletsUseCase: UnlockWalletsUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
private val dispatchers: CoroutineDispatcherProvider,
- private val shouldShowSwapPromoWalletUseCase: ShouldShowSwapPromoWalletUseCase,
+ private val shouldShowPromoWalletUseCase: ShouldShowPromoWalletUseCase,
private val getCardInfoUseCase: GetCardInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase,
private val urlOpener: UrlOpener,
private val tokensFeatureToggles: TokensFeatureToggles,
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
+ private val appRouter: AppRouter,
) : BaseWalletClickIntents(), WalletWarningsClickIntents {
override fun onAddBackupCardClick() {
@@ -257,16 +263,28 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
}
}
- override fun onCloseSwapPromoClick() {
+ override fun onClosePromoClick(promoId: PromoId) {
analyticsEventHandler.send(
- TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
- source = AnalyticsParam.ScreensSources.Main,
- programName = TokenSwapPromoAnalyticsEvent.ProgramName.Empty, // Use it on new promo action
- action = TokenSwapPromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Closed,
- ),
+ if (promoId == PromoId.Referral) {
+ MainScreen.ReferralPromoButtonDismiss
+ } else {
+ TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
+ source = AnalyticsParam.ScreensSources.Main,
+ programName = TokenSwapPromoAnalyticsEvent.ProgramName.Empty, // Use it on new promo action
+ action = TokenSwapPromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Closed,
+ )
+ },
)
modelScope.launch(dispatchers.main) {
- shouldShowSwapPromoWalletUseCase.neverToShow()
+ 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))
}
}
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/deeplink/WalletDeepLinksHandler.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/deeplink/WalletDeepLinksHandler.kt
index 64de32d2e2..132d29572c 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/deeplink/WalletDeepLinksHandler.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/deeplink/WalletDeepLinksHandler.kt
@@ -36,6 +36,7 @@ internal class WalletDeepLinksHandler @Inject constructor(
deepLinksRegistry.unregisterByIds(deepLinks.map { it.id })
deepLinksRegistry.register(deepLinks = deepLinks)
+ // When navigation to another screen scope is Cancelled and deeplinks are hot handled
scope.launchOnCancellation {
deepLinksRegistry.unregister(deepLinks)
}
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt
index a07053ba04..523e8923de 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt
@@ -137,5 +137,11 @@ sealed class WalletScreenAnalyticsEvent {
data object NoticeSeedPhraseSupportButtonUsed : MainScreen(event = "Button - Support Used")
data object NoticeSeedPhraseSupportButtonDeclined : MainScreen(event = "Button - Support Declined")
+
+ // region Referral Promo
+ data object ReferralPromo : MainScreen(event = "Referral Banner")
+ data object ReferralPromoButtonParticipate : MainScreen(event = "Button - Referral Participate")
+ data object ReferralPromoButtonDismiss : MainScreen(event = "Button - Referral Dismiss")
+ //endregion
}
}
\ No newline at end of file
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 36ed315d4b..0db4af387d 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
@@ -53,6 +53,7 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
source = AnalyticsParam.ScreensSources.Main,
programName = ProgramName.Empty, // Use it on new promo action
)
+ is WalletNotification.ReferralPromo -> MainScreen.ReferralPromo
is WalletNotification.UnlockWallets -> null // See [SelectedWalletAnalyticsSender]
is WalletNotification.Informational.NoAccount,
is WalletNotification.Warning.LowSignatures,
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 5ca74067c5..f50c6ea080 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
@@ -6,6 +6,8 @@ import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.core.lce.Lce
import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.models.StatusSource
+import com.tangem.domain.promo.ShouldShowPromoWalletUseCase
+import com.tangem.domain.promo.models.PromoId
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.model.CryptoCurrency
@@ -34,6 +36,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
private val isNeedToBackupUseCase: IsNeedToBackupUseCase,
private val backupValidator: BackupValidator,
private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase,
+ private val shouldShowPromoWalletUseCase: ShouldShowPromoWalletUseCase,
) {
@Suppress("MagicNumber", "MaximumLineLength")
@@ -45,12 +48,15 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
flow2 = isReadyToShowRateAppUseCase(),
flow3 = isNeedToBackupUseCase(userWallet.walletId),
flow4 = seedPhraseNotificationUseCase(userWalletId = userWallet.walletId),
- ) { maybeTokenList, isReadyToShowRating, isNeedToBackup, seedPhraseIssueStatus ->
+ flow5 = shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.Referral),
+ ) { maybeTokenList, isReadyToShowRating, isNeedToBackup, seedPhraseIssueStatus, shouldShowReferralPromo ->
buildList {
addUsedOutdatedDataNotification(maybeTokenList)
addCriticalNotifications(userWallet, seedPhraseIssueStatus, clickIntents)
+ addReferralPromoNotification(cardTypesResolver, clickIntents, shouldShowReferralPromo)
+
addInformationalNotifications(cardTypesResolver, maybeTokenList, clickIntents)
addWarningNotifications(cardTypesResolver, maybeTokenList, isNeedToBackup, clickIntents)
@@ -187,6 +193,20 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
.map(CryptoCurrencyStatus::currency)
}
+ private fun MutableList.addReferralPromoNotification(
+ cardTypesResolver: CardTypesResolver,
+ clickIntents: WalletClickIntents,
+ shouldShowPromo: Boolean,
+ ) {
+ addIf(
+ element = WalletNotification.ReferralPromo(
+ onCloseClick = { clickIntents.onClosePromoClick(promoId = PromoId.Referral) },
+ onClick = { clickIntents.onPromoClick(promoId = PromoId.Referral) },
+ ),
+ condition = shouldShowPromo && cardTypesResolver.isTangemWallet(),
+ )
+ }
+
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 203a35a3a8..8315a590cd 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
@@ -254,4 +254,20 @@ sealed class WalletNotification(val config: NotificationConfig) {
iconResId = R.drawable.ic_error_sync_24,
),
)
+
+ data class ReferralPromo(
+ val onCloseClick: () -> Unit,
+ val onClick: () -> Unit,
+ ) : WalletNotification(
+ config = NotificationConfig(
+ title = resourceReference(R.string.notification_referral_promo_title),
+ subtitle = resourceReference(R.string.notification_referral_promo_text),
+ iconResId = R.drawable.img_referral_promo,
+ onCloseClick = onCloseClick,
+ buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
+ text = resourceReference(R.string.notification_referral_promo_button),
+ onClick = onClick,
+ ),
+ ),
+ )
}
\ No newline at end of file
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt
index 34389c69a8..e336654e43 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt
@@ -2,6 +2,8 @@ package com.tangem.feature.wallet.presentation.wallet.subscribers
import arrow.core.getOrElse
import com.tangem.core.deeplink.DeepLinksRegistry
+import com.tangem.core.deeplink.global.ReferralDeepLink
+import com.tangem.core.deeplink.global.SellCurrencyDeepLink
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.core.lce.Lce
@@ -113,8 +115,14 @@ internal abstract class BasicTokenListSubscriber(
protected open suspend fun onTokenListReceived(maybeTokenList: Lce) {
/* no-op */
+ // Handling sell deeplink requires full content in order to correctly open Send screen
if (maybeTokenList.getOrNull(false) != null) {
- deepLinksRegistry.triggerDelayedDeeplink()
+ deepLinksRegistry.triggerDelayedDeeplink(deepLinkClass = SellCurrencyDeepLink::class.java)
+ }
+ // Handling referral deeplink requires only selected wallet to be loaded
+ // This is temporary solution, will be removed with complete deeplink navigation overhaul
+ if (maybeTokenList.getOrNull(true) != null) {
+ deepLinksRegistry.triggerDelayedDeeplink(deepLinkClass = ReferralDeepLink::class.java)
}
}
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt
index 211ce57e2d..0227a5339a 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt
@@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.common
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.notifications.NoteMigrationNotification
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.res.TangemTheme
@@ -38,6 +39,11 @@ internal fun LazyListScope.notifications(configs: ImmutableList TangemTheme.colors.icon.warning
is WalletNotification.Informational -> TangemTheme.colors.icon.accent
diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcTransactionRequestButtons.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcTransactionRequestButtons.kt
index 27eec41927..8451c004cb 100644
--- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcTransactionRequestButtons.kt
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcTransactionRequestButtons.kt
@@ -30,7 +30,7 @@ internal fun WcTransactionRequestButtons(
modifier = Modifier
.fillMaxWidth()
.weight(1f),
- text = stringResourceSafe(R.string.wc_sign_message_button_text),
+ text = stringResourceSafe(R.string.common_sign),
onClick = onSign,
iconResId = R.drawable.ic_tangem_24,
showProgress = isLoading,
diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml
index 0d1f7d70f6..8a6f485b07 100644
--- a/gradle/tangem_dependencies.toml
+++ b/gradle/tangem_dependencies.toml
@@ -5,9 +5,9 @@
# https://github.com/tangem/tangem-sdk-android/
# https://github.com/tangem/vico
-tangemBlockchainSdk = "develop-1051"
+tangemBlockchainSdk = "develop-1052"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
-tangemCardSdk = "develop-455"
+tangemCardSdk = "develop-463"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
tangemVico = "2.0.0-alpha.25-tangem12"
#tangemVico = "0.0.1" # Keep it! - used for local builds ^