Updated on 2026-08-14
This commit is contained in:
commit
acf110dbcc
30 changed files with 171 additions and 63 deletions
|
|
@ -4,30 +4,38 @@ import android.app.Activity
|
||||||
import android.app.Application.ActivityLifecycleCallbacks
|
import android.app.Application.ActivityLifecycleCallbacks
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import java.util.WeakHashMap
|
import timber.log.Timber
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
class ForegroundActivityObserver {
|
object ForegroundActivityObserver {
|
||||||
|
|
||||||
private val activities = WeakHashMap<KClass<out Activity>, AppCompatActivity>()
|
private val activities = HashMap<KClass<out Activity>, AppCompatActivity>()
|
||||||
|
|
||||||
val foregroundActivity: AppCompatActivity?
|
val foregroundActivity: AppCompatActivity?
|
||||||
get() = activities.entries
|
get() = activities.entries
|
||||||
.firstOrNull { it.value?.isDestroyed == false }
|
.firstOrNull { entry ->
|
||||||
|
Timber.i("foregroundActivity: ${entry.key} | ${entry.value.isDestroyed}")
|
||||||
|
entry.value.isDestroyed == false
|
||||||
|
}
|
||||||
?.value
|
?.value
|
||||||
|
|
||||||
internal val callbacks: ActivityLifecycleCallbacks
|
internal val callbacks: ActivityLifecycleCallbacks
|
||||||
get() = Callbacks()
|
get() = Callbacks()
|
||||||
|
|
||||||
internal inner class Callbacks : ActivityLifecycleCallbacks {
|
internal class Callbacks : ActivityLifecycleCallbacks {
|
||||||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResumed(activity: Activity) {
|
override fun onActivityResumed(activity: Activity) {
|
||||||
activities[activity::class] = activity as? AppCompatActivity
|
Timber.i("onActivityResumed ${activity::class}")
|
||||||
|
if (activity is AppCompatActivity) {
|
||||||
|
Timber.i("onActivityResumed store activity")
|
||||||
|
activities[activity::class] = activity
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityDestroyed(activity: Activity) {
|
override fun onActivityDestroyed(activity: Activity) {
|
||||||
|
Timber.i("onActivityDestroyed")
|
||||||
activities.remove(activity::class)
|
activities.remove(activity::class)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -191,6 +191,7 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
|
||||||
private val onActivityResultCallbacks = mutableListOf<OnActivityResultCallback>()
|
private val onActivityResultCallbacks = mutableListOf<OnActivityResultCallback>()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
Timber.i("onCreate")
|
||||||
// We need to call it before onCreate to prevent unnecessary activity recreation
|
// We need to call it before onCreate to prevent unnecessary activity recreation
|
||||||
installAppTheme()
|
installAppTheme()
|
||||||
|
|
||||||
|
|
@ -328,15 +329,18 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
|
Timber.i("onStart")
|
||||||
dialogManager.onStart(this)
|
dialogManager.onStart(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
dialogManager.onStop()
|
dialogManager.onStop()
|
||||||
super.onStop()
|
super.onStop()
|
||||||
|
Timber.i("onStop")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
Timber.i("onDestroy")
|
||||||
// workaround: kill process when activity destroy to avoid state when lock() wallets
|
// workaround: kill process when activity destroy to avoid state when lock() wallets
|
||||||
// and navigation to unlock screen was skipped because system kills activity but not process
|
// and navigation to unlock screen was skipped because system kills activity but not process
|
||||||
if (BuildConfig.BUILD_TYPE != MOCKED_BUILD_TYPE) {
|
if (BuildConfig.BUILD_TYPE != MOCKED_BUILD_TYPE) {
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ import timber.log.Timber
|
||||||
|
|
||||||
lateinit var store: Store<AppState>
|
lateinit var store: Store<AppState>
|
||||||
|
|
||||||
lateinit var foregroundActivityObserver: ForegroundActivityObserver
|
val foregroundActivityObserver = ForegroundActivityObserver
|
||||||
internal lateinit var derivationsFinder: DerivationsFinder
|
internal lateinit var derivationsFinder: DerivationsFinder
|
||||||
|
|
||||||
open class TangemApplication : Application(), ImageLoaderFactory, Configuration.Provider {
|
open class TangemApplication : Application(), ImageLoaderFactory, Configuration.Provider {
|
||||||
|
|
@ -246,6 +246,7 @@ open class TangemApplication : Application(), ImageLoaderFactory, Configuration.
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
enableStrictModeInDebug()
|
enableStrictModeInDebug()
|
||||||
|
preInit()
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
init()
|
init()
|
||||||
}
|
}
|
||||||
|
|
@ -286,22 +287,25 @@ open class TangemApplication : Application(), ImageLoaderFactory, Configuration.
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize components that need to be initialized before [super.onCreate] is called
|
||||||
|
*/
|
||||||
|
private fun preInit() {
|
||||||
|
tangemAppLoggerInitializer.initialize()
|
||||||
|
registerActivityLifecycleCallbacks(foregroundActivityObserver.callbacks)
|
||||||
|
}
|
||||||
|
|
||||||
fun init() {
|
fun init() {
|
||||||
apiConfigsManager.initialize()
|
apiConfigsManager.initialize()
|
||||||
|
|
||||||
store = createReduxStore()
|
store = createReduxStore()
|
||||||
|
|
||||||
tangemAppLoggerInitializer.initialize()
|
|
||||||
|
|
||||||
Timber.i("APP STARTED")
|
Timber.i("APP STARTED")
|
||||||
if (BuildConfig.TESTER_MENU_ENABLED) {
|
if (BuildConfig.TESTER_MENU_ENABLED) {
|
||||||
Timber.i(featureTogglesManager.toString())
|
Timber.i(featureTogglesManager.toString())
|
||||||
Timber.i(excludedBlockchainsManager.toString())
|
Timber.i(excludedBlockchainsManager.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
foregroundActivityObserver = ForegroundActivityObserver()
|
|
||||||
registerActivityLifecycleCallbacks(foregroundActivityObserver.callbacks)
|
|
||||||
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
initWithConfigDependency(environmentConfig = environmentConfigStorage.initialize())
|
initWithConfigDependency(environmentConfig = environmentConfigStorage.initialize())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,12 @@ class AppsFlyerAnalyticsHandler(
|
||||||
}
|
}
|
||||||
|
|
||||||
class Builder : AnalyticsHandlerBuilder {
|
class Builder : AnalyticsHandlerBuilder {
|
||||||
override fun build(data: AnalyticsHandlerBuilder.Data): AnalyticsHandler? = when {
|
override fun build(data: AnalyticsHandlerBuilder.Data): AnalyticsHandler? = null
|
||||||
!data.isDebug -> AppsFlyerClient(data.application, data.config.appsFlyerApiKey, data.config.appsAppId)
|
// disabled for now until analytics strategy is defined
|
||||||
data.isDebug && data.logConfig.appsflyer -> AppsFlyerLogClient(data.jsonConverter)
|
// when {
|
||||||
else -> null
|
// !data.isDebug -> AppsFlyerClient(data.application, data.config.appsFlyerApiKey, data.config.appsAppId)
|
||||||
}?.let { AppsFlyerAnalyticsHandler(it) }
|
// data.isDebug && data.logConfig.appsflyer -> AppsFlyerLogClient(data.jsonConverter)
|
||||||
|
// else -> null
|
||||||
|
// }?.let { AppsFlyerAnalyticsHandler(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,4 +2,5 @@
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@mipmap/ic_launcher_background"/>
|
<background android:drawable="@mipmap/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
|
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
|
|
@ -19,7 +19,10 @@
|
||||||
<string name="account_archived_recover">回復する</string>
|
<string name="account_archived_recover">回復する</string>
|
||||||
<string name="account_archived_recover_dialog_description">「 %1$s 」を回復しようとしています。</string>
|
<string name="account_archived_recover_dialog_description">「 %1$s 」を回復しようとしています。</string>
|
||||||
<string name="account_archived_recover_dialog_title">アカウントを回復する</string>
|
<string name="account_archived_recover_dialog_title">アカウントを回復する</string>
|
||||||
|
<string name="account_archived_recover_error_message">すでにアクティブアカウント数の上限(20件)を超えています。復元するには1件アーカイブしてください。</string>
|
||||||
|
<string name="account_archived_recover_error_title">アカウントを復元できません</string>
|
||||||
<string name="account_archived_title">アーカイブ済み</string>
|
<string name="account_archived_title">アーカイブ済み</string>
|
||||||
|
<string name="account_could_not_create">アカウントを作成できませんでした。しばらくしてからもう一度お試しください。</string>
|
||||||
<string name="account_create_success_message">アカウントを作成しました</string>
|
<string name="account_create_success_message">アカウントを作成しました</string>
|
||||||
<string name="account_details_archive">アカウントをアーカイブする</string>
|
<string name="account_details_archive">アカウントをアーカイブする</string>
|
||||||
<string name="account_details_archive_action">アーカイブ</string>
|
<string name="account_details_archive_action">アーカイブ</string>
|
||||||
|
|
@ -258,6 +261,7 @@
|
||||||
<string name="common_left">残り%1$s</string>
|
<string name="common_left">残り%1$s</string>
|
||||||
<string name="common_legacy_bitcoin_address">レガシービットコイン</string>
|
<string name="common_legacy_bitcoin_address">レガシービットコイン</string>
|
||||||
<string name="common_locked">ロックされています</string>
|
<string name="common_locked">ロックされています</string>
|
||||||
|
<string name="common_locked_wallets">ロックされたウォレット</string>
|
||||||
<string name="common_main_network">メインネットワーク</string>
|
<string name="common_main_network">メインネットワーク</string>
|
||||||
<string name="common_month">月</string>
|
<string name="common_month">月</string>
|
||||||
<string name="common_network_fee_title">ネットワーク手数料</string>
|
<string name="common_network_fee_title">ネットワーク手数料</string>
|
||||||
|
|
@ -307,6 +311,7 @@
|
||||||
<string name="common_sign">署名</string>
|
<string name="common_sign">署名</string>
|
||||||
<string name="common_sign_and_send">署名して送信</string>
|
<string name="common_sign_and_send">署名して送信</string>
|
||||||
<string name="common_skip">スキップ</string>
|
<string name="common_skip">スキップ</string>
|
||||||
|
<string name="common_something_went_wrong">問題が発生しました</string>
|
||||||
<string name="common_stake">ステーキング</string>
|
<string name="common_stake">ステーキング</string>
|
||||||
<string name="common_staking">ステーキング</string>
|
<string name="common_staking">ステーキング</string>
|
||||||
<string name="common_start">始める</string>
|
<string name="common_start">始める</string>
|
||||||
|
|
@ -1143,7 +1148,7 @@
|
||||||
<string name="staking_notification_restake_rewards_text">獲得した報酬をステーキングに再投資し、潜在的な収益を増やします。</string>
|
<string name="staking_notification_restake_rewards_text">獲得した報酬をステーキングに再投資し、潜在的な収益を増やします。</string>
|
||||||
<string name="staking_notification_restake_text">再ステーキングを使うと、ステーキングを解除することなく、あるバリデータから別のバリデータに資金を移動できます。</string>
|
<string name="staking_notification_restake_text">再ステーキングを使うと、ステーキングを解除することなく、あるバリデータから別のバリデータに資金を移動できます。</string>
|
||||||
<string name="staking_notification_stake_entire_balance_text">残高のすべてをステーキングしようとしています。ステーキング解除や報酬請求にかかるネットワーク手数料をカバーするために、少額を残しておくことをお勧めします。</string>
|
<string name="staking_notification_stake_entire_balance_text">残高のすべてをステーキングしようとしています。ステーキング解除や報酬請求にかかるネットワーク手数料をカバーするために、少額を残しておくことをお勧めします。</string>
|
||||||
<string name="staking_notification_ton_activate_account">TONでステーキングを開始するには、まず任意の金額の送金を行ってください。これにより、ウォレットがアクティブになります。</string>
|
<string name="staking_notification_ton_activate_account">TONのステーキングを開始するには、まず自分のアドレスに少額の取引を送信します。これによりウォレットが有効になります。</string>
|
||||||
<string name="staking_notification_ton_extra_reserve_info">取引を完了するには、ネットワーク手数料に加えて最大0.2TONが必要になる場合があります。未使用分は返金されます。</string>
|
<string name="staking_notification_ton_extra_reserve_info">取引を完了するには、ネットワーク手数料に加えて最大0.2TONが必要になる場合があります。未使用分は返金されます。</string>
|
||||||
<string name="staking_notification_ton_extra_reserve_is_required">この操作を続行するには、ネットワーク手数料に加えて0.2 TONが必要です。残高を補充してください。</string>
|
<string name="staking_notification_ton_extra_reserve_is_required">この操作を続行するには、ネットワーク手数料に加えて0.2 TONが必要です。残高を補充してください。</string>
|
||||||
<string name="staking_notification_ton_extra_reserve_title">TONの準備金が必要です</string>
|
<string name="staking_notification_ton_extra_reserve_title">TONの準備金が必要です</string>
|
||||||
|
|
@ -1214,6 +1219,7 @@
|
||||||
<string name="story_web3_title">Web3.0対応</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_description">続行するには少なくとも%1$sの受信取引が必要です</string>
|
||||||
<string name="sui_not_enough_coin_for_fee_title">残高不足</string>
|
<string name="sui_not_enough_coin_for_fee_title">残高不足</string>
|
||||||
|
<string name="swap_approve_description">承認すると、このスマートコントラクトが今後の取引でトークンを利用できるようになります。</string>
|
||||||
<string name="swap_fixed_rate">固定レート</string>
|
<string name="swap_fixed_rate">固定レート</string>
|
||||||
<string name="swap_give_permission_fee_footer">ネットワークは、あなたがトークンのスワップを承認していることを確認するために、トークン承認手数料を請求します。</string>
|
<string name="swap_give_permission_fee_footer">ネットワークは、あなたがトークンのスワップを承認していることを確認するために、トークン承認手数料を請求します。</string>
|
||||||
<string name="swap_promo_text">より多くのトークンをより良いレートで、ウォレット内にて直接交換します。</string>
|
<string name="swap_promo_text">より多くのトークンをより良いレートで、ウォレット内にて直接交換します。</string>
|
||||||
|
|
@ -1653,7 +1659,7 @@
|
||||||
<string name="yield_module_approve_sheet_title">承認を確定する</string>
|
<string name="yield_module_approve_sheet_title">承認を確定する</string>
|
||||||
<string name="yield_module_balance_info_sheet_subtitle">資産残高に関するテキスト [プレースホルダー]</string>
|
<string name="yield_module_balance_info_sheet_subtitle">資産残高に関するテキスト [プレースホルダー]</string>
|
||||||
<string name="yield_module_balance_info_sheet_title">あなたの%sはAaveに預けられています</string>
|
<string name="yield_module_balance_info_sheet_title">あなたの%sはAaveに預けられています</string>
|
||||||
<string name="yield_module_earn_badge">%s%を獲得</string>
|
<string name="yield_module_earn_badge">%1$s%% を獲得</string>
|
||||||
<string name="yield_module_earn_sheet_available_title">利用可能</string>
|
<string name="yield_module_earn_sheet_available_title">利用可能</string>
|
||||||
<string name="yield_module_earn_sheet_current_apy_title">現在のAPY</string>
|
<string name="yield_module_earn_sheet_current_apy_title">現在のAPY</string>
|
||||||
<string name="yield_module_earn_sheet_my_funds_title">私の資金</string>
|
<string name="yield_module_earn_sheet_my_funds_title">私の資金</string>
|
||||||
|
|
@ -1670,6 +1676,8 @@
|
||||||
<string name="yield_module_fee_policy_sheet_title">手数料ポリシー</string>
|
<string name="yield_module_fee_policy_sheet_title">手数料ポリシー</string>
|
||||||
<string name="yield_module_main_view_approve_notification_description">ここに説明を入力してください。1〜3行が理想的です。[プレースホルダー]</string>
|
<string name="yield_module_main_view_approve_notification_description">ここに説明を入力してください。1〜3行が理想的です。[プレースホルダー]</string>
|
||||||
<string name="yield_module_main_view_approve_notification_title">トークン承認が必要</string>
|
<string name="yield_module_main_view_approve_notification_title">トークン承認が必要</string>
|
||||||
|
<string name="yield_module_network_fee_unreachable_notification_description">ネットワーク接続を確認してください</string>
|
||||||
|
<string name="yield_module_network_fee_unreachable_notification_title">ネットワーク手数料についての情報にアクセスできません</string>
|
||||||
<string name="yield_module_promo_screen_auto_balance_subtitle">アカウントへの入金はすべて自動的にAaveに貸し出されます。</string>
|
<string name="yield_module_promo_screen_auto_balance_subtitle">アカウントへの入金はすべて自動的にAaveに貸し出されます。</string>
|
||||||
<string name="yield_module_promo_screen_auto_balance_title">残高は自動的に計算されます</string>
|
<string name="yield_module_promo_screen_auto_balance_title">残高は自動的に計算されます</string>
|
||||||
<string name="yield_module_promo_screen_cash_out_subtitle">いつでも、即座に資金を送信、交換、売却できます。</string>
|
<string name="yield_module_promo_screen_cash_out_subtitle">いつでも、即座に資金を送信、交換、売却できます。</string>
|
||||||
|
|
|
||||||
|
|
@ -1298,6 +1298,8 @@
|
||||||
<string name="wallet_connect_error_wrong_card_selected">Неверная карта или кольцо выбрана в приложении Tangem</string>
|
<string name="wallet_connect_error_wrong_card_selected">Неверная карта или кольцо выбрана в приложении Tangem</string>
|
||||||
<string name="wallet_connect_failed_to_build_tx">Не удалось создать транзакцию из данных Dapp. Код: %s</string>
|
<string name="wallet_connect_failed_to_build_tx">Не удалось создать транзакцию из данных Dapp. Код: %s</string>
|
||||||
<string name="wallet_connect_generic_error_with_code">Произошла непредвиденная ошибка. Код ошибки: %d. Попробуйте, пожалуйста, позже. Если проблема будет продолжать возникать — обратитесь в службу поддержки.</string>
|
<string name="wallet_connect_generic_error_with_code">Произошла непредвиденная ошибка. Код ошибки: %d. Попробуйте, пожалуйста, позже. Если проблема будет продолжать возникать — обратитесь в службу поддержки.</string>
|
||||||
|
<string name="wallet_connect_multiple_transactions">Транзакция в несколько шагов</string>
|
||||||
|
<string name="wallet_connect_multiple_transactions_description">Чтобы успешно обработать запрос, ваша транзакция будет разделена на несколько частей. Для завершения потребуется несколько приложений карты.</string>
|
||||||
<string name="wallet_connect_no_sessions_message">Нет открытых сессий WalletConnect</string>
|
<string name="wallet_connect_no_sessions_message">Нет открытых сессий WalletConnect</string>
|
||||||
<string name="wallet_connect_no_sessions_title">Упс. Нет сессий.</string>
|
<string name="wallet_connect_no_sessions_title">Упс. Нет сессий.</string>
|
||||||
<string name="wallet_connect_pairing_error">Не удалось создать пару WalletConnect: %1$s</string>
|
<string name="wallet_connect_pairing_error">Не удалось создать пару WalletConnect: %1$s</string>
|
||||||
|
|
@ -1309,6 +1311,8 @@
|
||||||
<string name="wallet_connect_scanner_error_not_valid_card">Эту карту нельзя использовать с WalletConnect.</string>
|
<string name="wallet_connect_scanner_error_not_valid_card">Эту карту нельзя использовать с WalletConnect.</string>
|
||||||
<string name="wallet_connect_scanner_error_unsupported_network">Сеть не поддерживается. Пожалуйста, выберите другую сеть.</string>
|
<string name="wallet_connect_scanner_error_unsupported_network">Сеть не поддерживается. Пожалуйста, выберите другую сеть.</string>
|
||||||
<string name="wallet_connect_select_network">Выберите сеть</string>
|
<string name="wallet_connect_select_network">Выберите сеть</string>
|
||||||
|
<string name="wallet_connect_sending_multiple_explanation">Транзакция в процессе отправки. Пожалуйста, приложите карту несколько раз для её завершения.</string>
|
||||||
|
<string name="wallet_connect_sending_multiple_tx">Транзакция в обработке</string>
|
||||||
<string name="wallet_connect_sessions_title">Сессии WalletConnect</string>
|
<string name="wallet_connect_sessions_title">Сессии WalletConnect</string>
|
||||||
<string name="wallet_connect_subtitle">Подключение к dApps</string>
|
<string name="wallet_connect_subtitle">Подключение к dApps</string>
|
||||||
<string name="wallet_connect_title">WalletConnect</string>
|
<string name="wallet_connect_title">WalletConnect</string>
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,10 @@
|
||||||
<string name="account_archived_recover">Recover</string>
|
<string name="account_archived_recover">Recover</string>
|
||||||
<string name="account_archived_recover_dialog_description">You’re about to recover “%1$s”.</string>
|
<string name="account_archived_recover_dialog_description">You’re about to recover “%1$s”.</string>
|
||||||
<string name="account_archived_recover_dialog_title">Recover account</string>
|
<string name="account_archived_recover_dialog_title">Recover account</string>
|
||||||
|
<string name="account_archived_recover_error_message">You have already exceeded the limit of 20 active accounts. Archive one to recover</string>
|
||||||
|
<string name="account_archived_recover_error_title">Can\'t recover account</string>
|
||||||
<string name="account_archived_title">Archived</string>
|
<string name="account_archived_title">Archived</string>
|
||||||
|
<string name="account_could_not_create">We couldn’t create account. Please try again later.</string>
|
||||||
<string name="account_create_success_message">Account created</string>
|
<string name="account_create_success_message">Account created</string>
|
||||||
<string name="account_details_archive">Archive account</string>
|
<string name="account_details_archive">Archive account</string>
|
||||||
<string name="account_details_archive_action">Archive</string>
|
<string name="account_details_archive_action">Archive</string>
|
||||||
|
|
@ -314,6 +317,7 @@
|
||||||
<string name="common_sign">Sign</string>
|
<string name="common_sign">Sign</string>
|
||||||
<string name="common_sign_and_send">Sign and send</string>
|
<string name="common_sign_and_send">Sign and send</string>
|
||||||
<string name="common_skip">Skip</string>
|
<string name="common_skip">Skip</string>
|
||||||
|
<string name="common_something_went_wrong">Something went wrong</string>
|
||||||
<string name="common_stake">Stake</string>
|
<string name="common_stake">Stake</string>
|
||||||
<string name="common_staking">Staking</string>
|
<string name="common_staking">Staking</string>
|
||||||
<string name="common_start">Start</string>
|
<string name="common_start">Start</string>
|
||||||
|
|
@ -927,6 +931,7 @@
|
||||||
<string name="receive_bottom_sheet_warning_message_full">Send only %s to this address. Sending any other currency will result in its irreversible loss.</string>
|
<string name="receive_bottom_sheet_warning_message_full">Send only %s to this address. Sending any other currency will result in its irreversible loss.</string>
|
||||||
<string name="receive_bottom_sheet_warning_title">Send only %1$s on the %2$s network</string>
|
<string name="receive_bottom_sheet_warning_title">Send only %1$s on the %2$s network</string>
|
||||||
<string name="receive_token_description">Transfer funds from any wallet or exchange</string>
|
<string name="receive_token_description">Transfer funds from any wallet or exchange</string>
|
||||||
|
<string name="referral_address_for_rewards">Address for rewards</string>
|
||||||
<string name="referral_button_participate">Participate</string>
|
<string name="referral_button_participate">Participate</string>
|
||||||
<string name="referral_error_failed_to_load_info">Failed to load the information about the referral program. Please try again later.</string>
|
<string name="referral_error_failed_to_load_info">Failed to load the information about the referral program. Please try again later.</string>
|
||||||
<string name="referral_error_failed_to_load_info_with_reason">Failed to load the information about the referral program. Error code: %s. Please try again later.</string>
|
<string name="referral_error_failed_to_load_info_with_reason">Failed to load the information about the referral program. Error code: %s. Please try again later.</string>
|
||||||
|
|
@ -1237,6 +1242,7 @@
|
||||||
<string name="story_web3_title">Web 3.0 Compatible</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_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>
|
<string name="sui_not_enough_coin_for_fee_title">Insufficient funds</string>
|
||||||
|
<string name="swap_approve_description">By approving, you allow the smart contract to use your tokens in future transactions.</string>
|
||||||
<string name="swap_fixed_rate">Fixed Rate</string>
|
<string name="swap_fixed_rate">Fixed Rate</string>
|
||||||
<string name="swap_give_permission_fee_footer">The network will charge a token approval fee to verify that you are authorizing the use of your token for the swap.</string>
|
<string name="swap_give_permission_fee_footer">The network will charge a token approval fee to verify that you are authorizing the use of your token for the swap.</string>
|
||||||
<string name="swap_promo_text">Exchange more tokens at better rates directly in your wallet.</string>
|
<string name="swap_promo_text">Exchange more tokens at better rates directly in your wallet.</string>
|
||||||
|
|
@ -1455,8 +1461,8 @@
|
||||||
<string name="wallet_connect_error_wrong_card_selected">Wrong card or ring selected in Tangem App</string>
|
<string name="wallet_connect_error_wrong_card_selected">Wrong card or ring selected in Tangem App</string>
|
||||||
<string name="wallet_connect_failed_to_build_tx">Failed to create transaction from Dapp data. Code: %s</string>
|
<string name="wallet_connect_failed_to_build_tx">Failed to create transaction from Dapp data. Code: %s</string>
|
||||||
<string name="wallet_connect_generic_error_with_code">We\'ve encountered unknown error. Error code: %d. If the problem persists — feel free to contact our support</string>
|
<string name="wallet_connect_generic_error_with_code">We\'ve encountered unknown error. Error code: %d. If the problem persists — feel free to contact our support</string>
|
||||||
<string name="wallet_connect_multiple_transactions">Multiple transactions</string>
|
<string name="wallet_connect_multiple_transactions">Multi-Part Transaction</string>
|
||||||
<string name="wallet_connect_multiple_transactions_description">You’ll need to tap your Tangem device a few times to complete this process.</string>
|
<string name="wallet_connect_multiple_transactions_description">To process successfully, your transaction will be split into multiple parts. You\'ll need to tap your card several times to complete it.</string>
|
||||||
<string name="wallet_connect_no_sessions_message">No opened WalletConnect sessions</string>
|
<string name="wallet_connect_no_sessions_message">No opened WalletConnect sessions</string>
|
||||||
<string name="wallet_connect_no_sessions_title">Ooops. No Sessions.</string>
|
<string name="wallet_connect_no_sessions_title">Ooops. No Sessions.</string>
|
||||||
<string name="wallet_connect_pairing_error">Failed to pairing WalletConnect session: %1$s</string>
|
<string name="wallet_connect_pairing_error">Failed to pairing WalletConnect session: %1$s</string>
|
||||||
|
|
@ -1468,8 +1474,8 @@
|
||||||
<string name="wallet_connect_scanner_error_not_valid_card">This card can\'t be used to establish WalletConnect session</string>
|
<string name="wallet_connect_scanner_error_not_valid_card">This card can\'t be used to establish WalletConnect session</string>
|
||||||
<string name="wallet_connect_scanner_error_unsupported_network">This network is not supported. Please select another network.</string>
|
<string name="wallet_connect_scanner_error_unsupported_network">This network is not supported. Please select another network.</string>
|
||||||
<string name="wallet_connect_select_network">Select network</string>
|
<string name="wallet_connect_select_network">Select network</string>
|
||||||
<string name="wallet_connect_sending_multiple_explanation">We\'re processing the transaction</string>
|
<string name="wallet_connect_sending_multiple_explanation">The transaction is being processed. Please tap your card multiple times to complete it.</string>
|
||||||
<string name="wallet_connect_sending_multiple_tx">Sending your funds...</string>
|
<string name="wallet_connect_sending_multiple_tx">Transaction in progress</string>
|
||||||
<string name="wallet_connect_sessions_title">WalletConnect Sessions</string>
|
<string name="wallet_connect_sessions_title">WalletConnect Sessions</string>
|
||||||
<string name="wallet_connect_subtitle">Connect to dApps</string>
|
<string name="wallet_connect_subtitle">Connect to dApps</string>
|
||||||
<string name="wallet_connect_title">WalletConnect</string>
|
<string name="wallet_connect_title">WalletConnect</string>
|
||||||
|
|
@ -1725,7 +1731,7 @@
|
||||||
<string name="yield_module_approve_sheet_title">Confirm approval</string>
|
<string name="yield_module_approve_sheet_title">Confirm approval</string>
|
||||||
<string name="yield_module_balance_info_sheet_subtitle">Text about your money in balance [PLACEHOLDER]</string>
|
<string name="yield_module_balance_info_sheet_subtitle">Text about your money in balance [PLACEHOLDER]</string>
|
||||||
<string name="yield_module_balance_info_sheet_title">Your %s is deposited in Aave</string>
|
<string name="yield_module_balance_info_sheet_title">Your %s is deposited in Aave</string>
|
||||||
<string name="yield_module_earn_badge">Earn %s%</string>
|
<string name="yield_module_earn_badge">Earn %1$s%%</string>
|
||||||
<string name="yield_module_earn_sheet_available_title">Available</string>
|
<string name="yield_module_earn_sheet_available_title">Available</string>
|
||||||
<string name="yield_module_earn_sheet_current_apy_title">Current APY</string>
|
<string name="yield_module_earn_sheet_current_apy_title">Current APY</string>
|
||||||
<string name="yield_module_earn_sheet_my_funds_title">My Funds</string>
|
<string name="yield_module_earn_sheet_my_funds_title">My Funds</string>
|
||||||
|
|
@ -1742,6 +1748,8 @@
|
||||||
<string name="yield_module_fee_policy_sheet_title">Fee policy</string>
|
<string name="yield_module_fee_policy_sheet_title">Fee policy</string>
|
||||||
<string name="yield_module_main_view_approve_notification_description">Write description here. In one, two or three lines will be awesome. [PLACEHOLDER]</string>
|
<string name="yield_module_main_view_approve_notification_description">Write description here. In one, two or three lines will be awesome. [PLACEHOLDER]</string>
|
||||||
<string name="yield_module_main_view_approve_notification_title">Some token approve needed</string>
|
<string name="yield_module_main_view_approve_notification_title">Some token approve needed</string>
|
||||||
|
<string name="yield_module_network_fee_unreachable_notification_description">Check your network connection</string>
|
||||||
|
<string name="yield_module_network_fee_unreachable_notification_title">Network fee info unreachable</string>
|
||||||
<string name="yield_module_promo_screen_auto_balance_subtitle">Every top-up of your account will be lended to Aave automatically.</string>
|
<string name="yield_module_promo_screen_auto_balance_subtitle">Every top-up of your account will be lended to Aave automatically.</string>
|
||||||
<string name="yield_module_promo_screen_auto_balance_title">Your balance works automatically</string>
|
<string name="yield_module_promo_screen_auto_balance_title">Your balance works automatically</string>
|
||||||
<string name="yield_module_promo_screen_cash_out_subtitle">Send, swap, or sell your funds instantly, anytime you want.</string>
|
<string name="yield_module_promo_screen_cash_out_subtitle">Send, swap, or sell your funds instantly, anytime you want.</string>
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import com.tangem.data.walletconnect.utils.BlockAidVerificationDelegate
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.transaction.usecase.PrepareAndSignUseCase
|
import com.tangem.domain.transaction.usecase.PrepareAndSignUseCase
|
||||||
import com.tangem.domain.transaction.usecase.SendLargeSolanaTransactionUseCase
|
import com.tangem.domain.transaction.usecase.SendLargeSolanaTransactionUseCase
|
||||||
|
import com.tangem.domain.walletconnect.WcAnalyticEvents.SolanaLargeTransactionStatus
|
||||||
import com.tangem.domain.walletconnect.error.parseSendError
|
import com.tangem.domain.walletconnect.error.parseSendError
|
||||||
import com.tangem.domain.walletconnect.model.WcSolanaMethod
|
import com.tangem.domain.walletconnect.model.WcSolanaMethod
|
||||||
import com.tangem.domain.walletconnect.usecase.method.BlockAidTransactionCheck
|
import com.tangem.domain.walletconnect.usecase.method.BlockAidTransactionCheck
|
||||||
|
|
@ -59,10 +60,12 @@ internal class WcSolanaSignTransactionUseCase @AssistedInject constructor(
|
||||||
sendLargeSolanaTransactionUseCase(context.session.wallet as UserWallet.Cold, context.network, formattedHash)
|
sendLargeSolanaTransactionUseCase(context.session.wallet as UserWallet.Cold, context.network, formattedHash)
|
||||||
.fold(
|
.fold(
|
||||||
ifLeft = {
|
ifLeft = {
|
||||||
|
analytics.send(SolanaLargeTransactionStatus(SolanaLargeTransactionStatus.Status.Failed))
|
||||||
Timber.e(it.toString())
|
Timber.e(it.toString())
|
||||||
emit(state.toResult(parseSendError(it).left()))
|
emit(state.toResult(parseSendError(it).left()))
|
||||||
},
|
},
|
||||||
ifRight = {
|
ifRight = {
|
||||||
|
analytics.send(SolanaLargeTransactionStatus(SolanaLargeTransactionStatus.Status.Success))
|
||||||
val emptyRespond = ByteArray(0).formatAsSolanaSignature()
|
val emptyRespond = ByteArray(0).formatAsSolanaSignature()
|
||||||
val respondResult = respondService.respond(rawSdkRequest, emptyRespond)
|
val respondResult = respondService.respond(rawSdkRequest, emptyRespond)
|
||||||
emit(state.toResult(respondResult))
|
emit(state.toResult(respondResult))
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,10 @@ sealed interface StakingIntegrationID {
|
||||||
|
|
||||||
val integrationId = blockchain.integrationId ?: return null
|
val integrationId = blockchain.integrationId ?: return null
|
||||||
|
|
||||||
|
if (integrationId is Coin && currencyId.contractAddress != null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
return when (integrationId) {
|
return when (integrationId) {
|
||||||
is Coin -> integrationId
|
is Coin -> integrationId
|
||||||
is EthereumToken -> {
|
is EthereumToken -> {
|
||||||
|
|
|
||||||
|
|
@ -139,9 +139,13 @@ class StakingIntegrationIDTest {
|
||||||
expected = StakingIntegrationID.Coin.Cardano,
|
expected = StakingIntegrationID.Coin.Cardano,
|
||||||
),
|
),
|
||||||
CreateModel(
|
CreateModel(
|
||||||
currencyId = CryptoCurrency.ID.fromValue(value = "coin⟨ETH⟩polygon-ecosystem-token⚓"),
|
currencyId = CryptoCurrency.ID.fromValue(value = "token⟨ETH⟩polygon-ecosystem-token⚓1234567890"),
|
||||||
expected = StakingIntegrationID.EthereumToken.Polygon,
|
expected = StakingIntegrationID.EthereumToken.Polygon,
|
||||||
),
|
),
|
||||||
|
CreateModel(
|
||||||
|
currencyId = CryptoCurrency.ID.fromValue(value = "token⟨SOLANA⟩solana⚓1234567890"),
|
||||||
|
expected = null,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -236,6 +236,29 @@ sealed class WcAnalyticEvents(
|
||||||
enum class Source { Domain, SmartContract }
|
enum class Source { Domain, SmartContract }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class SolanaLargeTransaction(
|
||||||
|
val dappName: String,
|
||||||
|
) : WcAnalyticEvents(
|
||||||
|
event = "Solana Large Transaction",
|
||||||
|
params = mapOf(
|
||||||
|
AnalyticsParam.Key.DAPP_NAME to dappName,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SolanaLargeTransactionStatus(
|
||||||
|
val status: Status,
|
||||||
|
) : WcAnalyticEvents(
|
||||||
|
event = "Solana Large Transaction Status",
|
||||||
|
params = mapOf(
|
||||||
|
AnalyticsParam.Key.STATUS to status.value,
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
enum class Status(val value: String) {
|
||||||
|
Success("Success"),
|
||||||
|
Failed("Failed"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum class DAppVerificationStatus(val status: String) {
|
enum class DAppVerificationStatus(val status: String) {
|
||||||
Verified("Verified"),
|
Verified("Verified"),
|
||||||
Risky("Risky"),
|
Risky("Risky"),
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ internal interface ConfirmResidencyComponent : ComposableBottomSheetComponent {
|
||||||
val userWalletId: UserWalletId,
|
val userWalletId: UserWalletId,
|
||||||
val cryptoCurrency: CryptoCurrency,
|
val cryptoCurrency: CryptoCurrency,
|
||||||
val country: OnrampCountry,
|
val country: OnrampCountry,
|
||||||
|
val launchSepa: Boolean,
|
||||||
val onDismiss: () -> Unit,
|
val onDismiss: () -> Unit,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,14 @@ import com.tangem.core.decompose.model.ParamsContainer
|
||||||
import com.tangem.core.decompose.navigation.Router
|
import com.tangem.core.decompose.navigation.Router
|
||||||
import com.tangem.core.ui.extensions.resourceReference
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
import com.tangem.domain.onramp.OnrampSaveDefaultCountryUseCase
|
import com.tangem.domain.onramp.OnrampSaveDefaultCountryUseCase
|
||||||
|
import com.tangem.domain.onramp.OnrampSaveDefaultCurrencyUseCase
|
||||||
import com.tangem.domain.onramp.analytics.OnrampAnalyticsEvent
|
import com.tangem.domain.onramp.analytics.OnrampAnalyticsEvent
|
||||||
import com.tangem.domain.onramp.model.OnrampCountry
|
import com.tangem.domain.onramp.model.OnrampCountry
|
||||||
import com.tangem.features.onramp.confirmresidency.ConfirmResidencyComponent
|
import com.tangem.features.onramp.confirmresidency.ConfirmResidencyComponent
|
||||||
import com.tangem.features.onramp.confirmresidency.entity.ConfirmResidencyBottomSheetConfig
|
import com.tangem.features.onramp.confirmresidency.entity.ConfirmResidencyBottomSheetConfig
|
||||||
import com.tangem.features.onramp.confirmresidency.entity.ConfirmResidencyUM
|
import com.tangem.features.onramp.confirmresidency.entity.ConfirmResidencyUM
|
||||||
import com.tangem.features.onramp.impl.R
|
import com.tangem.features.onramp.impl.R
|
||||||
|
import com.tangem.features.onramp.utils.model.EUR_CURRENCY
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -26,6 +28,7 @@ internal class ConfirmResidencyModel @Inject constructor(
|
||||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||||
private val router: Router,
|
private val router: Router,
|
||||||
private val saveDefaultCountryUseCase: OnrampSaveDefaultCountryUseCase,
|
private val saveDefaultCountryUseCase: OnrampSaveDefaultCountryUseCase,
|
||||||
|
private val onrampSaveDefaultCurrencyUseCase: OnrampSaveDefaultCurrencyUseCase,
|
||||||
paramsContainer: ParamsContainer,
|
paramsContainer: ParamsContainer,
|
||||||
) : Model() {
|
) : Model() {
|
||||||
|
|
||||||
|
|
@ -54,6 +57,10 @@ internal class ConfirmResidencyModel @Inject constructor(
|
||||||
analyticsEventHandler.send(OnrampAnalyticsEvent.OnResidenceConfirm(country.name))
|
analyticsEventHandler.send(OnrampAnalyticsEvent.OnResidenceConfirm(country.name))
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
saveDefaultCountryUseCase.invoke(country)
|
saveDefaultCountryUseCase.invoke(country)
|
||||||
|
if (params.launchSepa) {
|
||||||
|
onrampSaveDefaultCurrencyUseCase.invoke(EUR_CURRENCY)
|
||||||
|
}
|
||||||
|
|
||||||
params.onDismiss()
|
params.onDismiss()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ internal class DefaultOnrampMainComponent @AssistedInject constructor(
|
||||||
userWalletId = params.userWalletId,
|
userWalletId = params.userWalletId,
|
||||||
cryptoCurrency = params.cryptoCurrency,
|
cryptoCurrency = params.cryptoCurrency,
|
||||||
country = config.country,
|
country = config.country,
|
||||||
|
launchSepa = params.launchSepa,
|
||||||
onDismiss = {
|
onDismiss = {
|
||||||
model.bottomSheetNavigation.dismiss()
|
model.bottomSheetNavigation.dismiss()
|
||||||
model.handleOnrampAvailable()
|
model.handleOnrampAvailable()
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package com.tangem.features.onramp.main.entity
|
||||||
import com.tangem.domain.onramp.model.OnrampProviderWithQuote
|
import com.tangem.domain.onramp.model.OnrampProviderWithQuote
|
||||||
|
|
||||||
interface OnrampIntents {
|
interface OnrampIntents {
|
||||||
fun onAmountValueChanged(value: String)
|
fun onAmountValueChanged(value: String, isValuePasted: Boolean)
|
||||||
fun openSettings()
|
fun openSettings()
|
||||||
fun openCurrenciesList()
|
fun openCurrenciesList()
|
||||||
fun onBuyClick(quote: OnrampProviderWithQuote.Data)
|
fun onBuyClick(quote: OnrampProviderWithQuote.Data)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
package com.tangem.features.onramp.main.entity
|
package com.tangem.features.onramp.main.entity
|
||||||
|
|
||||||
import com.tangem.domain.onramp.model.OnrampAmount
|
import com.tangem.domain.onramp.model.OnrampAmount
|
||||||
|
import com.tangem.domain.onramp.model.OnrampPaymentMethod
|
||||||
|
|
||||||
data class OnrampLastUpdate(
|
data class OnrampLastUpdate(
|
||||||
val lastAmount: OnrampAmount,
|
val fromAmount: OnrampAmount,
|
||||||
val lastCountryString: String,
|
val countryCode: String,
|
||||||
|
val paymentMethod: OnrampPaymentMethod,
|
||||||
)
|
)
|
||||||
|
|
@ -122,7 +122,7 @@ internal class OnrampStateFactory(
|
||||||
amountFieldModel = AmountFieldModel(
|
amountFieldModel = AmountFieldModel(
|
||||||
value = "",
|
value = "",
|
||||||
fiatValue = "",
|
fiatValue = "",
|
||||||
onValueChange = onrampIntents::onAmountValueChanged,
|
onValueChange = { onrampIntents.onAmountValueChanged(value = it, isValuePasted = false) },
|
||||||
keyboardOptions = KeyboardOptions(
|
keyboardOptions = KeyboardOptions(
|
||||||
imeAction = ImeAction.None,
|
imeAction = ImeAction.None,
|
||||||
keyboardType = KeyboardType.Number,
|
keyboardType = KeyboardType.Number,
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,12 @@ import java.math.BigDecimal
|
||||||
|
|
||||||
internal class OnrampAmountFieldChangeConverter(
|
internal class OnrampAmountFieldChangeConverter(
|
||||||
private val currentStateProvider: Provider<OnrampMainComponentUM>,
|
private val currentStateProvider: Provider<OnrampMainComponentUM>,
|
||||||
) : Converter<String, OnrampMainComponentUM> {
|
) : Converter<OnrampAmountFieldChangeConverter.Input, OnrampMainComponentUM> {
|
||||||
|
|
||||||
|
override fun convert(input: Input): OnrampMainComponentUM {
|
||||||
|
val value = input.value
|
||||||
|
val isValuePasted = input.isValuePasted
|
||||||
|
|
||||||
override fun convert(value: String): OnrampMainComponentUM {
|
|
||||||
val state = currentStateProvider()
|
val state = currentStateProvider()
|
||||||
if (state !is OnrampMainComponentUM.Content) return state
|
if (state !is OnrampMainComponentUM.Content) return state
|
||||||
|
|
||||||
|
|
@ -34,6 +37,7 @@ internal class OnrampAmountFieldChangeConverter(
|
||||||
imeAction = if (isDoneActionEnabled) ImeAction.Done else ImeAction.None,
|
imeAction = if (isDoneActionEnabled) ImeAction.Done else ImeAction.None,
|
||||||
keyboardType = KeyboardType.Number,
|
keyboardType = KeyboardType.Number,
|
||||||
),
|
),
|
||||||
|
isValuePasted = isValuePasted,
|
||||||
)
|
)
|
||||||
|
|
||||||
return state.copy(
|
return state.copy(
|
||||||
|
|
@ -66,4 +70,6 @@ internal class OnrampAmountFieldChangeConverter(
|
||||||
providerBlockState = OnrampProviderBlockUM.Empty,
|
providerBlockState = OnrampProviderBlockUM.Empty,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class Input(val value: String, val isValuePasted: Boolean)
|
||||||
}
|
}
|
||||||
|
|
@ -33,7 +33,8 @@ internal class OnrampAmountStateFactory(
|
||||||
currentStateProvider = currentStateProvider,
|
currentStateProvider = currentStateProvider,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun getOnAmountValueChange(value: String) = onrampAmountFieldChangeConverter.convert(value)
|
fun getOnAmountValueChange(value: String, isValuePasted: Boolean) =
|
||||||
|
onrampAmountFieldChangeConverter.convert(OnrampAmountFieldChangeConverter.Input(value, isValuePasted))
|
||||||
|
|
||||||
fun getUpdatedCurrencyState(currency: OnrampCurrency): OnrampMainComponentUM {
|
fun getUpdatedCurrencyState(currency: OnrampCurrency): OnrampMainComponentUM {
|
||||||
val currentState = currentStateProvider()
|
val currentState = currentStateProvider()
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import com.tangem.features.onramp.main.entity.factory.OnrampStateFactory
|
||||||
import com.tangem.features.onramp.main.entity.factory.OnrampStateFactory.Companion.PREDEFINED_SEPA_AMOUNT
|
import com.tangem.features.onramp.main.entity.factory.OnrampStateFactory.Companion.PREDEFINED_SEPA_AMOUNT
|
||||||
import com.tangem.features.onramp.main.entity.factory.amount.OnrampAmountStateFactory
|
import com.tangem.features.onramp.main.entity.factory.amount.OnrampAmountStateFactory
|
||||||
import com.tangem.features.onramp.providers.entity.SelectProviderResult
|
import com.tangem.features.onramp.providers.entity.SelectProviderResult
|
||||||
|
import com.tangem.features.onramp.utils.model.EUR_CURRENCY
|
||||||
import com.tangem.features.onramp.utils.sendOnrampErrorEvent
|
import com.tangem.features.onramp.utils.sendOnrampErrorEvent
|
||||||
import com.tangem.utils.Provider
|
import com.tangem.utils.Provider
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
|
@ -68,7 +69,7 @@ internal class OnrampMainComponentModel @Inject constructor(
|
||||||
|
|
||||||
private val params: OnrampMainComponent.Params = paramsContainer.require()
|
private val params: OnrampMainComponent.Params = paramsContainer.require()
|
||||||
|
|
||||||
private var isSepaLaunched = false
|
private var shouldForceChooseSepa = params.launchSepa
|
||||||
private var currencyToRestore: OnrampCurrency? = null
|
private var currencyToRestore: OnrampCurrency? = null
|
||||||
|
|
||||||
val userWallet = getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId }
|
val userWallet = getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId }
|
||||||
|
|
@ -132,6 +133,10 @@ internal class OnrampMainComponentModel @Inject constructor(
|
||||||
|
|
||||||
fun onProviderSelected(result: SelectProviderResult, isBestRate: Boolean) {
|
fun onProviderSelected(result: SelectProviderResult, isBestRate: Boolean) {
|
||||||
_state.update { amountStateFactory.getAmountSecondaryUpdatedState(result, isBestRate) }
|
_state.update { amountStateFactory.getAmountSecondaryUpdatedState(result, isBestRate) }
|
||||||
|
|
||||||
|
if (result.paymentMethod.id != SEPA_METHOD_ID) {
|
||||||
|
shouldForceChooseSepa = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkResidenceCountry() {
|
private fun checkResidenceCountry() {
|
||||||
|
|
@ -171,7 +176,7 @@ internal class OnrampMainComponentModel @Inject constructor(
|
||||||
updatePairsAndQuotes()
|
updatePairsAndQuotes()
|
||||||
|
|
||||||
if (wasInitialLoading && params.launchSepa) {
|
if (wasInitialLoading && params.launchSepa) {
|
||||||
onAmountValueChanged(PREDEFINED_SEPA_AMOUNT)
|
onAmountValueChanged(value = PREDEFINED_SEPA_AMOUNT, isValuePasted = true)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -243,8 +248,8 @@ internal class OnrampMainComponentModel @Inject constructor(
|
||||||
.launchIn(modelScope)
|
.launchIn(modelScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAmountValueChanged(value: String) {
|
override fun onAmountValueChanged(value: String, isValuePasted: Boolean) {
|
||||||
_state.update { amountStateFactory.getOnAmountValueChange(value) }
|
_state.update { amountStateFactory.getOnAmountValueChange(value, isValuePasted) }
|
||||||
modelScope.launch { amountInputManager.update(value) }
|
modelScope.launch { amountInputManager.update(value) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -341,9 +346,7 @@ internal class OnrampMainComponentModel @Inject constructor(
|
||||||
private fun selectOrUpdateQuote(quotes: List<OnrampQuote>): OnrampQuote? {
|
private fun selectOrUpdateQuote(quotes: List<OnrampQuote>): OnrampQuote? {
|
||||||
val quoteToCheck = quotes.firstOrNull { it !is OnrampQuote.Error }
|
val quoteToCheck = quotes.firstOrNull { it !is OnrampQuote.Error }
|
||||||
|
|
||||||
val sepaQuote = if (params.launchSepa && !isSepaLaunched) {
|
val bestSepaQuote = if (params.launchSepa && shouldForceChooseSepa) {
|
||||||
isSepaLaunched = true
|
|
||||||
|
|
||||||
quotes.filterIsInstance<OnrampQuote.Data>()
|
quotes.filterIsInstance<OnrampQuote.Data>()
|
||||||
.filter { it.paymentMethod.id == SEPA_METHOD_ID }
|
.filter { it.paymentMethod.id == SEPA_METHOD_ID }
|
||||||
.maxByOrNull { it.toAmount.value }
|
.maxByOrNull { it.toAmount.value }
|
||||||
|
|
@ -352,7 +355,7 @@ internal class OnrampMainComponentModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if amount, country or currency has changed
|
// Check if amount, country or currency has changed
|
||||||
val newQuote = sepaQuote ?: if (checkLastInputState(quoteToCheck)) {
|
val newQuote = bestSepaQuote ?: if (isAmountOrCountryChanged(quoteToCheck)) {
|
||||||
quoteToCheck
|
quoteToCheck
|
||||||
} else {
|
} else {
|
||||||
val state = state.value as? OnrampMainComponentUM.Content
|
val state = state.value as? OnrampMainComponentUM.Content
|
||||||
|
|
@ -371,7 +374,9 @@ internal class OnrampMainComponentModel @Inject constructor(
|
||||||
lastSelectedQuote
|
lastSelectedQuote
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
newQuote?.let { updateProvider(newQuote, quotes) }
|
if (newQuote != null) {
|
||||||
|
updateProvider(newQuote, quotes)
|
||||||
|
}
|
||||||
|
|
||||||
return newQuote
|
return newQuote
|
||||||
}
|
}
|
||||||
|
|
@ -380,8 +385,13 @@ internal class OnrampMainComponentModel @Inject constructor(
|
||||||
lastUpdateState.value = OnrampLastUpdate(
|
lastUpdateState.value = OnrampLastUpdate(
|
||||||
quote.fromAmount,
|
quote.fromAmount,
|
||||||
quote.countryCode,
|
quote.countryCode,
|
||||||
|
quote.paymentMethod,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (quote.paymentMethod.id != SEPA_METHOD_ID) {
|
||||||
|
shouldForceChooseSepa = false
|
||||||
|
}
|
||||||
|
|
||||||
_state.update {
|
_state.update {
|
||||||
amountStateFactory.getUpdatedProviderState(selectedQuote = quote, quotes = quotes)
|
amountStateFactory.getUpdatedProviderState(selectedQuote = quote, quotes = quotes)
|
||||||
}
|
}
|
||||||
|
|
@ -447,22 +457,14 @@ internal class OnrampMainComponentModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkLastInputState(quote: OnrampQuote?): Boolean {
|
private fun isAmountOrCountryChanged(quote: OnrampQuote?): Boolean {
|
||||||
return lastUpdateState.value?.lastAmount != quote?.fromAmount ||
|
return lastUpdateState.value?.fromAmount != quote?.fromAmount ||
|
||||||
lastUpdateState.value?.lastCountryString != quote?.countryCode
|
lastUpdateState.value?.countryCode != quote?.countryCode
|
||||||
}
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
const val UPDATE_DELAY = 10_000L
|
const val UPDATE_DELAY = 10_000L
|
||||||
|
|
||||||
const val SEPA_METHOD_ID = "sepa"
|
const val SEPA_METHOD_ID = "sepa"
|
||||||
|
|
||||||
val EUR_CURRENCY = OnrampCurrency(
|
|
||||||
code = "EUR",
|
|
||||||
name = "Euro",
|
|
||||||
unit = "€",
|
|
||||||
precision = 2,
|
|
||||||
image = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/Currencies/EUR.png",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -58,6 +58,7 @@ internal class DefaultOnrampV2MainComponent @AssistedInject constructor(
|
||||||
userWalletId = params.userWalletId,
|
userWalletId = params.userWalletId,
|
||||||
cryptoCurrency = params.cryptoCurrency,
|
cryptoCurrency = params.cryptoCurrency,
|
||||||
country = config.country,
|
country = config.country,
|
||||||
|
launchSepa = false,
|
||||||
onDismiss = { model.bottomSheetNavigation.dismiss() },
|
onDismiss = { model.bottomSheetNavigation.dismiss() },
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -348,8 +348,9 @@ internal class OnrampV2MainComponentModel @Inject constructor(
|
||||||
|
|
||||||
private fun updateProvider(quote: OnrampQuote) {
|
private fun updateProvider(quote: OnrampQuote) {
|
||||||
lastUpdateState.value = OnrampLastUpdate(
|
lastUpdateState.value = OnrampLastUpdate(
|
||||||
quote.fromAmount,
|
fromAmount = quote.fromAmount,
|
||||||
quote.countryCode,
|
countryCode = quote.countryCode,
|
||||||
|
paymentMethod = quote.paymentMethod,
|
||||||
)
|
)
|
||||||
|
|
||||||
_state.update {
|
_state.update {
|
||||||
|
|
@ -378,8 +379,8 @@ internal class OnrampV2MainComponentModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkLastInputState(quote: OnrampQuote?): Boolean {
|
private fun checkLastInputState(quote: OnrampQuote?): Boolean {
|
||||||
return lastUpdateState.value?.lastAmount != quote?.fromAmount ||
|
return lastUpdateState.value?.fromAmount != quote?.fromAmount ||
|
||||||
lastUpdateState.value?.lastCountryString != quote?.countryCode
|
lastUpdateState.value?.countryCode != quote?.countryCode
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sendScreenOpenAnalytics() {
|
private fun sendScreenOpenAnalytics() {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.tangem.features.onramp.utils.model
|
||||||
|
|
||||||
|
import com.tangem.domain.onramp.model.OnrampCurrency
|
||||||
|
|
||||||
|
internal val EUR_CURRENCY = OnrampCurrency(
|
||||||
|
code = "EUR",
|
||||||
|
name = "Euro",
|
||||||
|
unit = "€",
|
||||||
|
precision = 2,
|
||||||
|
image = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/Currencies/EUR.png",
|
||||||
|
)
|
||||||
|
|
@ -510,7 +510,7 @@ internal class SendConfirmModel @Inject constructor(
|
||||||
val txUrl = getExplorerTransactionUrlUseCase(
|
val txUrl = getExplorerTransactionUrlUseCase(
|
||||||
txHash = txData.hash.orEmpty(),
|
txHash = txData.hash.orEmpty(),
|
||||||
networkId = cryptoCurrency.network.id,
|
networkId = cryptoCurrency.network.id,
|
||||||
).getOrElse { "" }
|
).getOrNull().orEmpty()
|
||||||
_uiState.update(SendConfirmSentStateTransformer(txData, txUrl))
|
_uiState.update(SendConfirmSentStateTransformer(txData, txUrl))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ internal class SendConfirmSuccessModel @Inject constructor(
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
prevButton = null,
|
prevButton = null,
|
||||||
secondaryPairButtonsUM = NavigationButton(
|
secondaryPairButtonsUM = (NavigationButton(
|
||||||
textReference = resourceReference(R.string.common_explore),
|
textReference = resourceReference(R.string.common_explore),
|
||||||
iconRes = R.drawable.ic_web_24,
|
iconRes = R.drawable.ic_web_24,
|
||||||
onClick = ::onExploreClick,
|
onClick = ::onExploreClick,
|
||||||
|
|
@ -84,7 +84,7 @@ internal class SendConfirmSuccessModel @Inject constructor(
|
||||||
textReference = resourceReference(R.string.common_share),
|
textReference = resourceReference(R.string.common_share),
|
||||||
iconRes = R.drawable.ic_share_24,
|
iconRes = R.drawable.ic_share_24,
|
||||||
onClick = ::onShareClick,
|
onClick = ::onShareClick,
|
||||||
),
|
)).takeIf { params.txUrl.isNotEmpty() },
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -498,7 +498,7 @@ internal class NFTSendConfirmModel @Inject constructor(
|
||||||
iconRes = R.drawable.ic_share_24,
|
iconRes = R.drawable.ic_share_24,
|
||||||
onClick = ::onShareClick,
|
onClick = ::onShareClick,
|
||||||
)
|
)
|
||||||
).takeIf { confirmUM is ConfirmUM.Success },
|
).takeUnless { (confirmUM as? ConfirmUM.Success)?.txUrl.isNullOrBlank() },
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ internal class SendWithSwapSuccessModel @Inject constructor(
|
||||||
onClick = appRouter::pop,
|
onClick = appRouter::pop,
|
||||||
),
|
),
|
||||||
prevButton = null,
|
prevButton = null,
|
||||||
secondaryPairButtonsUM = NavigationButton(
|
secondaryPairButtonsUM = (NavigationButton(
|
||||||
textReference = resourceReference(R.string.common_explore),
|
textReference = resourceReference(R.string.common_explore),
|
||||||
iconRes = R.drawable.ic_web_24,
|
iconRes = R.drawable.ic_web_24,
|
||||||
onClick = ::onExploreClick,
|
onClick = ::onExploreClick,
|
||||||
|
|
@ -60,7 +60,7 @@ internal class SendWithSwapSuccessModel @Inject constructor(
|
||||||
textReference = resourceReference(R.string.common_share),
|
textReference = resourceReference(R.string.common_share),
|
||||||
iconRes = R.drawable.ic_share_24,
|
iconRes = R.drawable.ic_share_24,
|
||||||
onClick = ::onShareClick,
|
onClick = ::onShareClick,
|
||||||
),
|
)).takeUnless { confirmUM?.txUrl.isNullOrBlank() },
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -410,7 +410,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
||||||
rateType = RateType.FLOAT,
|
rateType = RateType.FLOAT,
|
||||||
)
|
)
|
||||||
|
|
||||||
return if (isBalanceWithoutFeeEnough) {
|
return if (isBalanceWithoutFeeEnough && maybeQuotes.isRight()) {
|
||||||
provider to loadDexSwapData(
|
provider to loadDexSwapData(
|
||||||
provider = provider,
|
provider = provider,
|
||||||
networkId = networkId,
|
networkId = networkId,
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import com.tangem.domain.transaction.error.SendTransactionError.UserCancelledErr
|
||||||
import com.tangem.domain.transaction.usecase.GetFeeUseCase
|
import com.tangem.domain.transaction.usecase.GetFeeUseCase
|
||||||
import com.tangem.domain.walletconnect.WcAnalyticEvents
|
import com.tangem.domain.walletconnect.WcAnalyticEvents
|
||||||
import com.tangem.domain.walletconnect.WcAnalyticEvents.SignatureRequestReceived.EmulationStatus
|
import com.tangem.domain.walletconnect.WcAnalyticEvents.SignatureRequestReceived.EmulationStatus
|
||||||
|
import com.tangem.domain.walletconnect.WcAnalyticEvents.SolanaLargeTransaction
|
||||||
import com.tangem.domain.walletconnect.WcRequestUseCaseFactory
|
import com.tangem.domain.walletconnect.WcRequestUseCaseFactory
|
||||||
import com.tangem.domain.walletconnect.model.WcRequestError
|
import com.tangem.domain.walletconnect.model.WcRequestError
|
||||||
import com.tangem.domain.walletconnect.model.WcRequestError.Companion.message
|
import com.tangem.domain.walletconnect.model.WcRequestError.Companion.message
|
||||||
|
|
@ -143,6 +144,7 @@ internal class WcSendTransactionModel @Inject constructor(
|
||||||
wcApproval = useCase as? WcApproval
|
wcApproval = useCase as? WcApproval
|
||||||
sign = {
|
sign = {
|
||||||
if (isMultipleSignRequired(useCase)) {
|
if (isMultipleSignRequired(useCase)) {
|
||||||
|
analytics.send(SolanaLargeTransaction(useCase.rawSdkRequest.dAppMetaData.name))
|
||||||
openMultipleTransaction(useCase)
|
openMultipleTransaction(useCase)
|
||||||
} else {
|
} else {
|
||||||
useCase.sign()
|
useCase.sign()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue