From e3593c69822941bed85e9b49afdcedf53b921f65 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 2 Dec 2024 09:55:53 +0300 Subject: [PATCH 1/9] Updated on 2026-08-14 --- .../tokens/operations/CurrencyStatusOperations.kt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt index 3e4f3a77ae..41d94b2cf2 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt @@ -85,14 +85,8 @@ internal class CurrencyStatusOperations( } else { null } + // order is important for correct total balance calculation return when { - quote is Quote.Empty || ignoreQuote -> CryptoCurrencyStatus.NoQuote( - amount = amount, - hasCurrentNetworkTransactions = hasCurrentNetworkTransactions, - pendingTransactions = currentTransactions, - networkAddress = status.address, - yieldBalance = currentYieldBalance, - ) currency is CryptoCurrency.Token && currency.isCustom -> CryptoCurrencyStatus.Custom( amount = amount, fiatAmount = calculateFiatAmountOrNull(amount, quote?.fiatRate), @@ -103,6 +97,13 @@ internal class CurrencyStatusOperations( networkAddress = status.address, yieldBalance = currentYieldBalance, ) + quote is Quote.Empty || ignoreQuote -> CryptoCurrencyStatus.NoQuote( + amount = amount, + hasCurrentNetworkTransactions = hasCurrentNetworkTransactions, + pendingTransactions = currentTransactions, + networkAddress = status.address, + yieldBalance = currentYieldBalance, + ) quote is Quote.Value -> CryptoCurrencyStatus.Loaded( amount = amount, fiatAmount = calculateFiatAmount(amount, quote.fiatRate), From 877d47db98a9db177ac8b73f35d79b57dadab1d8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 2 Dec 2024 12:47:42 +0300 Subject: [PATCH 2/9] Updated on 2026-08-14 --- Gemfile.lock | 2 +- .../tokens/GetCryptoCurrencyActionsUseCase.kt | 21 +++++++++---- .../CurrenciesStatusesOperations.kt | 30 ++++++++++++------- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index b4221a320d..535cdb9a32 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -217,4 +217,4 @@ DEPENDENCIES fastlane-plugin-firebase_app_distribution BUNDLED WITH - 1.17.2 + 2.5.23 diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt index ad321616d3..e6981db812 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt @@ -17,6 +17,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.isNullOrZero import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.* +import kotlinx.coroutines.withTimeoutOrNull /** * Use case to determine which TokenActions are available for a [CryptoCurrency] @@ -54,9 +55,13 @@ class GetCryptoCurrencyActionsUseCase( val networkFlow = if (userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()) { operations.getNetworkCoinForSingleWalletWithTokenFlow(networkId) } else if (!userWallet.isMultiCurrency) { - operations.getPrimaryCurrencyStatusFlow() + operations.getPrimaryCurrencyStatusFlow(includeQuotes = false) } else { - operations.getNetworkCoinFlow(networkId, cryptoCurrencyStatus.currency.network.derivationPath) + operations.getNetworkCoinFlow( + networkId = networkId, + derivationPath = cryptoCurrencyStatus.currency.network.derivationPath, + includeQuotes = false, + ) } val flow = networkFlow.mapLatest { maybeCoinStatus -> @@ -171,10 +176,10 @@ class GetCryptoCurrencyActionsUseCase( // swap if (userWallet.isMultiCurrency) { - if ( - marketCryptoCurrencyRepository.isExchangeable(userWallet.walletId, cryptoCurrency) && - cryptoCurrencyStatus.value !is CryptoCurrencyStatus.NoQuote - ) { + val isExchangeable = withTimeoutOrNull(REQUEST_EXCHANGE_DATA_TIMEOUT) { + marketCryptoCurrencyRepository.isExchangeable(userWallet.walletId, cryptoCurrency) + } ?: false + if (isExchangeable && cryptoCurrencyStatus.value !is CryptoCurrencyStatus.NoQuote) { activeList.add(TokenActionsState.ActionState.Swap(ScenarioUnavailabilityReason.None)) } else { disabledList.add( @@ -312,4 +317,8 @@ class GetCryptoCurrencyActionsUseCase( cryptoCurrency = cryptoCurrency, ) is StakingAvailability.Available } + + private companion object { + const val REQUEST_EXCHANGE_DATA_TIMEOUT = 1000L + } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt index 639ccd6991..9a4dcf3728 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt @@ -143,13 +143,14 @@ internal class CurrenciesStatusesOperations( suspend fun getNetworkCoinFlow( networkId: Network.ID, derivationPath: Network.DerivationPath, + includeQuotes: Boolean = true, ): Flow> { val currency = recover( block = { getNetworkCoin(networkId, derivationPath) }, recover = { return flowOf(it.left()) }, ) - return getCurrencyStatusFlow(currency) + return getCurrencyStatusFlow(currency, includeQuotes) } suspend fun getNetworkCoinForSingleWalletWithTokenFlow( @@ -163,25 +164,34 @@ internal class CurrenciesStatusesOperations( return getCurrencyStatusFlow(currency) } - suspend fun getPrimaryCurrencyStatusFlow(): Flow> { + suspend fun getPrimaryCurrencyStatusFlow(includeQuotes: Boolean = true): Flow> { val currency = recover( block = { getPrimaryCurrency() }, recover = { return flowOf(it.left()) }, ) - return getCurrencyStatusFlow(currency) + return getCurrencyStatusFlow(currency, includeQuotes) } - fun getCurrencyStatusFlow(currency: CryptoCurrency): Flow> { + fun getCurrencyStatusFlow( + currency: CryptoCurrency, + includeQuotes: Boolean = true, + ): Flow,> { val (networks, currenciesIds) = getIds(nonEmptyListOf(currency)) - val quoteFlow = getQuotes(currenciesIds) - .map { maybeQuotes -> - maybeQuotes.flatMap { quotes -> - quotes.singleOrNull { it.rawCurrencyId == currency.id.rawCurrencyId }?.right() - ?: Error.EmptyQuotes.left() + val quoteFlow = if (includeQuotes) { + getQuotes(currenciesIds) + .map { maybeQuotes -> + maybeQuotes.flatMap { quotes -> + quotes.singleOrNull { it.rawCurrencyId == currency.id.rawCurrencyId }?.right() + ?: Error.EmptyQuotes.left() + } } - } + } else { + // don't use emptyFlow() + flow { emit(Error.EmptyQuotes.left()) } + } val statusFlow = getNetworksStatuses(networks) .map { maybeStatuses -> From 3b946e45bb608f99146006b6599e0b1dc6fe12de Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 3 Dec 2024 10:32:43 +0300 Subject: [PATCH 3/9] Updated on 2026-08-14 --- .../tokens/GetCryptoCurrencyActionsUseCase.kt | 4 +++- .../viewmodels/TokenDetailsViewModel.kt | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt index e6981db812..78c3879acb 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt @@ -50,7 +50,9 @@ class GetCryptoCurrencyActionsUseCase( userWalletId = userWallet.walletId, ) val networkId = cryptoCurrencyStatus.currency.network.id - val requirements = walletManagersFacade.getAssetRequirements(userWallet.walletId, cryptoCurrencyStatus.currency) + val requirements = withTimeoutOrNull(REQUEST_EXCHANGE_DATA_TIMEOUT) { + walletManagersFacade.getAssetRequirements(userWallet.walletId, cryptoCurrencyStatus.currency) + } return flow { val networkFlow = if (userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()) { operations.getNetworkCoinForSingleWalletWithTokenFlow(networkId) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index 8130b96926..74f0f3418f 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -124,6 +124,7 @@ internal class TokenDetailsViewModel @Inject constructor( private val analyticsEventsHandler: AnalyticsEventHandler, private val vibratorHapticManager: VibratorHapticManager, private val clipboardManager: ClipboardManager, + private val getCryptoCurrencySyncUseCase: GetCryptoCurrencyStatusSyncUseCase, getUserWalletUseCase: GetUserWalletUseCase, getStakingIntegrationIdUseCase: GetStakingIntegrationIdUseCase, deepLinksRegistry: DeepLinksRegistry, @@ -223,6 +224,7 @@ internal class TokenDetailsViewModel @Inject constructor( event = TokenScreenAnalyticsEvent.DetailsScreenOpened(token = cryptoCurrency.symbol), ) updateTopBarMenu() + initButtons() updateContent() handleBalanceHiding(owner) } @@ -232,6 +234,20 @@ internal class TokenDetailsViewModel @Inject constructor( super.onCleared() } + private fun initButtons() { + // we need also init buttons before start all loading to avoid buttons blocking + viewModelScope.launch { + val currentCryptoCurrencyStatus = getCryptoCurrencySyncUseCase.invoke( + userWalletId = userWalletId, + cryptoCurrencyId = cryptoCurrency.id, + isSingleWalletWithTokens = false, + ).getOrNull() + currentCryptoCurrencyStatus?.let { + updateButtons(it) + } + } + } + private fun updateContent() { subscribeOnCurrencyStatusUpdates() subscribeOnExchangeTransactionsUpdates() From 83ceee67869c2a58d78496c3a4cf5a1763e8ea7f Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 3 Dec 2024 12:09:32 +0300 Subject: [PATCH 4/9] Updated on 2026-08-14 --- .../tokendetails/viewmodels/TokenDetailsViewModel.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index 74f0f3418f..9f3a1887f4 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -243,6 +243,7 @@ internal class TokenDetailsViewModel @Inject constructor( isSingleWalletWithTokens = false, ).getOrNull() currentCryptoCurrencyStatus?.let { + cryptoCurrencyStatus = it updateButtons(it) } } From 44209d7db1dc108fa0f407ced3a710d99f373510 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 3 Dec 2024 17:02:36 +0300 Subject: [PATCH 5/9] Updated on 2026-08-14 --- fastlane/Fastfile | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index a411e3c160..edd1284284 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -42,13 +42,7 @@ platform :android do desc "Build external and release APKs" lane :build do |options| - gradle( - task: "clean assemble", - build_type: "External", - properties: { - 'versionCode' => options[:versionCode], - 'versionName' => options[:versionName], - }) + gradle(task: 'clean') gradle( task: "bundle", build_type: "Release", From 1827ba42d81839045051ebc0f707878534382f1d Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 6 Dec 2024 22:19:35 +0300 Subject: [PATCH 6/9] Updated on 2026-08-14 --- .../java/com/tangem/tap/TangemApplication.kt | 1 + .../com/tangem/tap/common/DialogManager.kt | 6 ++ .../com/tangem/tap/common/redux/AppDialog.kt | 6 ++ .../domain/scanCard/LegacyScanProcessor.kt | 100 ++++++++++++++---- .../ui/dialogs/WalletAlreadyWasUsedDialog.kt | 33 ++++++ .../features/welcome/redux/WelcomeAction.kt | 1 + .../welcome/redux/WelcomeMiddleware.kt | 3 + .../features/welcome/redux/WelcomeReducer.kt | 3 + .../tap/proxy/redux/DaggerGraphState.kt | 2 + core/res/src/main/res/values-ru/strings.xml | 3 + core/res/src/main/res/values/strings.xml | 4 + .../feedback/SendFeedbackEmailUseCase.kt | 1 + .../feedback/models/FeedbackEmailType.kt | 2 + .../utils/EmailMessageBodyResolver.kt | 1 + .../utils/EmailMessageTitleResolver.kt | 1 + .../feedback/utils/EmailSubjectResolver.kt | 1 + gradle/dependencies.toml | 2 +- 17 files changed, 146 insertions(+), 24 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/WalletAlreadyWasUsedDialog.kt diff --git a/app/src/main/java/com/tangem/tap/TangemApplication.kt b/app/src/main/java/com/tangem/tap/TangemApplication.kt index 0e2e83f67f..78c59931bf 100644 --- a/app/src/main/java/com/tangem/tap/TangemApplication.kt +++ b/app/src/main/java/com/tangem/tap/TangemApplication.kt @@ -258,6 +258,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { onrampFeatureToggles = onrampFeatureToggles, environmentConfigStorage = environmentConfigStorage, onboardingV2FeatureToggles = onboardingV2FeatureToggles, + appPreferencesStore = appPreferencesStore, ), ), ) diff --git a/app/src/main/java/com/tangem/tap/common/DialogManager.kt b/app/src/main/java/com/tangem/tap/common/DialogManager.kt index 8bc1f8e7ae..333923e760 100644 --- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt +++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt @@ -159,6 +159,12 @@ class DialogManager : StoreSubscriber { ), context = context, ) + is AppDialog.WalletAlreadyWasUsedDialog -> WalletAlreadyWasUsedDialog.create( + context = context, + onOk = state.dialog.onOk, + onSupport = state.dialog.onSupportClick, + onCancel = state.dialog.onCancel, + ) is AppDialog.RemoveWalletDialog -> SimpleCancelableAlertDialog.create( title = context.getString(state.dialog.titleRes, state.dialog.currencyTitle), messageRes = state.dialog.messageRes, diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppDialog.kt b/app/src/main/java/com/tangem/tap/common/redux/AppDialog.kt index 04a76036c3..4586881efc 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppDialog.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppDialog.kt @@ -48,4 +48,10 @@ sealed class AppDialog : StateDialog { val messageRes: Int = R.string.token_details_unable_hide_alert_message val titleRes: Int = R.string.token_details_unable_hide_alert_title } + + data class WalletAlreadyWasUsedDialog( + val onOk: () -> Unit, + val onSupportClick: () -> Unit, + val onCancel: () -> Unit, + ) : AppDialog() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt index 09ca2e1778..2de5746b8e 100644 --- a/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt @@ -9,18 +9,25 @@ import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.analytics.models.Basic +import com.tangem.datasource.api.tangemTech.models.UserTokensResponse +import com.tangem.datasource.local.preferences.PreferencesKeys +import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull import com.tangem.domain.common.TapWorkarounds.canSkipBackup import com.tangem.domain.common.extensions.withMainContext import com.tangem.domain.common.util.twinsIsTwinned +import com.tangem.domain.feedback.models.FeedbackEmailType import com.tangem.domain.models.scan.ScanResponse +import com.tangem.domain.wallets.builder.UserWalletIdBuilder import com.tangem.tap.common.analytics.paramsInterceptor.CardContextInterceptor import com.tangem.tap.common.extensions.* +import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.features.disclaimer.createDisclaimer import com.tangem.tap.features.onboarding.OnboardingHelper import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsStep import com.tangem.tap.features.onboarding.products.wallet.redux.BackupStartedSource +import com.tangem.tap.mainScope import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.scope import com.tangem.tap.store @@ -135,32 +142,79 @@ internal object LegacyScanProcessor { ) { store.dispatchOnMain(TwinCardsAction.IfTwinsPrepareState(scanResponse)) - if (OnboardingHelper.isOnboardingCase(scanResponse)) { - Analytics.addContext(scanResponse) - onWalletNotCreated() - // must check skip backup using card canSkipBackup - store.dispatchOnMain( - GlobalAction.Onboarding.Start( - scanResponse = scanResponse, - source = BackupStartedSource.Onboarding, - canSkipBackup = scanResponse.card.canSkipBackup, + checkCardWasUsedInApp( + scanResponse = scanResponse, + onCancel = { mainScope.launch { onProgressStateChange.invoke(false) } }, + ) { + if (OnboardingHelper.isOnboardingCase(scanResponse)) { + Analytics.addContext(scanResponse) + onWalletNotCreated() + // must check skip backup using card canSkipBackup + store.dispatchOnMain( + GlobalAction.Onboarding.Start( + scanResponse = scanResponse, + source = BackupStartedSource.Onboarding, + canSkipBackup = scanResponse.card.canSkipBackup, + ), + ) + val appScreen = OnboardingHelper.whereToNavigate(scanResponse) + navigateTo(appScreen) { onProgressStateChange(it) } + } else { + Analytics.setContext(scanResponse) + + val wasTwinsOnboardingShown = + store.inject(DaggerGraphState::wasTwinsOnboardingShownUseCase).invokeSync() + + if (scanResponse.twinsIsTwinned() && !wasTwinsOnboardingShown) { + onWalletNotCreated() + store.dispatchOnMain(TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly(scanResponse))) + navigateTo(AppRoute.OnboardingTwins) { onProgressStateChange(it) } + } else { + delay(DELAY_SDK_DIALOG_CLOSE) + onSuccess(scanResponse) + } + } + } + } + + /** + * Checks if card has password and never login at this app + * Show alert in this case + */ + private suspend fun checkCardWasUsedInApp( + scanResponse: ScanResponse, + onCancel: () -> Unit, + onSuccess: suspend () -> Unit, + ) { + val userWalletId = UserWalletIdBuilder.card(scanResponse.card).build() + if (userWalletId == null) { + onSuccess() + return + } + val appPrefStoreStore = store.inject(DaggerGraphState::appPreferencesStore) + val tokens = appPrefStoreStore.getObjectSyncOrNull( + key = PreferencesKeys.getUserTokensKey(userWalletId.stringValue), + ) + if (scanResponse.card.isAccessCodeSet && tokens == null) { + store.dispatchDialogShow( + AppDialog.WalletAlreadyWasUsedDialog( + onOk = { mainScope.launch { onSuccess() } }, + onSupportClick = { + val cardInfo = + store.inject(DaggerGraphState::getCardInfoUseCase).invoke(scanResponse).getOrNull() + ?: error("CardInfo must be not null") + + scope.launch { + store.inject(DaggerGraphState::sendFeedbackEmailUseCase) + .invoke(type = FeedbackEmailType.PreActivatedWallet(cardInfo)) + } + onCancel() + }, + onCancel = { onCancel() }, ), ) - val appScreen = OnboardingHelper.whereToNavigate(scanResponse) - navigateTo(appScreen) { onProgressStateChange(it) } } else { - Analytics.setContext(scanResponse) - - val wasTwinsOnboardingShown = store.inject(DaggerGraphState::wasTwinsOnboardingShownUseCase).invokeSync() - - if (scanResponse.twinsIsTwinned() && !wasTwinsOnboardingShown) { - onWalletNotCreated() - store.dispatchOnMain(TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly(scanResponse))) - navigateTo(AppRoute.OnboardingTwins) { onProgressStateChange(it) } - } else { - delay(DELAY_SDK_DIALOG_CLOSE) - onSuccess(scanResponse) - } + onSuccess() } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/WalletAlreadyWasUsedDialog.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/WalletAlreadyWasUsedDialog.kt new file mode 100644 index 0000000000..6d5875be78 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/WalletAlreadyWasUsedDialog.kt @@ -0,0 +1,33 @@ +package com.tangem.tap.features.onboarding.products.wallet.ui.dialogs + +import android.content.Context +import androidx.appcompat.app.AlertDialog +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.store +import com.tangem.wallet.R + +object WalletAlreadyWasUsedDialog { + + fun create(context: Context, onOk: () -> Unit, onCancel: () -> Unit, onSupport: () -> Unit): AlertDialog { + return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog).apply { + setTitle(R.string.security_alert_title) + setMessage(R.string.wallet_been_activated_message) + setPositiveButton(R.string.this_is_my_wallet_title) { dialog, _ -> + onOk() + dialog.dismiss() + } + setNeutralButton(R.string.common_cancel) { dialog, _ -> + onCancel() + dialog.dismiss() + } + setNegativeButton(R.string.alert_button_request_support) { dialog, _ -> + onSupport() + dialog.dismiss() + } + setOnDismissListener { + store.dispatch(GlobalAction.HideDialog) + } + }.create() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt index 97138375c6..fbae455ce2 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt @@ -19,6 +19,7 @@ internal sealed interface WelcomeAction : Action { object ProceedWithCard : WelcomeAction { object Success : WelcomeAction data class Error(val error: TangemError) : WelcomeAction + data class ChangeProgress(val showProgress: Boolean) : WelcomeAction } data class ProceedWithIntent(val intent: Intent) : WelcomeAction diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt index 8bd3ee34fc..b9f8660cf1 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt @@ -197,6 +197,9 @@ internal class WelcomeMiddleware { } } }, + onProgressStateChange = { + store.dispatchWithMain(WelcomeAction.ProceedWithCard.ChangeProgress(it)) + }, onWalletNotCreated = { store.dispatchOnMain(WelcomeAction.ProceedWithCard.Success) }, diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeReducer.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeReducer.kt index 85db06dc63..35f81371fa 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeReducer.kt @@ -27,6 +27,9 @@ internal object WelcomeReducer { error = action.error, isUnlockWithCardInProgress = false, ) + is WelcomeAction.ProceedWithCard.ChangeProgress -> state.copy( + isUnlockWithCardInProgress = action.showProgress, + ) is WelcomeAction.ProceedWithBiometrics.Success -> state.copy(isUnlockWithBiometricsInProgress = false) is WelcomeAction.ProceedWithCard.Success -> state.copy(isUnlockWithCardInProgress = false) is WelcomeAction.CloseError -> state.copy(error = null) diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt index 075686701f..cb94b5b0cc 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt @@ -8,6 +8,7 @@ import com.tangem.core.navigation.url.UrlOpener import com.tangem.datasource.connection.NetworkConnectionManager import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage import com.tangem.datasource.local.config.issuers.IssuersConfigStorage +import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.domain.appcurrency.repository.AppCurrencyRepository import com.tangem.domain.apptheme.repository.AppThemeModeRepository import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository @@ -71,4 +72,5 @@ data class DaggerGraphState( val onrampFeatureToggles: OnrampFeatureToggles? = null, val environmentConfigStorage: EnvironmentConfigStorage? = null, val onboardingV2FeatureToggles: OnboardingV2FeatureToggles? = null, + val appPreferencesStore: AppPreferencesStore? = null, ) : StateType \ No newline at end of file diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 6e9a779cef..403c45a975 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -650,6 +650,7 @@ Сканировать Отсканируйте карту или кольцо, чтобы изменить ее настройки. Изменения затронут только ту карту или кольцо, которые вы отсканировали, и не повлияют на другие устройства, привязанные к вашему кошельку. Приготовьте свой Tangem! + Уведомление безопасности Уже содержится во введенном адресе Сумма комиссии в %s раз превышает рекомендованную. Убедитесь, что указанная комиссия верна. Вы указали комиссию ниже рекомендуемой, это может привести к задержке исполнения вашей транзакции. Продолжить? @@ -861,6 +862,7 @@ Вы получите Выберите токен не доступен + Это мой кошелек Балансы скрыты Балансы показаны Отменить @@ -929,6 +931,7 @@ Разблокировать все с %s Блокчейн недоступен. Попробуйте позже. Отсканируйте карту или кольцо + Этот кошелек уже был активирован ранее.\nЕсли это сделали не вы, свяжитесь со службой поддержки.\nTangem никогда не продает кошелек вместе с предустановленным кодом доступа. Запрос на подпись сообщения.\n\n%s Dapp %1$s, запрос на\nподпись транзакции с BNB.\n\n%2$s Торговый ордер на %1$s\nЦена: %2$s\nСумма к получению: %3$s\nСумма к оплате: %4$s diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 1f7c527b60..818d75da17 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -307,6 +307,7 @@ Please tell us what card or ring do you have Hi support team, Please tell us more about your issue. Every small detail can help. + Previously activated wallet My suggestions Can\'t scan a card/ring Feedback @@ -648,6 +649,7 @@ Scan card or ring Scan the card or ring to change its settings. The changes will impact only the card or ring you\'ve scanned and will not affect other devices tied to your wallet. Get your Tangem ready! + Security Alert Already included in the entered address Your commission amount is %s times higher than the recommended amount. Please review and adjust your custom settings. You specified a commission below the recommended amount, which could cause a delay in your transaction. Continue? @@ -860,6 +862,7 @@ You receive Choose token not available + This is my wallet Balances hidden Balances shown Undo @@ -928,6 +931,7 @@ Unlock all with %s Blockchain is unreachable. Try later Scan card or ring + This wallet has already been activated earlier.\nIf it was done not by you please contact support.\nTangem never sells wallets along with the pre-generated access code. Requesting to sign a message.\n\n%s Dapp %1$s, requesting to\nsign BNB transaction.\n\n%2$s Trade order for %1$s\nPrice: %2$s\nAmount to receive: %3$s\nAmount to pay: %4$s diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/SendFeedbackEmailUseCase.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/SendFeedbackEmailUseCase.kt index 2f9578ed40..302680f4c6 100644 --- a/domain/feedback/src/main/java/com/tangem/domain/feedback/SendFeedbackEmailUseCase.kt +++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/SendFeedbackEmailUseCase.kt @@ -57,6 +57,7 @@ class SendFeedbackEmailUseCase( return when (type) { is FeedbackEmailType.ScanningProblem, is FeedbackEmailType.CurrencyDescriptionError, + is FeedbackEmailType.PreActivatedWallet, -> this is FeedbackEmailType.DirectUserRequest, is FeedbackEmailType.RateCanBeBetter, diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/models/FeedbackEmailType.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/models/FeedbackEmailType.kt index 70d4017993..2ec928f7ab 100644 --- a/domain/feedback/src/main/java/com/tangem/domain/feedback/models/FeedbackEmailType.kt +++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/models/FeedbackEmailType.kt @@ -46,4 +46,6 @@ sealed interface FeedbackEmailType { data class CurrencyDescriptionError(val currencyId: String, val currencyName: String) : FeedbackEmailType { override val cardInfo: CardInfo? = null } + + data class PreActivatedWallet(override val cardInfo: CardInfo) : FeedbackEmailType } \ No newline at end of file diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageBodyResolver.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageBodyResolver.kt index c6d5bd52eb..9983e4bcc4 100644 --- a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageBodyResolver.kt +++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageBodyResolver.kt @@ -26,6 +26,7 @@ internal class EmailMessageBodyResolver( is FeedbackEmailType.StakingProblem -> addStakingProblemBody(type) is FeedbackEmailType.SwapProblem -> addSwapProblemBody(type) is FeedbackEmailType.CurrencyDescriptionError -> addTokenInfo(type) + is FeedbackEmailType.PreActivatedWallet -> addUserRequestBody(type.cardInfo) } return build() diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageTitleResolver.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageTitleResolver.kt index d0768aff1f..1311a3016e 100644 --- a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageTitleResolver.kt +++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageTitleResolver.kt @@ -25,6 +25,7 @@ internal class EmailMessageTitleResolver(private val resources: Resources) { is FeedbackEmailType.StakingProblem, is FeedbackEmailType.SwapProblem, -> R.string.feedback_preface_tx_failed + is FeedbackEmailType.PreActivatedWallet -> R.string.feedback_preface_support } .let(resources::getString) } diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailSubjectResolver.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailSubjectResolver.kt index a833d1b6d3..e373d8d496 100644 --- a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailSubjectResolver.kt +++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailSubjectResolver.kt @@ -29,6 +29,7 @@ internal class EmailSubjectResolver(private val resources: Resources) { is FeedbackEmailType.StakingProblem, is FeedbackEmailType.SwapProblem, -> R.string.feedback_subject_tx_failed + is FeedbackEmailType.PreActivatedWallet -> R.string.feedback_subject_pre_activated_wallet is FeedbackEmailType.CurrencyDescriptionError -> R.string.feedback_token_description_error } .let(resources::getString) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 2d7317203b..b15619d568 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -89,7 +89,7 @@ markdownComposeView = "0.5.4" # region Tangem tangemBlockchainSdk = "release-app_5.18-874" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "release-app_5.18-409" +tangemCardSdk = "release-app_5.18-413" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ tangemVico = "2.0.0-alpha.25-tangem17" #tangemVico = "0.0.1" # Keep it! - used for local builds ^ From 1614d813557fb449c4ca6b3bdc98a0b81178a847 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 6 Dec 2024 23:55:04 +0300 Subject: [PATCH 7/9] Updated on 2026-08-14 --- .../java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt index 2de5746b8e..5299ee9de6 100644 --- a/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt @@ -186,7 +186,7 @@ internal object LegacyScanProcessor { onCancel: () -> Unit, onSuccess: suspend () -> Unit, ) { - val userWalletId = UserWalletIdBuilder.card(scanResponse.card).build() + val userWalletId = runCatching { UserWalletIdBuilder.card(scanResponse.card).build() }.getOrNull() if (userWalletId == null) { onSuccess() return From c451b213019bc6bcefa0de3b1228b800bf125797 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 7 Dec 2024 01:09:47 +0300 Subject: [PATCH 8/9] Updated on 2026-08-14 --- .../scanCard/DefaultScanCardProcessor.kt | 2 + .../domain/scanCard/LegacyScanProcessor.kt | 10 ++- .../tangem/domain/card/ScanCardProcessor.kt | 1 + .../details/model/UserWalletListModel.kt | 2 +- .../features/details/utils/UserWalletSaver.kt | 68 ++++++++++--------- 5 files changed, 50 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/DefaultScanCardProcessor.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/DefaultScanCardProcessor.kt index b8515ee5b9..a19ec7e918 100644 --- a/app/src/main/java/com/tangem/tap/domain/scanCard/DefaultScanCardProcessor.kt +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/DefaultScanCardProcessor.kt @@ -32,6 +32,7 @@ internal class DefaultScanCardProcessor : ScanCardProcessor { onProgressStateChange: suspend (showProgress: Boolean) -> Unit, onWalletNotCreated: suspend () -> Unit, disclaimerWillShow: () -> Unit, + onCancel: suspend () -> Unit, onFailure: suspend (error: TangemError) -> Unit, onSuccess: suspend (scanResponse: ScanResponse) -> Unit, ) { @@ -52,6 +53,7 @@ internal class DefaultScanCardProcessor : ScanCardProcessor { onProgressStateChange, onWalletNotCreated, disclaimerWillShow, + onCancel, onFailure, onSuccess, ) diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt index 5299ee9de6..b1810f1745 100644 --- a/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt @@ -57,6 +57,7 @@ internal object LegacyScanProcessor { onProgressStateChange: suspend (showProgress: Boolean) -> Unit, onWalletNotCreated: suspend () -> Unit, disclaimerWillShow: () -> Unit, + onCancel: suspend () -> Unit, onFailure: suspend (error: TangemError) -> Unit, onSuccess: suspend (scanResponse: ScanResponse) -> Unit, ) = withMainContext { @@ -88,6 +89,7 @@ internal object LegacyScanProcessor { onProgressStateChange = onProgressStateChange, onSuccess = onSuccess, onWalletNotCreated = onWalletNotCreated, + onCancel = onCancel, ) }, ) @@ -138,13 +140,19 @@ internal object LegacyScanProcessor { scanResponse: ScanResponse, crossinline onProgressStateChange: suspend (showProgress: Boolean) -> Unit, crossinline onWalletNotCreated: suspend () -> Unit, + crossinline onCancel: suspend () -> Unit, crossinline onSuccess: suspend (ScanResponse) -> Unit, ) { store.dispatchOnMain(TwinCardsAction.IfTwinsPrepareState(scanResponse)) checkCardWasUsedInApp( scanResponse = scanResponse, - onCancel = { mainScope.launch { onProgressStateChange.invoke(false) } }, + onCancel = { + mainScope.launch { + onProgressStateChange.invoke(false) + onCancel() + } + }, ) { if (OnboardingHelper.isOnboardingCase(scanResponse)) { Analytics.addContext(scanResponse) diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/ScanCardProcessor.kt b/domain/card/src/main/kotlin/com/tangem/domain/card/ScanCardProcessor.kt index ff96eda7f5..9c52d3c84a 100644 --- a/domain/card/src/main/kotlin/com/tangem/domain/card/ScanCardProcessor.kt +++ b/domain/card/src/main/kotlin/com/tangem/domain/card/ScanCardProcessor.kt @@ -18,6 +18,7 @@ interface ScanCardProcessor { onProgressStateChange: suspend (showProgress: Boolean) -> Unit = {}, onWalletNotCreated: suspend () -> Unit = {}, disclaimerWillShow: () -> Unit = {}, + onCancel: suspend () -> Unit = {}, onFailure: suspend (error: TangemError) -> Unit = {}, onSuccess: suspend (scanResponse: ScanResponse) -> Unit = {}, ) diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/model/UserWalletListModel.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/model/UserWalletListModel.kt index a36e126509..ef9905ff46 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/model/UserWalletListModel.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/model/UserWalletListModel.kt @@ -64,6 +64,6 @@ internal class UserWalletListModel @Inject constructor( } private fun addUserWallet() = withProgress(isWalletSavingInProgress) { - userWalletSaver.scanAndSaveUserWallet() + userWalletSaver.scanAndSaveUserWallet(modelScope) } } \ No newline at end of file diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletSaver.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletSaver.kt index 80a7f98ce1..b3dc6173e0 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletSaver.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletSaver.kt @@ -28,7 +28,11 @@ import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase import com.tangem.domain.wallets.usecase.SaveWalletUseCase import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsSyncUseCase import com.tangem.features.details.impl.R +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine import javax.inject.Inject +import kotlin.coroutines.resume @ComponentScoped @Suppress("LongParameterList") @@ -42,9 +46,9 @@ internal class UserWalletSaver @Inject constructor( private val router: Router, ) { - suspend fun scanAndSaveUserWallet() = recover( + suspend fun scanAndSaveUserWallet(scope: CoroutineScope) = recover( block = { - val response = scanCard() ?: return@recover + val response = scanCard(scope) ?: return@recover val userWallet = createUserWallet(response) saveWallet(userWallet) @@ -105,36 +109,38 @@ internal class UserWalletSaver @Inject constructor( return ensureNotNull(userWallet) { Error.Unknown } } - private suspend fun Raise.scanCard(): ScanResponse? { - var response: ScanResponse? = null + private suspend fun Raise.scanCard(scope: CoroutineScope) = suspendCancellableCoroutine { continuation -> + scope.launch { + scanCardProcessor.scan( + analyticsSource = AnalyticsParam.ScreensSources.Settings, + onWalletNotCreated = { + /* no-op */ + }, + disclaimerWillShow = { + continuation.resume(null) + router.pop() + }, + onSuccess = { + continuation.resume(it) + }, + onCancel = { + continuation.resume(null) + }, + onFailure = { tangemError -> + val error = if (!tangemError.silent) { + val message = tangemError.messageResId + ?.let(::resourceReference) + ?: stringReference(tangemError.customMessage) - scanCardProcessor.scan( - analyticsSource = AnalyticsParam.ScreensSources.Settings, - onWalletNotCreated = { - /* no-op */ - }, - disclaimerWillShow = { - router.pop() - }, - onSuccess = { - response = it - }, - onFailure = { tangemError -> - val error = if (!tangemError.silent) { - val message = tangemError.messageResId - ?.let(::resourceReference) - ?: stringReference(tangemError.customMessage) - - Error.Message(message) - } else { - Error.Silent - } - - raise(error) - }, - ) - - return response + Error.Message(message) + } else { + Error.Silent + } + continuation.resume(null) + raise(error) + }, + ) + } } sealed class Error { From fc300de8d7540858b637339f4019a98328f662cb Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 7 Dec 2024 01:55:50 +0300 Subject: [PATCH 9/9] Updated on 2026-08-14 --- .../features/details/utils/UserWalletSaver.kt | 48 +++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletSaver.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletSaver.kt index b3dc6173e0..a427f0fc29 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletSaver.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletSaver.kt @@ -1,6 +1,7 @@ package com.tangem.features.details.utils import androidx.compose.ui.res.stringResource +import arrow.core.Either import arrow.core.raise.Raise import arrow.core.raise.ensureNotNull import arrow.core.raise.fold @@ -46,21 +47,31 @@ internal class UserWalletSaver @Inject constructor( private val router: Router, ) { - suspend fun scanAndSaveUserWallet(scope: CoroutineScope) = recover( - block = { - val response = scanCard(scope) ?: return@recover - val userWallet = createUserWallet(response) + suspend fun scanAndSaveUserWallet(scope: CoroutineScope) { + val response = scanCard(scope) + response.fold( + ifLeft = { + val message = it.message - saveWallet(userWallet) - }, - recover = { error -> - val message = error.message + if (!message.isNullOrEmpty()) { + messageSender.send(SnackbarMessage(message)) + } + }, + ifRight = { scanResponse -> + recover(block = { + scanResponse ?: return + val userWallet = createUserWallet(scanResponse) + saveWallet(userWallet) + }, recover = { + val message = it.message - if (!message.isNullOrEmpty()) { - messageSender.send(SnackbarMessage(message)) - } - }, - ) + if (!message.isNullOrEmpty()) { + messageSender.send(SnackbarMessage(message)) + } + },) + }, + ) + } private suspend fun Raise.saveWallet(userWallet: UserWallet) { fold( @@ -109,7 +120,7 @@ internal class UserWalletSaver @Inject constructor( return ensureNotNull(userWallet) { Error.Unknown } } - private suspend fun Raise.scanCard(scope: CoroutineScope) = suspendCancellableCoroutine { continuation -> + private suspend fun scanCard(scope: CoroutineScope) = suspendCancellableCoroutine { continuation -> scope.launch { scanCardProcessor.scan( analyticsSource = AnalyticsParam.ScreensSources.Settings, @@ -117,14 +128,14 @@ internal class UserWalletSaver @Inject constructor( /* no-op */ }, disclaimerWillShow = { - continuation.resume(null) + continuation.resume(Either.Right(null)) router.pop() }, onSuccess = { - continuation.resume(it) + continuation.resume(Either.Right(it)) }, onCancel = { - continuation.resume(null) + continuation.resume(Either.Right(null)) }, onFailure = { tangemError -> val error = if (!tangemError.silent) { @@ -136,8 +147,7 @@ internal class UserWalletSaver @Inject constructor( } else { Error.Silent } - continuation.resume(null) - raise(error) + continuation.resume(Either.Left(error)) }, ) }