Updated on 2026-08-14
This commit is contained in:
commit
633db5a0cf
74 changed files with 822 additions and 315 deletions
|
|
@ -142,6 +142,17 @@
|
|||
android:scheme="tangem" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data
|
||||
android:host="*"
|
||||
android:scheme="tangem" />
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
|
||||
<!-- Disable android.startup completely. Used for Worker according doc -->
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ import org.rekotlin.StateType
|
|||
// todo refactor [REDACTED_TASK_KEY]
|
||||
data class HomeState(
|
||||
val scanInProgress: Boolean = false,
|
||||
val stories: ImmutableList<Stories> = Stories.entries.toImmutableList(),
|
||||
val stories: ImmutableList<Stories> = 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<Stories> {
|
||||
return Stories.entries.filterNot { it == Stories.Currencies }
|
||||
}
|
||||
|
|
@ -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")
|
||||
|
||||
|
|
|
|||
|
|
@ -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<String, String>) {
|
||||
onReceive()
|
||||
}
|
||||
}
|
||||
|
|
@ -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<out DeepLink>)
|
||||
|
||||
fun cancelDelayedDeeplink()
|
||||
}
|
||||
|
|
@ -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<DeepLink> = 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<out DeepLink>) {
|
||||
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<String, String>?) {
|
||||
|
|
|
|||
|
|
@ -310,6 +310,7 @@
|
|||
<string name="express_exchange_status_receiving">Einzahlung wird erwartet</string>
|
||||
<string name="express_exchange_status_receiving_active">Warten auf Einzahlung...</string>
|
||||
<string name="express_exchange_status_refunded">Rückerstattet</string>
|
||||
<string name="express_exchange_status_refunding">Rückerstattung</string>
|
||||
<string name="express_exchange_status_sending">An dich gesendet</string>
|
||||
<string name="express_exchange_status_sending_active">wird an dich versendet...</string>
|
||||
<string name="express_exchange_status_sent">Gesendet</string>
|
||||
|
|
@ -514,6 +515,8 @@
|
|||
<string name="markets_tooltip_message">Rufe dies auf oder tippe auf die Suchleiste, um Token direkt vom Markt hinzuzufügen</string>
|
||||
<string name="markets_tooltip_title">Token hinzufügen</string>
|
||||
<string name="nfc_error_unavailable">NFC ist auf deinem Gerät nicht verfügbar</string>
|
||||
<string name="nft_about_title">Über NFT</string>
|
||||
<string name="nft_asset">NFT-Vermögenswert</string>
|
||||
<plurals name="nft_collections_count">
|
||||
<item quantity="one">%d Stück</item>
|
||||
<item quantity="other">%d Stücke</item>
|
||||
|
|
@ -535,8 +538,14 @@
|
|||
<string name="nft_details_token_standard">Token-Standard</string>
|
||||
<string name="nft_details_traits">Eigenschaften</string>
|
||||
<string name="nft_empty_search">Keine Ergebnisse. Bitte versuche eine andere Anfrage.</string>
|
||||
<string name="nft_no_collection">Keine Abholung</string>
|
||||
<string name="nft_receive_available_section_title">Verfügbar</string>
|
||||
<string name="nft_receive_choose_network">Netzwerk auswählen</string>
|
||||
<string name="nft_receive_title">NFT erhalten</string>
|
||||
<string name="nft_receive_unavailable_asset_warning_message">Du hast dieses Netzwerk noch nicht hinzugefügt. Um NFTs zu empfangen, füge es zum Hauptbildschirm hinzu!</string>
|
||||
<string name="nft_receive_unavailable_asset_warning_title">Netzwerk nicht hinzugefügt</string>
|
||||
<string name="nft_receive_unavailable_section_title">Nicht hinzugefügt</string>
|
||||
<string name="nft_send">NFT senden</string>
|
||||
<string name="nft_traits_title">Eigenschaften</string>
|
||||
<string name="nft_wallet_count">%1$d NFTs in der %2$d Sammlung</string>
|
||||
<string name="nft_wallet_receive_nft">Tippe hier, um das erste NFT zu erhalten</string>
|
||||
|
|
@ -546,6 +555,9 @@
|
|||
<string name="no_account_polkadot">Das Zielkonto ist nicht aktiv. Sende %s oder mehr, um das Konto zu aktivieren.</string>
|
||||
<string name="no_account_send_to_create">Senden Sie Geld an diese Andresse um ein Konto zu erstellen</string>
|
||||
<string name="no_trustline_xlm_asset">Das Zielkonto verfügt nicht über eine Vertrauensstellung für das gesendete Asset.</string>
|
||||
<string name="notification_referral_promo_button">Jetzt beitreten</string>
|
||||
<string name="notification_referral_promo_text">Teile Deinen Code – verdiene 5 USDT pro Verkauf. Deine Freunde erhalten 10 % Rabatt.</string>
|
||||
<string name="notification_referral_promo_title">Erhalte BELOHNUNGEN für jeden Freund!</string>
|
||||
<string name="onboarding_access_code_feature_1_description">Du musst einen einzigen Zugangscode einrichten, um alle deine Geräte zu schützen</string>
|
||||
<string name="onboarding_access_code_feature_1_title">Schützen</string>
|
||||
<string name="onboarding_access_code_feature_2_description">Du kannst später auf jeder Karte oder Ring einen individuellen Zugangscode einrichten</string>
|
||||
|
|
@ -894,6 +906,11 @@
|
|||
<string name="staking_notification_restake_text">Mit Restake kannst Du Dein Guthaben von einem Validator zu einem anderen verschieben, ohne dass Du den Stake aufheben musst.</string>
|
||||
<string name="staking_notification_stake_entire_balance_text">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.</string>
|
||||
<string name="staking_notification_ton_activate_account">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.</string>
|
||||
<string name="staking_notification_ton_extra_reserve_info">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.</string>
|
||||
<string name="staking_notification_ton_extra_reserve_is_required">Für diesen Vorgang sind zusätzlich zur Netzwerkgebühr 0,2 TON erforderlich. Bitte lade Dein Guthaben auf.</string>
|
||||
<string name="staking_notification_ton_extra_reserve_title">0,2 Ton reserviert</string>
|
||||
<string name="staking_notification_ton_have_to_unstake_all_text">Durch diese Aktion werden andere Positionen geschlossen oder gemäß den Netzwerkregeln in den Auszahlungsstatus versetzt.</string>
|
||||
<string name="staking_notification_ton_have_to_unstake_all_title">Positionsstatus</string>
|
||||
<string name="staking_notification_unlock_text">Entsperre dein Geld, um es aus dem Staking-Prozess abzuheben. Das Freischalten nimmt %s.</string>
|
||||
<string name="staking_notification_unstake_cosmos_text">Nach Ablauf der 21-tägigen Bindungsfrist kannst Du über Dein Guthaben verfügen. Die Prämie wird zusammen mit dem ungestaketen Guthaben abgehoben.</string>
|
||||
<string name="staking_notification_unstake_text">Deine Assets stehen Dir nach Ablauf der Frist für die Aufhebung der Bindung %s zur Verfügung.</string>
|
||||
|
|
@ -955,7 +972,7 @@
|
|||
<string name="story_finish_description">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.</string>
|
||||
<string name="story_finish_title">Die Wallet für jeden</string>
|
||||
<string name="story_meet_title">Lerne Tangem kennen</string>
|
||||
<string name="story_web3_description">Tausche, kaufen Sie NFTs, vergebe Kredite und tätige Einlagen bei mehr als 100 verschiedenen dezentralen Diensten</string>
|
||||
<string name="story_web3_description">Mehr als 100 Integrationen dezentraler Dienste sind verfügbar</string>
|
||||
<string name="story_web3_title">Web 3.0-kompatibel</string>
|
||||
<string name="sui_not_enough_coin_for_fee_description">Zum Fortfahren ist eine eingehende Transaktion von mindestens %1$s erforderlich</string>
|
||||
<string name="sui_not_enough_coin_for_fee_title">Unzureichende Mittel</string>
|
||||
|
|
@ -1010,7 +1027,7 @@
|
|||
<string name="token_details_hide_alert_title">Blende %s aus</string>
|
||||
<string name="token_details_hide_token">Token ausblenden</string>
|
||||
<string name="token_details_staking_block_subtitle">Durch das Staking kannst du alle %2$s Tage %1$s verdienen und Belohnungen erhalten</string>
|
||||
<string name="token_details_staking_block_title">Verdiene bis zu %s Stakingprämie pro Jahr</string>
|
||||
<string name="token_details_staking_block_title">Staking-Dienst</string>
|
||||
<string name="token_details_token_type_subtitle">%1$s Token in %%image%% %2$s Netzwerk</string>
|
||||
<string name="token_details_token_type_subtitle_no_standard">Token in %%image%% %1$s Netzwerk</string>
|
||||
<string name="token_details_unable_hide_alert_message">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.</string>
|
||||
|
|
@ -1215,13 +1232,36 @@
|
|||
<string name="warning_testnet_card_message">Dies ist eine Testnet-Karte. Sie kann keine Transaktionen verarbeiten und sollte nur zu Test- und Entwicklungszwecken verwendet werden.</string>
|
||||
<string name="warning_testnet_card_title">Nur für Testzwecke</string>
|
||||
<string name="warning_token_balance_not_updated">Der Kontostand ist möglicherweise veraltet. Aktualisiere bittre die Seite.</string>
|
||||
<string name="wc_alert_audit_unknown_domain">Unbekannte Domäne</string>
|
||||
<string name="wc_alert_connect_anyway">Trotzdem verbinden</string>
|
||||
<string name="wc_alert_connection_timeout_description">Zeitüberschreitungsfehler. Bitte versuche es später erneut.</string>
|
||||
<string name="wc_alert_connection_timeout_title">WalletConnect konnte nicht hergestellt werden</string>
|
||||
<string name="wc_alert_domain_issues_description">Diese Domäne kann nicht verifiziert werden. Überprüfe die Anfrage sorgfältig und bestätige diese dann.</string>
|
||||
<string name="wc_alert_session_disconnected_title">WalletConnect-Sitzung wurde getrennt</string>
|
||||
<string name="wc_alert_unknown_error_description">Fehlercode: %s. Wenn das Problem weiterhin besteht, wende Dich bitte an unseren Support.</string>
|
||||
<string name="wc_alert_unknown_error_title">Wir haben einen unbekannten Fehler festgestellt.</string>
|
||||
<string name="wc_alert_unsupported_networks_description">Tangem unterstützt derzeit das erforderliches Netzwerk von %s nicht.</string>
|
||||
<string name="wc_alert_unsupported_networks_title">Nicht unterstützte Netzwerke</string>
|
||||
<string name="wc_alert_verified_domain_description">Tangem unterstützt ein erforderliches Netzwerk um %s.</string>
|
||||
<string name="wc_alert_verified_domain_title">Verifizierte Domain</string>
|
||||
<string name="wc_alert_wrong_card_title">Wir haben eine Art Problem</string>
|
||||
<string name="wc_common_network">Netzwerk</string>
|
||||
<string name="wc_common_networks">Netzwerke</string>
|
||||
<string name="wc_common_wallet">Wallet</string>
|
||||
<string name="wc_connection_reqeust_cant_sign">Signiere die Transaktionen ohne eine Vorankündigung</string>
|
||||
<string name="wc_connection_reqeust_request_approval">Genehmigung für Transaktionen anfordern</string>
|
||||
<string name="wc_connection_reqeust_will_not">Wird nicht in der Lage sein,</string>
|
||||
<string name="wc_connections">Verbindungen</string>
|
||||
<string name="wc_disconnect_all">Alle trennen</string>
|
||||
<string name="wc_disconnect_all_alert_desc">Text über die Trennung aller dApps</string>
|
||||
<string name="wc_disconnect_all_alert_title">Alle dApps trennen</string>
|
||||
<string name="wc_missing_required_network_description">Füge Deinem Profil für dieses Wallet das Netzwerk %s hinzu</string>
|
||||
<string name="wc_missing_required_network_title">Die Wallet verfügt über keine erforderlichen Netzwerke</string>
|
||||
<string name="wc_new_connection">Neue Verbindung</string>
|
||||
<string name="wc_no_sessions_desc">Verbinde Deine Wallet mit einer anderen dApp</string>
|
||||
<string name="wc_no_sessions_title">Keine Sitzungen</string>
|
||||
<string name="wc_notification_security_risk_subtitle">Diese Domain wird von mehreren Sicherheitsanbietern als unsicher eingestuft. Verlasse diese umgehend, um Dein Vermögen zu schützen.</string>
|
||||
<string name="wc_notification_security_risk_title">Bekanntes Sicherheitsrisiko</string>
|
||||
<string name="welcome_interrupted_backup_alert_discard">Verwerfen</string>
|
||||
<string name="welcome_interrupted_backup_alert_message">Du hast eine unterbrochene Sicherung. Möchtest du diese fortsetzen?</string>
|
||||
<string name="welcome_interrupted_backup_alert_resume">Ja, fortsetzen</string>
|
||||
|
|
|
|||
|
|
@ -514,6 +514,9 @@
|
|||
<string name="no_account_polkadot">La cuenta de destino no está activa. Envíe %s o más para activar la cuenta.</string>
|
||||
<string name="no_account_send_to_create">Para crear una cuenta, envíe fondos a esta dirección</string>
|
||||
<string name="no_trustline_xlm_asset">La cuenta de destino no tiene una Trustline para el activo que se envía.</string>
|
||||
<string name="notification_referral_promo_button">Únete ahora</string>
|
||||
<string name="notification_referral_promo_text">Comparte tu código y gana 5 USDT por venta. Tu amigo obtiene un 10% de descuento.</string>
|
||||
<string name="notification_referral_promo_title">¡Obtén RECOMPENSAS por cada amigo!</string>
|
||||
<string name="onboarding_access_code_feature_1_description">Deba configurar un único código de acceso para proteger todss sus dispositivos.</string>
|
||||
<string name="onboarding_access_code_feature_1_title">Proteger</string>
|
||||
<string name="onboarding_access_code_feature_2_description">Puede configurar un código de acceso individual en cada tarjeta más adelante</string>
|
||||
|
|
@ -922,7 +925,7 @@
|
|||
<string name="story_finish_description">Úselo en cualquier lugar, en cualquier momento. Sin cables ni baterías. Solo toque la tarjeta con su teléfono cuando necesites su cripto.</string>
|
||||
<string name="story_finish_title">La billetera para todos</string>
|
||||
<string name="story_meet_title">Descubra Tangem</string>
|
||||
<string name="story_web3_description">Intercambie, compre NFT, haga préstamos y depósitos en más de 100 servicios descentralizados diferentes</string>
|
||||
<string name="story_web3_description">Más de 100 integraciones de servicios descentralizados están disponibles</string>
|
||||
<string name="story_web3_title">Compatible con Web 3.0</string>
|
||||
<string name="sui_not_enough_coin_for_fee_description">Se requiere una transacción entrante de al menos %1$s para proceder</string>
|
||||
<string name="sui_not_enough_coin_for_fee_title">Fondos insuficientes</string>
|
||||
|
|
@ -977,7 +980,7 @@
|
|||
<string name="token_details_hide_alert_title">Ocultar %s</string>
|
||||
<string name="token_details_hide_token">Ocultar el token</string>
|
||||
<string name="token_details_staking_block_subtitle">El staking le permite ganar %1$s y obtener recompensas cada %2$s días</string>
|
||||
<string name="token_details_staking_block_title">Gane hasta %s recompensa del staking por año</string>
|
||||
<string name="token_details_staking_block_title">Servicio de Staking</string>
|
||||
<string name="token_details_token_type_subtitle">Token de %1$s en la red %%image%% %2$s</string>
|
||||
<string name="token_details_token_type_subtitle_no_standard">Token en la %%image%% red %1$s</string>
|
||||
<string name="token_details_unable_hide_alert_message">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</string>
|
||||
|
|
|
|||
|
|
@ -524,6 +524,9 @@
|
|||
<string name="no_account_polkadot">Le compte de destination est inactif. Envoyez %s ou plus pour activer le compte.</string>
|
||||
<string name="no_account_send_to_create">Pour créer un compte, envoyez des fonds à cette adresse</string>
|
||||
<string name="no_trustline_xlm_asset">Le compte destinataire n\'a pas de Trustline pour l\'actif envoyé qu\'il tente d\'envoyer.</string>
|
||||
<string name="notification_referral_promo_button">Rejoignez maintenant</string>
|
||||
<string name="notification_referral_promo_text">Partagez votre code et gagnez 5 USDT par vente. Votre ami bénéficie de 10 % de réduction.</string>
|
||||
<string name="notification_referral_promo_title">Recevez des RÉCOMPENSES pour chaque ami !</string>
|
||||
<string name="onboarding_access_code_feature_1_description">Vous devez définir un seul code d\'accès pour protéger tous vos appareils.</string>
|
||||
<string name="onboarding_access_code_feature_1_title">Protéger</string>
|
||||
<string name="onboarding_access_code_feature_2_description">Vous pourrez définir un code d\'accès individuel sur chaque carte plus tard</string>
|
||||
|
|
@ -932,7 +935,7 @@
|
|||
<string name="story_finish_description">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.</string>
|
||||
<string name="story_finish_title">Le portefeuille pour tous</string>
|
||||
<string name="story_meet_title">Découvrez Tangem</string>
|
||||
<string name="story_web3_description">Échangez, achetez des NFT, faites des prêts et des dépôts dans plus de 100 services décentralisés différents</string>
|
||||
<string name="story_web3_description">Plus de 100 intégrations de services décentralisés sont disponibles</string>
|
||||
<string name="story_web3_title">Compatible avec Web 3.0</string>
|
||||
<string name="sui_not_enough_coin_for_fee_description">Une transaction entrante d\'au moins de %1$s est requise pour continuer</string>
|
||||
<string name="sui_not_enough_coin_for_fee_title">Fonds insuffisants</string>
|
||||
|
|
@ -986,7 +989,7 @@
|
|||
<string name="token_details_hide_alert_title">Masquer %s</string>
|
||||
<string name="token_details_hide_token">Masquer le jeton</string>
|
||||
<string name="token_details_staking_block_subtitle">Le Staking vous permet d\'en gagner %1$s et d\'obtenir des récompenses tous les %2$s jours</string>
|
||||
<string name="token_details_staking_block_title">Gagnez jusqu\'à %s récompense de mise par an</string>
|
||||
<string name="token_details_staking_block_title">Service de Staking</string>
|
||||
<string name="token_details_token_type_subtitle">%1$s jeton dans %%image%% %2$s le réseau</string>
|
||||
<string name="token_details_token_type_subtitle_no_standard">Jeton dans le %%image%% %1$s réseau</string>
|
||||
<string name="token_details_unable_hide_alert_message">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</string>
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@
|
|||
<string name="exchange_tokens_unavailable_tokens_header">%sとの交換はできません</string>
|
||||
<string name="express_by_provider">提供元</string>
|
||||
<string name="express_cex_status_button_title">ステータス</string>
|
||||
<string name="express_choose_providers_subtitle">Tangemは、各プロバイダーの条件に従って、サードパーティプロバイダーを介してトークンスワップを提供します。</string>
|
||||
<string name="express_choose_providers_subtitle">プロバイダーが取引を促進します</string>
|
||||
<string name="express_choose_providers_title">プロバイダー</string>
|
||||
<string name="express_error_code">エラーが発生しました。コード: %s</string>
|
||||
<string name="express_error_provider_amount_roundup">エラー%1$s 。選択したプロバイダーは指定された取引を処理できません。値を%2$sに切り上げるか、変更してください。</string>
|
||||
|
|
@ -316,11 +316,11 @@
|
|||
<string name="express_exchange_status_verifying">確認が必要です</string>
|
||||
<string name="express_exchange_status_waiting_tx_hash">取引ハッシュを待機中</string>
|
||||
<string name="express_exchange_token_list_subtitle">ウォレットに追加されたすべてのトークンのリスト</string>
|
||||
<string name="express_fetch_best_rates">最良のレートを取得しています...</string>
|
||||
<string name="express_fetch_best_rates">現在のレートを取得中...</string>
|
||||
<string name="express_floating_rate">変動レート</string>
|
||||
<string name="express_legal_one_placeholder">スワップ機能を使用すると、プロバイダーの%sに同意したことになります。</string>
|
||||
<string name="express_legal_two_placeholders">スワップ機能を使用すると、プロバイダーの%1$sおよび%2$sに同意したことになります。</string>
|
||||
<string name="express_more_providers_soon">さらに多くのプロバイダーを追加予定です。 \nお楽しみに。</string>
|
||||
<string name="express_more_providers_soon">さらに多くのプロバイダーが利用可能になる予定です</string>
|
||||
<string name="express_provider">プロバイダー</string>
|
||||
<string name="express_provider_best_rate">ベストレート</string>
|
||||
<string name="express_provider_max_amount">最大 %s まで使用可能</string>
|
||||
|
|
@ -415,6 +415,7 @@
|
|||
<string name="markets_add_to_my_portfolio_unavailable_description">このアセットは現在ウォレットで利用できません</string>
|
||||
<string name="markets_add_to_my_portfolio_unavailable_for_wallet_description">このアセットはこのウォレットでは使用できません。</string>
|
||||
<string name="markets_add_token">追加</string>
|
||||
<string name="markets_apy_placeholder">APY %s</string>
|
||||
<string name="markets_available_networks">利用可能なネットワーク</string>
|
||||
<string name="markets_common_my_portfolio">私のポートフォリオ</string>
|
||||
<string name="markets_common_title">マーケット</string>
|
||||
|
|
@ -439,11 +440,14 @@
|
|||
<string name="markets_selector_interval_7d_title">7d</string>
|
||||
<string name="markets_selector_interval_all_title">全部</string>
|
||||
<string name="markets_sort_by_experienced_buyers_title">経験豊富な買い手</string>
|
||||
<string name="markets_sort_by_rating_title">格付け</string>
|
||||
<string name="markets_sort_by_rating_title">時価総額</string>
|
||||
<string name="markets_sort_by_title">並べ替え</string>
|
||||
<string name="markets_sort_by_top_gainers_title">上昇率上位</string>
|
||||
<string name="markets_sort_by_top_losers_title">下落率上位</string>
|
||||
<string name="markets_sort_by_trending_title">トレンド</string>
|
||||
<string name="markets_staking_banner_description_placeholder">ステーキングは暗号資産で報酬を受け取る最も簡単な方法です。 %s</string>
|
||||
<string name="markets_staking_banner_description_show_more">もっと見る</string>
|
||||
<string name="markets_staking_banner_title">最大%s APYを獲得</string>
|
||||
<string name="markets_token_details_about_token_title">%sについて</string>
|
||||
<plurals name="markets_token_details_amount_exchanges">
|
||||
<item quantity="other">%d取引所</item>
|
||||
|
|
@ -548,6 +552,7 @@
|
|||
<string name="no_account_polkadot">送信先アカウントが有効ではありません。%s 以上を送信してアカウントを有効にしてください。</string>
|
||||
<string name="no_account_send_to_create">アカウントを作成するには、このアドレスに資金を送金してください</string>
|
||||
<string name="no_trustline_xlm_asset">送信先アカウントには、送金されるアセットのトラストラインがありません。</string>
|
||||
<string name="notification_referral_promo_button">今すぐ参加</string>
|
||||
<string name="notification_referral_promo_text">コードを共有すると、販売ごとに5 USDTを獲得できます。お友達は10%割引になります。</string>
|
||||
<string name="notification_referral_promo_title">友達への紹介で報酬を獲得しよう!</string>
|
||||
<string name="onboarding_access_code_feature_1_description">すべてのデバイスを保護するには、単一のアクセスコードを設定してください。</string>
|
||||
|
|
@ -646,7 +651,7 @@
|
|||
<string name="onboarding_wallet_info_title_second">同一のカード</string>
|
||||
<string name="onboarding_wallet_info_title_third">アクセスコード</string>
|
||||
<string name="onramp_avaiable_with_payment_methods">%sで利用可能</string>
|
||||
<string name="onramp_choose_provider_title_hint">Tangemでの暗号通貨の買付は、サードパーティプロバイダーの条件に基づいて行われます。</string>
|
||||
<string name="onramp_choose_provider_title_hint">プロバイダーが取引を促進します</string>
|
||||
<string name="onramp_country_search">国で検索</string>
|
||||
<string name="onramp_country_unavailable">利用不可</string>
|
||||
<string name="onramp_currency_other">その他の通貨</string>
|
||||
|
|
@ -835,16 +840,16 @@
|
|||
<string name="staking_amount_tron_integer_error_unstaking">ネットワークルールにより、ステーキング解除の量は%1$s TRX に切り上げられます。</string>
|
||||
<string name="staking_claim_unstaked">ステーキング解除分を請求する</string>
|
||||
<string name="staking_details_account_fee">ステーキングアカウント手数料</string>
|
||||
<string name="staking_details_account_fee_info">ステーキングアカウントは、ステーキングされたSOLトークンが保管される特別なアカウントです。取引の検証に参加して報酬を得るために、トークンをバリデーターに委任すると、このアカウントが作成されます。ステーキングアカウントの作成には少額の手数料がかかりますが、この手数料はステーキングが完了すると返金されます。</string>
|
||||
<string name="staking_details_account_fee_info">ステーキングアカウントとは、ステーキングされたSOLが保管される特別なアカウントです。トークンをバリデーターに委任し、取引の検証に参加して報酬を受け取る際に作成されます。ステーキングアカウントの作成には少額の手数料がかかりますが、ステーキング完了後に返金されます。</string>
|
||||
<string name="staking_details_annual_percentage_rate">年率</string>
|
||||
<string name="staking_details_annual_percentage_rate_info">ステーキングに参加することで得られる年間収益率。</string>
|
||||
<string name="staking_details_apr">APR</string>
|
||||
<string name="staking_details_auto_claiming_rewards_daily_text">報酬は毎日自動的にステーキング残高に蓄積されます。</string>
|
||||
<string name="staking_details_available">利用可能</string>
|
||||
<string name="staking_details_average_reward_rate">平均報酬率</string>
|
||||
<string name="staking_details_banner_text">ステーキングとは?</string>
|
||||
<string name="staking_details_banner_text">ステーキングの仕組み</string>
|
||||
<string name="staking_details_estimated_profit">%s 推定利益</string>
|
||||
<string name="staking_details_market_rating">市場評価</string>
|
||||
<string name="staking_details_market_rating">市場格付け</string>
|
||||
<string name="staking_details_metrics_block_header">指標</string>
|
||||
<string name="staking_details_min_rewards_notification">%1$sネットワークルールによれば、 %2$sからの請求が可能です。以下の金額は、ステーキング解除時にアカウントに入金されます。</string>
|
||||
<string name="staking_details_minimum_requirement">最低要件</string>
|
||||
|
|
@ -870,15 +875,15 @@
|
|||
<string name="staking_notification_additional_ada_deposit_text">Cardanoネットワークでステーキングする場合、残高全体が使用されます。追加の2ADAは確保され、ステーキング解除後に返却されます。ステーキング中、ADAはロック解除されたままです。</string>
|
||||
<string name="staking_notification_additional_ada_deposit_title">ADAステーキングの詳細</string>
|
||||
<string name="staking_notification_claim_rewards_text">獲得した報酬はあなたのアドレスに直接送られ、すぐ使用可能です。</string>
|
||||
<string name="staking_notification_earn_rewards_text">安全にステーキングして報酬を獲得しましょう</string>
|
||||
<string name="staking_notification_earn_rewards_text_daily">安全にステーキングして、報酬を毎日獲得しましょう</string>
|
||||
<string name="staking_notification_earn_rewards_text_hourly">安全にステーキングして、報酬を毎時間獲得しましょう</string>
|
||||
<string name="staking_notification_earn_rewards_text_monthly">安全にステーキングして、報酬を毎月獲得しましょう</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_day">ステーキングにより%1$sを獲得できます。ステーキング報酬は毎日受け取れます。</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_hour">ステーキングにより%1$sを獲得できます。ステーキング報酬は毎時間受け取れます。</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_month">ステーキングにより%1$sを獲得できます。ステーキング報酬は毎月受け取れます。</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_week">ステーキングにより%1$sを獲得できます。ステーキング報酬は毎週受け取れます。</string>
|
||||
<string name="staking_notification_earn_rewards_text_weekly">安全にステーキングして、報酬を毎週獲得しましょう</string>
|
||||
<string name="staking_notification_earn_rewards_text">Tangemでは暗号資産をステーキングできます</string>
|
||||
<string name="staking_notification_earn_rewards_text_daily">Tangemでは暗号資産をステーキングできます</string>
|
||||
<string name="staking_notification_earn_rewards_text_hourly">Tangemでは暗号資産をステーキングできます</string>
|
||||
<string name="staking_notification_earn_rewards_text_monthly">Tangemでは暗号資産をステーキングできます</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_day">ステーキングを行うと%1$sを受け取ることができます。ステーキング報酬は毎日受け取れます。</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_hour">ステーキングを行うと%1$sを受け取ることができます。ステーキング報酬は1時間ごとに受け取れます。</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_month">ステーキングを行うと%1$sを受け取ることができます。ステーキング報酬は毎月受け取れます。</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_week">ステーキングを行うと%1$sを受け取ることができます。ステーキング報酬は毎週受け取れます。</string>
|
||||
<string name="staking_notification_earn_rewards_text_weekly">Tangemでは暗号資産をステーキングできます</string>
|
||||
<string name="staking_notification_earn_rewards_title">ステーキング報酬を獲得</string>
|
||||
<string name="staking_notification_low_staked_balance_text">残りのステーキング残高が少なすぎてステーキングを解除できません。最小のステーキング解除残高を満たすには、さらにステーキングする必要があります。</string>
|
||||
<string name="staking_notification_low_staked_balance_title">ステーキング残高が低いです</string>
|
||||
|
|
@ -960,7 +965,7 @@
|
|||
<string name="story_finish_description">外出先でも、いつでもどこでも使用できます。コードや電池は不要です。暗号資産が必要なときに、カードまたはリングをスマートフォンにタップするだけです。</string>
|
||||
<string name="story_finish_title">すべての人のためのウォレット</string>
|
||||
<string name="story_meet_title">Tangemのご紹介</string>
|
||||
<string name="story_web3_description">100種類以上の分散型サービスで、NFTの交換・購入や、借入・預金を行うことができます。</string>
|
||||
<string name="story_web3_description">100以上の分散型サービスが利用可能</string>
|
||||
<string name="story_web3_title">Web3.0対応</string>
|
||||
<string name="sui_not_enough_coin_for_fee_description">続行するには少なくとも%1$sの受信取引が必要です</string>
|
||||
<string name="sui_not_enough_coin_for_fee_title">残高不足</string>
|
||||
|
|
@ -1014,8 +1019,8 @@
|
|||
<string name="token_details_hide_alert_message">このトークンをメイン画面から非表示にします。トークンの管理ページからいつでも再度追加できます。</string>
|
||||
<string name="token_details_hide_alert_title">%sを非表示</string>
|
||||
<string name="token_details_hide_token">トークンを非表示</string>
|
||||
<string name="token_details_staking_block_subtitle">ステーキングにより、 %1$sを獲得し、 %2$s日ごとに報酬を受け取ることができます。</string>
|
||||
<string name="token_details_staking_block_title">年間最大%sのステーキング報酬を獲得</string>
|
||||
<string name="token_details_staking_block_subtitle">ステーキングすると%1$sを受け取り、 %2$s日ごとに報酬を獲得できます</string>
|
||||
<string name="token_details_staking_block_title">ステーキングサービス</string>
|
||||
<string name="token_details_token_type_subtitle">%%image%% %2$s ネットワークの %1$s トークン</string>
|
||||
<string name="token_details_token_type_subtitle_no_standard">%%image%% %1$sネットワーク上のトークン</string>
|
||||
<string name="token_details_unable_hide_alert_message">%1$s ( %2$s ) トークンは%3$sネットワークの主要通貨であり、このネットワーク上の他のトークンがリストにある限り、非表示にすることはできません。</string>
|
||||
|
|
@ -1039,6 +1044,8 @@
|
|||
<string name="transaction_history_transaction_validator">バリデーター: %s</string>
|
||||
<string name="transfer_min_amount_error">最小%s</string>
|
||||
<string name="transfer_notification_invalid_minimum_transaction_amount_text">最小取引金額は%1$sです。</string>
|
||||
<string name="tron_will_be_send_token_fee_description">Tronネットワークの人気トークンの手数料は高めです。TRXをステーキングすると、より安く、あるいは無料で取引できます。</string>
|
||||
<string name="tron_will_be_send_token_fee_title">Tronネットワーク手数料を節約</string>
|
||||
<string name="try_to_load_data_again_button_title">もう一度やり直してください</string>
|
||||
<string name="twin_error_same_card">同じカードをスキャンしました。ツインウォレットを作成するには、番号%dのカードをスキャンする必要があります。</string>
|
||||
<string name="twin_error_wrong_twin">間違ったツインカードをスキャンしました。別のカードをお試しください。</string>
|
||||
|
|
@ -1234,6 +1241,7 @@
|
|||
<string name="wc_common_network">ネットワーク</string>
|
||||
<string name="wc_common_networks">ネットワーク</string>
|
||||
<string name="wc_common_wallet">ウォレット</string>
|
||||
<string name="wc_connected_networks">接続されたネットワーク</string>
|
||||
<string name="wc_connection_reqeust_can_view_balance">ウォレットの残高とアクティビティを表示する</string>
|
||||
<string name="wc_connection_reqeust_cant_sign">通知なしに取引に署名する</string>
|
||||
<string name="wc_connection_reqeust_request_approval">取引の承認をリクエストする</string>
|
||||
|
|
@ -1249,6 +1257,8 @@
|
|||
<string name="wc_new_connection">新しい接続</string>
|
||||
<string name="wc_no_sessions_desc">ウォレットを別のdAppに接続する</string>
|
||||
<string name="wc_no_sessions_title">セッションなし</string>
|
||||
<string name="wc_notification_security_risk_subtitle">このドメインは複数のセキュリティプロバイダーから安全でないとの警告を受けています。あなたの資産を守るため、直ちにアクセスを中止してください。</string>
|
||||
<string name="wc_notification_security_risk_title">既知のセキュリティリスク</string>
|
||||
<string name="wc_wallet_connect">ウォレットコネクト</string>
|
||||
<string name="welcome_interrupted_backup_alert_discard">破棄</string>
|
||||
<string name="welcome_interrupted_backup_alert_message">バックアップが中断されました。再開しますか?</string>
|
||||
|
|
|
|||
|
|
@ -445,7 +445,7 @@
|
|||
<string name="markets_selector_interval_7d_title">7д</string>
|
||||
<string name="markets_selector_interval_all_title">Все</string>
|
||||
<string name="markets_sort_by_experienced_buyers_title">Опытные трейдеры</string>
|
||||
<string name="markets_sort_by_rating_title">Рейтинг</string>
|
||||
<string name="markets_sort_by_rating_title">Капитализация</string>
|
||||
<string name="markets_sort_by_title">Сортировать по</string>
|
||||
<string name="markets_sort_by_top_gainers_title">Лидеры роста</string>
|
||||
<string name="markets_sort_by_top_losers_title">Лидеры падения</string>
|
||||
|
|
@ -945,7 +945,7 @@
|
|||
<string name="story_finish_description">Используйте его на ходу, в любом месте, в любое время. Без проводов и батареек. Как только понадобится крипта, просто приложите карту или кольцо к телефону.</string>
|
||||
<string name="story_finish_title">Кошелек для каждого</string>
|
||||
<string name="story_meet_title">Встречайте Tangem</string>
|
||||
<string name="story_web3_description">Обменивайте, покупайте NFT, получайте займы и делайте вклады в более чем 100 различных децентрализованных сервисах</string>
|
||||
<string name="story_web3_description">Более 100 децентрализованных сервисов уже доступны для интеграции</string>
|
||||
<string name="story_web3_title">Поддержка Web 3.0</string>
|
||||
<string name="sui_not_enough_coin_for_fee_description">Для отправки требуется входящая транзакция на сумму не менее %1$s</string>
|
||||
<string name="sui_not_enough_coin_for_fee_title">Недостаточно средств</string>
|
||||
|
|
@ -1000,7 +1000,7 @@
|
|||
<string name="token_details_hide_alert_title">Скрыть %s</string>
|
||||
<string name="token_details_hide_token">Скрыть токен</string>
|
||||
<string name="token_details_staking_block_subtitle">Стейкинг позволяет вам зарабатывать %1$s и получать вознаграждения каждые %2$s дней</string>
|
||||
<string name="token_details_staking_block_title">Зарабатывайте до %s вознаграждений за стейкинг ежегодно</string>
|
||||
<string name="token_details_staking_block_title">Сервис стейкинга</string>
|
||||
<string name="token_details_token_type_subtitle">%1$s токен в сети %%image%% %2$s</string>
|
||||
<string name="token_details_token_type_subtitle_no_standard">Токен в сети %%image%% %1$s</string>
|
||||
<string name="token_details_unable_hide_alert_message">Токен %1$s (%2$s) является основной валютой в сети %3$s и не может быть скрыт до тех пор, пока у вас в списке есть другие токены этой сети</string>
|
||||
|
|
|
|||
|
|
@ -526,6 +526,9 @@
|
|||
<string name="no_account_polkadot">Обліковий запис одержувача не активовано. Надішліть %s або більше, щоб активувати обліковий запис.</string>
|
||||
<string name="no_account_send_to_create">Для створення акаунту надішліть кошти на цю адресу</string>
|
||||
<string name="no_trustline_xlm_asset">Акаунт одержувача не містить трастлайну для активу, що надсилається.</string>
|
||||
<string name="notification_referral_promo_button">Приєднатися</string>
|
||||
<string name="notification_referral_promo_text">Поділіться промокодом — заробіть 5 USDT з кожної покупки. Ваші друзі отримають знижку 10% на картку Tangem!</string>
|
||||
<string name="notification_referral_promo_title">Отримуй бонуси за кожного друга!</string>
|
||||
<string name="onboarding_access_code_feature_1_description">Налаштуйте єдиний код доступу для захисту всіх ваших карток або кілець</string>
|
||||
<string name="onboarding_access_code_feature_1_title">Захист</string>
|
||||
<string name="onboarding_access_code_feature_2_description">Встановіть індивідуальний код доступу для кожної картки або кільця пізніше.</string>
|
||||
|
|
@ -942,7 +945,7 @@
|
|||
<string name="story_finish_description">Використовуйте його в дорозі, будь-де і будь-коли. Ніяких дротів чи батарейок. Просто прикладіть картку або кільце до телефону, коли вам потрібна криптовалюта.</string>
|
||||
<string name="story_finish_title">Гаманець для кожного</string>
|
||||
<string name="story_meet_title">Зустрічайте Tangem</string>
|
||||
<string name="story_web3_description">Обмінюйте, купуйте NFT, отримуйте позики та робіть депозити у понад 100 різних децентралізованих сервісах</string>
|
||||
<string name="story_web3_description">Доступно понад 100 інтеграцій з децентралізованими сервісами</string>
|
||||
<string name="story_web3_title">Web 3.0 сумісність</string>
|
||||
<string name="sui_not_enough_coin_for_fee_description">Для відправки потрібна вхідна транзакція на суму не менше %1$s</string>
|
||||
<string name="sui_not_enough_coin_for_fee_title">Недостатньо коштів</string>
|
||||
|
|
@ -997,7 +1000,7 @@
|
|||
<string name="token_details_hide_alert_title">Приховати %s</string>
|
||||
<string name="token_details_hide_token">Приховати токен</string>
|
||||
<string name="token_details_staking_block_subtitle">Стейкінг дозволяє заробляти %1$s і отримувати винагороду кожні %2$s днів</string>
|
||||
<string name="token_details_staking_block_title">Заробляйте до %s винагород за стейкінг щороку</string>
|
||||
<string name="token_details_staking_block_title">Сервіс стейкінгу</string>
|
||||
<string name="token_details_token_type_subtitle">%1$s токен в мережі %%image%% %2$s</string>
|
||||
<string name="token_details_token_type_subtitle_no_standard">Токен у мережі %%image%% %1$s</string>
|
||||
<string name="token_details_unable_hide_alert_message">Токен %1$s (%2$s) є основною валютою в мережі %3$s і не може бути прихований до тих пір, поки у вас в списку є інші токени цієї мережі</string>
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@
|
|||
<string name="exchange_tokens_unavailable_tokens_header">Cannot be swapped for %s</string>
|
||||
<string name="express_by_provider">Provided by</string>
|
||||
<string name="express_cex_status_button_title">Status</string>
|
||||
<string name="express_choose_providers_subtitle">Tangem offers token swaps via 3rd-party providers according to each provider\'s terms</string>
|
||||
<string name="express_choose_providers_subtitle">Providers facilitate transactions</string>
|
||||
<string name="express_choose_providers_title">Provider</string>
|
||||
<string name="express_error_code">An error occurred. Code: %s</string>
|
||||
<string name="express_error_provider_amount_roundup">Error %1$s. The selected provider cannot process the specified transaction. Please round the value up to %2$s or change it</string>
|
||||
|
|
@ -320,11 +320,11 @@
|
|||
<string name="express_exchange_status_verifying">Verification required</string>
|
||||
<string name="express_exchange_status_waiting_tx_hash">Awaiting transaction hash</string>
|
||||
<string name="express_exchange_token_list_subtitle">List of all tokens added to your wallet</string>
|
||||
<string name="express_fetch_best_rates">Fetching best rates...</string>
|
||||
<string name="express_fetch_best_rates">Fetching current rates...</string>
|
||||
<string name="express_floating_rate">Floating rate</string>
|
||||
<string name="express_legal_one_placeholder">By using swap functionality, you agree with provider’s %s</string>
|
||||
<string name="express_legal_two_placeholders">By using swap functionality, you agree with provider’s %1$s and %2$s</string>
|
||||
<string name="express_more_providers_soon">More providers are coming soon.\nStay tuned!</string>
|
||||
<string name="express_more_providers_soon">More providers will be available soon</string>
|
||||
<string name="express_provider">Provider</string>
|
||||
<string name="express_provider_best_rate">Best rate</string>
|
||||
<string name="express_provider_max_amount">Available up to %s</string>
|
||||
|
|
@ -420,6 +420,7 @@
|
|||
<string name="markets_add_to_my_portfolio_unavailable_description">This asset is currently not supported in the wallet</string>
|
||||
<string name="markets_add_to_my_portfolio_unavailable_for_wallet_description">This asset is not available for this wallet</string>
|
||||
<string name="markets_add_token">Add</string>
|
||||
<string name="markets_apy_placeholder">APY %s</string>
|
||||
<string name="markets_available_networks">Available networks</string>
|
||||
<string name="markets_common_my_portfolio">My portfolio</string>
|
||||
<string name="markets_common_title">Market</string>
|
||||
|
|
@ -444,11 +445,14 @@
|
|||
<string name="markets_selector_interval_7d_title">7d</string>
|
||||
<string name="markets_selector_interval_all_title">All</string>
|
||||
<string name="markets_sort_by_experienced_buyers_title">Experienced buyers</string>
|
||||
<string name="markets_sort_by_rating_title">Rating</string>
|
||||
<string name="markets_sort_by_rating_title">Capitalization</string>
|
||||
<string name="markets_sort_by_title">Sort By</string>
|
||||
<string name="markets_sort_by_top_gainers_title">Top Gainers</string>
|
||||
<string name="markets_sort_by_top_losers_title">Top Losers</string>
|
||||
<string name="markets_sort_by_trending_title">Trending</string>
|
||||
<string name="markets_staking_banner_description_placeholder">Staking is the easiest way to receive rewards on your crypto. %s</string>
|
||||
<string name="markets_staking_banner_description_show_more">Show more</string>
|
||||
<string name="markets_staking_banner_title">Earn up to %s APY</string>
|
||||
<string name="markets_token_details_about_token_title">About %s</string>
|
||||
<plurals name="markets_token_details_amount_exchanges">
|
||||
<item quantity="one">%d exchange</item>
|
||||
|
|
@ -493,9 +497,9 @@
|
|||
<string name="markets_token_details_market_capitalization">Market cap</string>
|
||||
<string name="markets_token_details_market_capitalization_description">The total market value of a cryptocurrency, calculated by multiplying the current price of the coin by the total number of coins in circulation</string>
|
||||
<string name="markets_token_details_market_capitalization_full">Market cap</string>
|
||||
<string name="markets_token_details_market_rating">Market rating</string>
|
||||
<string name="markets_token_details_market_rating">Market position</string>
|
||||
<string name="markets_token_details_market_rating_description">Position in crypto rating between all coins based on market capitalization</string>
|
||||
<string name="markets_token_details_market_rating_full">Market rating</string>
|
||||
<string name="markets_token_details_market_rating_full">Market position</string>
|
||||
<string name="markets_token_details_max_supply">Max supply</string>
|
||||
<string name="markets_token_details_max_supply_description">The maximum number of coins or tokens that can ever exist for a particular cryptocurrency</string>
|
||||
<string name="markets_token_details_max_supply_full">Max supply</string>
|
||||
|
|
@ -531,6 +535,13 @@
|
|||
<string name="nft_details_base_information">Base information</string>
|
||||
<string name="nft_details_chain">Chain</string>
|
||||
<string name="nft_details_contract_address">Contract Address</string>
|
||||
<string name="nft_details_info_chain">stub</string>
|
||||
<string name="nft_details_info_contract_address">stub</string>
|
||||
<string name="nft_details_info_rarity_label">stub</string>
|
||||
<string name="nft_details_info_rarity_rank">stub</string>
|
||||
<string name="nft_details_info_token_address">stub</string>
|
||||
<string name="nft_details_info_token_id">stub</string>
|
||||
<string name="nft_details_info_token_standard">stub</string>
|
||||
<string name="nft_details_last_sale_price">Last sale price</string>
|
||||
<string name="nft_details_rarity_label">Rarity label</string>
|
||||
<string name="nft_details_rarity_rank">Rarity rank</string>
|
||||
|
|
@ -538,13 +549,6 @@
|
|||
<string name="nft_details_token_id">Token ID</string>
|
||||
<string name="nft_details_token_standard">Token Standard</string>
|
||||
<string name="nft_details_traits">Traits</string>
|
||||
<string name="nft_details_info_rarity_label"></string>
|
||||
<string name="nft_details_info_rarity_rank"></string>
|
||||
<string name="nft_details_info_token_standard"></string>
|
||||
<string name="nft_details_info_token_address"></string>
|
||||
<string name="nft_details_info_contract_address"></string>
|
||||
<string name="nft_details_info_token_id"></string>
|
||||
<string name="nft_details_info_chain"></string>
|
||||
<string name="nft_empty_search">No results. Please try another request.</string>
|
||||
<string name="nft_no_collection">No collection</string>
|
||||
<string name="nft_receive_available_section_title">Available</string>
|
||||
|
|
@ -664,7 +668,7 @@
|
|||
<string name="onboarding_wallet_info_title_second">Identical cards</string>
|
||||
<string name="onboarding_wallet_info_title_third">Access code</string>
|
||||
<string name="onramp_avaiable_with_payment_methods">Available with %s</string>
|
||||
<string name="onramp_choose_provider_title_hint">Buying crypto in Tangem is powered by third-party providers on their terms.</string>
|
||||
<string name="onramp_choose_provider_title_hint">Providers facilitate transactions</string>
|
||||
<string name="onramp_country_search">Search by country</string>
|
||||
<string name="onramp_country_unavailable">Unavailable</string>
|
||||
<string name="onramp_currency_other">Other currencies</string>
|
||||
|
|
@ -855,16 +859,16 @@
|
|||
<string name="staking_amount_tron_integer_error_unstaking">Unstaking amount will be rounded to %1$s TRX due to network rules.</string>
|
||||
<string name="staking_claim_unstaked">Claim unstaked</string>
|
||||
<string name="staking_details_account_fee">Stake account fee</string>
|
||||
<string name="staking_details_account_fee_info">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.</string>
|
||||
<string name="staking_details_account_fee_info">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.</string>
|
||||
<string name="staking_details_annual_percentage_rate">Annual percentage rate</string>
|
||||
<string name="staking_details_annual_percentage_rate_info">The annual percentage return you can earn from participating in staking.</string>
|
||||
<string name="staking_details_apr">APR</string>
|
||||
<string name="staking_details_auto_claiming_rewards_daily_text">Rewards automatically accumulate in your staking balance daily.</string>
|
||||
<string name="staking_details_available">Available</string>
|
||||
<string name="staking_details_average_reward_rate">Average Reward Rate</string>
|
||||
<string name="staking_details_banner_text">What is Staking?</string>
|
||||
<string name="staking_details_banner_text">How Staking Works?</string>
|
||||
<string name="staking_details_estimated_profit">%s est. profit</string>
|
||||
<string name="staking_details_market_rating">Market rating</string>
|
||||
<string name="staking_details_market_rating">Market position</string>
|
||||
<string name="staking_details_metrics_block_header">Metrics</string>
|
||||
<string name="staking_details_min_rewards_notification">According to %1$s network rules, claims are possible from %2$s. Amounts below will be credited to your account upon unstaking.</string>
|
||||
<string name="staking_details_minimum_requirement">Minimum Requirement</string>
|
||||
|
|
@ -890,15 +894,15 @@
|
|||
<string name="staking_notification_additional_ada_deposit_text">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.</string>
|
||||
<string name="staking_notification_additional_ada_deposit_title">ADA Staking Details</string>
|
||||
<string name="staking_notification_claim_rewards_text">Earned rewards will be sent to your wallet and available for use immediately</string>
|
||||
<string name="staking_notification_earn_rewards_text">Stake securely and start earning your rewards</string>
|
||||
<string name="staking_notification_earn_rewards_text_daily">Stake securely and start earning daily rewards</string>
|
||||
<string name="staking_notification_earn_rewards_text_hourly">Stake securely and start earning hourly rewards</string>
|
||||
<string name="staking_notification_earn_rewards_text_monthly">Stake securely and start earning monthly rewards</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_day">Staking allows you to earn %1$s. Your staking rewards arrive every day.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_hour">Staking allows you to earn %1$s. Your staking rewards arrive every hour.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_month">Staking allows you to earn %1$s. Your staking rewards arrive every month.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_week">Staking allows you to earn %1$s. Your staking rewards arrive every week.</string>
|
||||
<string name="staking_notification_earn_rewards_text_weekly">Stake securely and start earning weekly rewards</string>
|
||||
<string name="staking_notification_earn_rewards_text">Tangem allows users to stake their crypto</string>
|
||||
<string name="staking_notification_earn_rewards_text_daily">Tangem allows users to stake their crypto</string>
|
||||
<string name="staking_notification_earn_rewards_text_hourly">Tangem allows users to stake their crypto</string>
|
||||
<string name="staking_notification_earn_rewards_text_monthly">Tangem allows users to stake their crypto</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_day">Staking allows you to receive %1$s. Your staking rewards arrive every day.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_hour">Staking allows you to receive %1$s. Your staking rewards arrive every hour.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_month">Staking allows you to receive %1$s. Your staking rewards arrive every month.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_week">Staking allows you to receive %1$s. Your staking rewards arrive every week.</string>
|
||||
<string name="staking_notification_earn_rewards_text_weekly">Tangem allows users to stake their crypto</string>
|
||||
<string name="staking_notification_earn_rewards_title">Earn staking rewards</string>
|
||||
<string name="staking_notification_low_staked_balance_text">Your remaining staked balance will be too low to unstake. You’ll need to stake more to meet the minimum unstake amount.</string>
|
||||
<string name="staking_notification_low_staked_balance_title">Low staked balance</string>
|
||||
|
|
@ -980,7 +984,7 @@
|
|||
<string name="story_finish_description">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.</string>
|
||||
<string name="story_finish_title">The Wallet for Everyone</string>
|
||||
<string name="story_meet_title">Meet Tangem</string>
|
||||
<string name="story_web3_description">Exchange, buy NFT\'s, make loans and deposits in more than 100 different decentralized services</string>
|
||||
<string name="story_web3_description">More than 100 decentralized service integrations are available</string>
|
||||
<string name="story_web3_title">Web 3.0 Compatible</string>
|
||||
<string name="sui_not_enough_coin_for_fee_description">An incoming transaction of at least %1$s is required to proceed</string>
|
||||
<string name="sui_not_enough_coin_for_fee_title">Insufficient funds</string>
|
||||
|
|
@ -1034,8 +1038,8 @@
|
|||
<string name="token_details_hide_alert_message">You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page.</string>
|
||||
<string name="token_details_hide_alert_title">Hide %s</string>
|
||||
<string name="token_details_hide_token">Hide token</string>
|
||||
<string name="token_details_staking_block_subtitle">Staking allows you to earn %1$s and get rewards every %2$s days</string>
|
||||
<string name="token_details_staking_block_title">Earn up to %s staking rewards yearly</string>
|
||||
<string name="token_details_staking_block_subtitle">Staking allows you to receive %1$s and get rewards every %2$s days</string>
|
||||
<string name="token_details_staking_block_title">Staking Service</string>
|
||||
<string name="token_details_token_type_subtitle">%1$s token in %%image%% %2$s network</string>
|
||||
<string name="token_details_token_type_subtitle_no_standard">Token in %%image%% %1$s network</string>
|
||||
<string name="token_details_unable_hide_alert_message">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</string>
|
||||
|
|
@ -1059,6 +1063,8 @@
|
|||
<string name="transaction_history_transaction_validator">validator: %s</string>
|
||||
<string name="transfer_min_amount_error">Minimum %s</string>
|
||||
<string name="transfer_notification_invalid_minimum_transaction_amount_text">The minimum transaction amount is %1$s.</string>
|
||||
<string name="tron_will_be_send_token_fee_description">Tron network fees for popular tokens are higher. Stake some TRX for cheaper or free transactions.</string>
|
||||
<string name="tron_will_be_send_token_fee_title">Save on Tron network fees</string>
|
||||
<string name="try_to_load_data_again_button_title">Try again</string>
|
||||
<string name="twin_error_same_card">You\'ve scanned the same card. To create a twin wallet you need to scan the card with number %d</string>
|
||||
<string name="twin_error_wrong_twin">You\'ve scanned wrong twin card. Please try another one</string>
|
||||
|
|
@ -1310,8 +1316,9 @@
|
|||
<string name="wc_connection_reqeust_will_not">Will not be able to</string>
|
||||
<string name="wc_connection_reqeust_would_like">Would like to</string>
|
||||
<string name="wc_connection_request">Connection request</string>
|
||||
<string name="wc_transaction_request">Transaction request</string>
|
||||
<string name="wc_connections">Connections</string>
|
||||
<string name="wc_contents">Contents</string>
|
||||
<string name="wc_copy_data_button_text">Copy data</string>
|
||||
<string name="wc_disconnect_all">Disconnect all</string>
|
||||
<string name="wc_disconnect_all_alert_desc">Text about discnected all dApps</string>
|
||||
<string name="wc_disconnect_all_alert_title">Disconect All dApps</string>
|
||||
|
|
@ -1322,13 +1329,11 @@
|
|||
<string name="wc_no_sessions_title">No sessions</string>
|
||||
<string name="wc_notification_security_risk_subtitle">This domain is flagged as unsafe by multiple security providers. Leave immediately to protect your assets</string>
|
||||
<string name="wc_notification_security_risk_title">Known security risk</string>
|
||||
<string name="wc_wallet_connect">Wallet connect</string>
|
||||
<string name="wc_request_from">Request from</string>
|
||||
<string name="wc_sign_message_button_text">Sign</string>
|
||||
<string name="wc_copy_data_button_text">Copy data</string>
|
||||
<string name="wc_transaction_request_title">Transaction request</string>
|
||||
<string name="wc_signature_type">Signature Type</string>
|
||||
<string name="wc_contents">Contents</string>
|
||||
<string name="wc_transaction_request">Transaction request</string>
|
||||
<string name="wc_transaction_request_title">Transaction request</string>
|
||||
<string name="wc_wallet_connect">Wallet connect</string>
|
||||
<string name="welcome_interrupted_backup_alert_discard">Discard</string>
|
||||
<string name="welcome_interrupted_backup_alert_message">You have an interrupted backup. Do you want to resume?</string>
|
||||
<string name="welcome_interrupted_backup_alert_resume">Yes, resume</string>
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
)
|
||||
|
||||
|
|
|
|||
BIN
core/ui/src/main/res/drawable/img_referral_promo.webp
Normal file
BIN
core/ui/src/main/res/drawable/img_referral_promo.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<Boolean> {
|
||||
return flowOf(false) // Use it on new promo action
|
||||
override fun isReadyToShowWalletPromo(userWalletId: UserWalletId, promoId: PromoId): Flow<Boolean> {
|
||||
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<Boolean> {
|
||||
return flowOf(false) // Use it on new promo action
|
||||
override fun isReadyToShowTokenPromo(promoId: PromoId): Flow<Boolean> {
|
||||
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<StoryContent?> = isReadyToShowStories(id).mapLatest {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -23,4 +23,8 @@ data class PromoBanner(
|
|||
private companion object {
|
||||
const val ACTIVE_STATUS = "active"
|
||||
}
|
||||
}
|
||||
|
||||
enum class PromoId {
|
||||
Referral,
|
||||
}
|
||||
|
|
@ -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<Either<Throwable, StoryContent?>> = promoRepository.getStoryById(id)
|
||||
.map<StoryContent?, Either<Throwable, StoryContent?>> { it.right() }
|
||||
.catch { emit(it.left()) }
|
||||
.onEmpty { emit(null.right()) }
|
||||
operator fun invoke(id: String): Flow<Either<Throwable, StoryContent?>> {
|
||||
return isFCAAllowed(id).transform { isAllowed ->
|
||||
if (isAllowed) {
|
||||
emitAll(
|
||||
promoRepository.getStoryById(id)
|
||||
.map<StoryContent?, Either<Throwable, StoryContent?>> { it.right() }
|
||||
.catch { emit(it.left()) }
|
||||
.onEmpty { emit(null.right()) },
|
||||
)
|
||||
} else {
|
||||
emit(null.right())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun invokeSync(id: String, refresh: Boolean = false): Either<Throwable, StoryContent?> = 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<Boolean> {
|
||||
return if (id == StoryContentIds.STORY_FIRST_TIME_SWAP.id) {
|
||||
settingsRepository.getUserCountryCode()
|
||||
.filterNotNull()
|
||||
.timeout(5.seconds)
|
||||
.map { !it.needApplyFCARestrictions() }
|
||||
} else {
|
||||
flowOf(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Boolean>
|
||||
fun isReadyToShowWalletPromo(userWalletId: UserWalletId, promoId: PromoId): Flow<Boolean>
|
||||
|
||||
fun isReadyToShowTokenSwapPromo(): Flow<Boolean>
|
||||
fun isReadyToShowTokenPromo(promoId: PromoId): Flow<Boolean>
|
||||
|
||||
suspend fun setNeverToShowWalletSwapPromo()
|
||||
suspend fun setNeverToShowWalletPromo(promoId: PromoId)
|
||||
|
||||
suspend fun setNeverToShowTokenSwapPromo()
|
||||
suspend fun setNeverToShowTokenPromo(promoId: PromoId)
|
||||
// endregion
|
||||
|
||||
// region Stories
|
||||
|
|
|
|||
|
|
@ -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<Boolean> = promoRepository.isReadyToShowTokenPromo(promoId)
|
||||
|
||||
suspend fun neverToShow(promoId: PromoId) = promoRepository.setNeverToShowTokenPromo(promoId)
|
||||
}
|
||||
|
|
@ -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<Boolean> {
|
||||
return flow {
|
||||
emit(false)
|
||||
emitAll(promoRepository.isReadyToShowWalletPromo(userWalletId, promoId))
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun neverToShow(promoId: PromoId) = promoRepository.setNeverToShowWalletPromo(promoId)
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
package com.tangem.domain.promo
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class ShouldShowSwapPromoTokenUseCase(private val promoRepository: PromoRepository) {
|
||||
|
||||
operator fun invoke(): Flow<Boolean> = promoRepository.isReadyToShowTokenSwapPromo()
|
||||
|
||||
suspend fun neverToShow() = promoRepository.setNeverToShowTokenSwapPromo()
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
package com.tangem.domain.promo
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class ShouldShowSwapPromoWalletUseCase(private val promoRepository: PromoRepository) {
|
||||
|
||||
operator fun invoke(): Flow<Boolean> = promoRepository.isReadyToShowWalletSwapPromo()
|
||||
|
||||
suspend fun neverToShow() = promoRepository.setNeverToShowWalletSwapPromo()
|
||||
}
|
||||
|
|
@ -34,7 +34,7 @@ interface SettingsRepository {
|
|||
|
||||
suspend fun setMarketsTooltipShown(value: Boolean)
|
||||
|
||||
suspend fun getUserCountryCodeSync(): UserCountry?
|
||||
fun getUserCountryCodeSync(): UserCountry?
|
||||
|
||||
fun getUserCountryCode(): StateFlow<UserCountry?>
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class GetUserCountryUseCase(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun invokeSync(): Either<UserCountryError, UserCountry> {
|
||||
fun invokeSync(): Either<UserCountryError, UserCountry> {
|
||||
return either {
|
||||
val userCountryCode = catch(
|
||||
block = { settingsRepository.getUserCountryCodeSync() },
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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<MarketsTokenDetailsComponent.Params>()
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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<Boolean>,
|
||||
) : Converter<TokenMarketInfo, MarketsTokenDetailsUM.Description?> {
|
||||
|
||||
override fun convert(value: TokenMarketInfo): MarketsTokenDetailsUM.Description? {
|
||||
if (needApplyFCARestrictions()) return null
|
||||
return value.shortDescription?.let { desc ->
|
||||
MarketsTokenDetailsUM.Description(
|
||||
shortDescription = stringReference(desc),
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import com.tangem.utils.converter.Converter
|
|||
@Suppress("LongParameterList")
|
||||
internal class TokenMarketInfoConverter(
|
||||
private val appCurrency: Provider<AppCurrency>,
|
||||
private val needApplyFCARestrictions: Provider<Boolean>,
|
||||
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(
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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<String, ReferralData> = 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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<CommonSendRoute.Amount>(),
|
||||
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<CommonSendRoute.Amount>(),
|
||||
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<CommonSendRoute.Fee>(),
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() }),
|
||||
)
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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() },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() }),
|
||||
)
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<WalletNotification>.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<WalletNotification>.addWarningNotifications(
|
||||
cardTypesResolver: CardTypesResolver,
|
||||
tokenList: Lce<TokenListError, TokenList>,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -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<TokenListError, TokenList>) {
|
||||
/* 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<WalletNotificati
|
|||
Notification(
|
||||
config = it.config,
|
||||
modifier = modifier.animateItem(fadeInSpec = null, fadeOutSpec = null),
|
||||
iconSize = if (it is WalletNotification.ReferralPromo) {
|
||||
54.dp
|
||||
} else {
|
||||
20.dp
|
||||
},
|
||||
iconTint = when (it) {
|
||||
is WalletNotification.Critical -> TangemTheme.colors.icon.warning
|
||||
is WalletNotification.Informational -> TangemTheme.colors.icon.accent
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 ^
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue