Updated on 2026-08-14

This commit is contained in:
Tangem 2024-12-06 22:19:35 +03:00
parent 44209d7db1
commit 1827ba42d8
17 changed files with 146 additions and 24 deletions

View file

@ -258,6 +258,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
onrampFeatureToggles = onrampFeatureToggles,
environmentConfigStorage = environmentConfigStorage,
onboardingV2FeatureToggles = onboardingV2FeatureToggles,
appPreferencesStore = appPreferencesStore,
),
),
)

View file

@ -159,6 +159,12 @@ class DialogManager : StoreSubscriber<GlobalState> {
),
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,

View file

@ -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()
}

View file

@ -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<UserTokensResponse>(
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()
}
}

View file

@ -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()
}
}

View file

@ -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

View file

@ -197,6 +197,9 @@ internal class WelcomeMiddleware {
}
}
},
onProgressStateChange = {
store.dispatchWithMain(WelcomeAction.ProceedWithCard.ChangeProgress(it))
},
onWalletNotCreated = {
store.dispatchOnMain(WelcomeAction.ProceedWithCard.Success)
},

View file

@ -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)

View file

@ -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