Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-20 10:53:25 +04:00
parent 40a5e13ad4
commit f8268dbe6e
20 changed files with 126 additions and 125 deletions

View file

@ -8,6 +8,7 @@ import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.clickAndWaitFor
import com.tangem.common.extensions.clickWithAssertion
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.domain.card.ScanFailsRequester
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import com.tangem.scenarios.checkFailedTransactionDialog
@ -28,16 +29,19 @@ import com.tangem.screens.onStoriesScreen
import com.tangem.screens.onTokenDetailsScreen
import com.tangem.screens.onMainScreenTopBar
import com.tangem.tap.domain.sdk.mocks.MockProvider
import com.tangem.tap.store
import dagger.hilt.android.testing.HiltAndroidTest
import io.qameta.allure.kotlin.AllureId
import io.qameta.allure.kotlin.junit4.DisplayName
import org.junit.Ignore
import org.junit.Test
import javax.inject.Inject
@HiltAndroidTest
class FeedbackTest : BaseTestCase() {
@Inject
lateinit var scanFailsRequester: ScanFailsRequester
@AllureId("894")
@DisplayName("Send feedback: from details")
@Test
@ -177,9 +181,8 @@ class FeedbackTest : BaseTestCase() {
}
step("Force show 'Scan warning' dialog"){
runOnUiThread {
val requester = store.state.daggerGraphState.scanFailsRequester!!
MainScope().launch {
requester.show(AnalyticsParam.ScreensSources.Main)
scanFailsRequester.show(AnalyticsParam.ScreensSources.Main)
}
}
}

View file

@ -7,12 +7,12 @@ import androidx.lifecycle.LifecycleOwner
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.wallets.hot.HotWalletPasswordRequester
import com.tangem.domain.wallets.usecase.ClearAllHotWalletContextualUnlockUseCase
import com.tangem.tap.LockTimerWorker.Companion.TAG
import com.tangem.tap.common.extensions.dispatchNavigationAction
import com.tangem.utils.logging.TangemLogger
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@ -30,6 +30,7 @@ internal class LockUserWalletsTimer(
private val coroutineScope: CoroutineScope,
private val clearAllHotWalletContextualUnlockUseCase: ClearAllHotWalletContextualUnlockUseCase,
private val passwordRequester: HotWalletPasswordRequester,
private val appRouter: AppRouter,
) : LifecycleOwner by context as LifecycleOwner,
DefaultLifecycleObserver {
@ -57,7 +58,7 @@ internal class LockUserWalletsTimer(
if (shouldOpenWelcomeScreenOnResume) {
passwordRequester.dismiss()
clearAllHotWalletContextualUnlockUseCase.invoke()
store.dispatchNavigationAction { replaceAll(AppRoute.Welcome()) }
appRouter.replaceAll(AppRoute.Welcome())
settingsRepository.setShouldOpenWelcomeScreenOnResume(value = false)
}
}
@ -121,7 +122,7 @@ internal class LockUserWalletsTimer(
.onRight {
passwordRequester.dismiss()
clearAllHotWalletContextualUnlockUseCase.invoke()
store.dispatchNavigationAction { replaceAll(AppRoute.Welcome()) }
appRouter.replaceAll(AppRoute.Welcome())
}
}
}

View file

@ -28,6 +28,7 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import arrow.core.getOrElse
import com.tangem.common.routing.AppRouter
import com.tangem.common.routing.deeplink.DeeplinkConst.WEBLINK_KEY
import com.tangem.common.routing.deeplink.PayloadToDeeplinkConverter
import com.tangem.core.analytics.api.AnalyticsEventHandler
@ -39,7 +40,6 @@ import com.tangem.core.navigation.url.UrlOpener
import com.tangem.data.balancehiding.DefaultDeviceFlipDetector
import com.tangem.data.card.sdk.CardSdkOwner
import com.tangem.domain.apptheme.model.AppThemeMode
import com.tangem.domain.card.ScanCardUseCase
import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.common.wallets.UserWalletsListRepository
@ -61,7 +61,6 @@ import com.tangem.tap.common.analytics.events.Push
import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder
import com.tangem.tap.features.intentHandler.handlers.BackgroundScanIntentHandler
import com.tangem.tap.features.main.MainViewModel
import com.tangem.tap.proxy.redux.DaggerGraphAction
import com.tangem.tap.routing.component.RoutingComponent
import com.tangem.tap.routing.configurator.AppRouterConfig
import com.tangem.tap.routing.utils.DeepLinkFactory
@ -104,9 +103,6 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
@Inject
lateinit var injectedTangemSdkManager: TangemSdkManager
@Inject
lateinit var scanCardUseCase: ScanCardUseCase
@Inject
lateinit var settingsRepository: SettingsRepository
@ -123,6 +119,9 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
@Inject
internal lateinit var appRouterConfig: AppRouterConfig
@Inject
internal lateinit var appRouter: AppRouter
@Inject
internal lateinit var routingComponentFactory: RoutingComponent.Factory
@ -269,13 +268,7 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
userWalletsListRepository = userWalletsListRepository,
clearAllHotWalletContextualUnlockUseCase = clearAllHotWalletContextualUnlockUseCase,
passwordRequester = passwordRequester,
)
store.dispatch(
DaggerGraphAction.SetActivityDependencies(
scanCardUseCase = scanCardUseCase,
cardSdkConfigRepository = cardSdkConfigRepository,
),
appRouter = appRouter,
)
}

View file

@ -79,6 +79,8 @@ import org.rekotlin.Store
lateinit var store: Store<AppState>
lateinit var walletsRepository: WalletsRepository
val foregroundActivityObserver = ForegroundActivityObserver
open class TangemApplication : Application(), ImageLoaderFactory, Configuration.Provider {
@ -129,9 +131,6 @@ open class TangemApplication : Application(), ImageLoaderFactory, Configuration.
val getAppThemeModeUseCase: GetAppThemeModeUseCase
get() = entryPoint.getGetAppThemeModeUseCase()
private val walletsRepository: WalletsRepository
get() = entryPoint.getWalletsRepository()
private val oneTimeEventFilter: OneTimeEventFilter
get() = entryPoint.getOneTimeEventFilter()
@ -276,6 +275,8 @@ open class TangemApplication : Application(), ImageLoaderFactory, Configuration.
}
fun init() {
walletsRepository = entryPoint.getWalletsRepository()
apiConfigsManager.initialize()
store = createReduxStore()

View file

@ -10,10 +10,8 @@ import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.models.scan.ProductType
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.store
import com.tangem.tap.walletsRepository
import kotlinx.coroutines.runBlocking
/**
@ -23,8 +21,6 @@ class CardContextInterceptor(
private val scanResponse: ScanResponse,
) : ParamsInterceptor {
private val walletsRepository = store.inject(DaggerGraphState::walletsRepository)
private val userWalletId = UserWalletIdBuilder.scanResponse(scanResponse).build()
override fun id(): String = CardContextInterceptor.id()

View file

@ -3,6 +3,7 @@ package com.tangem.tap.di
import android.content.Context
import com.tangem.core.analytics.api.AnalyticsErrorHandler
import com.tangem.domain.card.BuildConfig
import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.dynamicaddresses.DynamicAddressesFeatureToggles
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
@ -36,6 +37,7 @@ internal class TangemSdkManagerModule {
dynamicAddressesFeatureToggles: DynamicAddressesFeatureToggles,
blockchainToDeriveFinder: BlockchainToDeriveFinder,
analyticsErrorHandler: AnalyticsErrorHandler,
cardRepository: CardRepository,
): TangemSdkManager {
return if (BuildConfig.MOCK_DATA_SOURCE) {
MockTangemSdkManager(resources = context.resources)
@ -50,6 +52,7 @@ internal class TangemSdkManagerModule {
dynamicAddressesFeatureToggles = dynamicAddressesFeatureToggles,
blockchainToDeriveFinder = blockchainToDeriveFinder,
analyticsErrorHandler = analyticsErrorHandler,
cardRepository = cardRepository,
)
}
}

View file

@ -7,6 +7,7 @@ import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
import com.tangem.tap.domain.scanCard.CardScanningFeatureToggles
import com.tangem.tap.domain.scanCard.DefaultScanCardProcessor
import com.tangem.tap.domain.scanCard.LegacyScanProcessor
import com.tangem.tap.domain.scanCard.UseCaseScanProcessor
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -25,8 +26,16 @@ internal object CardLegacyDomainModule {
@Provides
@Singleton
fun provideScanCardProcessor(legacyScanProcessor: LegacyScanProcessor): ScanCardProcessor {
return DefaultScanCardProcessor(legacyScanProcessor = legacyScanProcessor)
fun provideScanCardProcessor(
legacyScanProcessor: LegacyScanProcessor,
useCaseScanProcessor: UseCaseScanProcessor,
cardScanningFeatureToggles: CardScanningFeatureToggles,
): ScanCardProcessor {
return DefaultScanCardProcessor(
legacyScanProcessor = legacyScanProcessor,
useCaseScanProcessor = useCaseScanProcessor,
cardScanningFeatureToggles = cardScanningFeatureToggles,
)
}
@Provides

View file

@ -5,16 +5,15 @@ import com.tangem.common.core.TangemError
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.store
// TODO: Remove this object after feature toggle was removed and use ScanCardUseCase instead
internal class DefaultScanCardProcessor(
private val legacyScanProcessor: LegacyScanProcessor,
private val useCaseScanProcessor: UseCaseScanProcessor,
private val cardScanningFeatureToggles: CardScanningFeatureToggles,
) : ScanCardProcessor {
private val isNewCardScanningEnabled: Boolean
get() = store.inject(DaggerGraphState::cardScanningFeatureToggles).isNewCardScanningEnabled
get() = cardScanningFeatureToggles.isNewCardScanningEnabled
override suspend fun scan(
cardId: String?,
@ -23,7 +22,7 @@ internal class DefaultScanCardProcessor(
shouldCheckIsAlreadyActivated: Boolean,
): CompletionResult<ScanResponse> {
return if (isNewCardScanningEnabled) {
UseCaseScanProcessor.scan(cardId, allowsRequestAccessCodeFromRepository)
useCaseScanProcessor.scan(cardId, allowsRequestAccessCodeFromRepository)
} else {
legacyScanProcessor.scan(
analyticsSource = analyticsSource,
@ -47,7 +46,7 @@ internal class DefaultScanCardProcessor(
onSuccess: suspend (scanResponse: ScanResponse) -> Unit,
) {
if (isNewCardScanningEnabled) {
UseCaseScanProcessor.scan(
useCaseScanProcessor.scan(
analyticsSource = analyticsSource,
cardId = cardId,
onProgressStateChange = onProgressStateChange,

View file

@ -6,6 +6,7 @@ import com.tangem.common.core.TangemSdkError
import com.tangem.common.doOnFailure
import com.tangem.common.doOnSuccess
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsEvent
@ -21,19 +22,18 @@ import com.tangem.core.ui.extensions.toWrappedList
import com.tangem.core.ui.message.dialog.Dialogs
import com.tangem.domain.card.ScanFailsCounter
import com.tangem.domain.card.common.util.twinsIsTwinned
import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
import com.tangem.sdk.extensions.localizedDescriptionRes
import com.tangem.tap.common.analytics.paramsInterceptor.CardContextInterceptor
import com.tangem.tap.common.extensions.dispatchNavigationAction
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.features.disclaimer.createDisclaimer
import com.tangem.tap.features.onboarding.OnboardingHelper
import com.tangem.tap.mainScope
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.tangemSdkManager
import com.tangem.utils.extensions.DELAY_SDK_DIALOG_CLOSE
import kotlinx.coroutines.Dispatchers
@ -44,11 +44,17 @@ import javax.inject.Inject
import javax.inject.Singleton
@Singleton
@Suppress("LongParameterList")
internal class LegacyScanProcessor @Inject constructor(
@GlobalUiMessageSender private val uiMessageSender: UiMessageSender,
private val analyticsEventHandler: AnalyticsEventHandler,
private val trackingContextProxy: TrackingContextProxy,
private val scanFailsCounter: ScanFailsCounter,
private val appRouter: AppRouter,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val wasTwinsOnboardingShownUseCase: WasTwinsOnboardingShownUseCase,
private val cardRepository: CardRepository,
private val onboardingHelper: OnboardingHelper,
) {
suspend fun scan(
@ -146,7 +152,7 @@ internal class LegacyScanProcessor @Inject constructor(
crossinline disclaimerWillShow: () -> Unit = {},
crossinline nextHandler: suspend (ScanResponse) -> Unit,
) {
val disclaimer = scanResponse.card.createDisclaimer()
val disclaimer = scanResponse.card.createDisclaimer(cardRepository)
if (disclaimer.isAccepted()) {
nextHandler(scanResponse)
@ -157,9 +163,7 @@ internal class LegacyScanProcessor @Inject constructor(
withContext(Dispatchers.Main.immediate) {
disclaimerWillShow()
store.dispatchNavigationAction {
push(AppRoute.Disclaimer(isTosAccepted = false))
}
appRouter.push(AppRoute.Disclaimer(isTosAccepted = false))
}
}
}
@ -189,7 +193,7 @@ internal class LegacyScanProcessor @Inject constructor(
mainScope.launch {
onCancel()
store.inject(DaggerGraphState::sendFeedbackEmailUseCase).invoke(
sendFeedbackEmailUseCase.invoke(
type = FeedbackEmailType.CardAttestationFailed,
)
}
@ -209,7 +213,7 @@ internal class LegacyScanProcessor @Inject constructor(
crossinline onWalletNotCreated: suspend () -> Unit,
crossinline onSuccess: suspend (ScanResponse) -> Unit,
) {
if (OnboardingHelper.isOnboardingCase(scanResponse)) {
if (onboardingHelper.isOnboardingCase(scanResponse)) {
trackingContextProxy.addContext(scanResponse)
onWalletNotCreated()
navigateTo(
@ -221,8 +225,7 @@ internal class LegacyScanProcessor @Inject constructor(
} else {
trackingContextProxy.setContext(scanResponse)
val wasTwinsOnboardingShown =
store.inject(DaggerGraphState::wasTwinsOnboardingShownUseCase).invokeSync()
val wasTwinsOnboardingShown = wasTwinsOnboardingShownUseCase.invokeSync()
if (scanResponse.twinsIsTwinned() && !wasTwinsOnboardingShown) {
onWalletNotCreated()
@ -241,7 +244,7 @@ internal class LegacyScanProcessor @Inject constructor(
private suspend inline fun navigateTo(route: AppRoute, onProgressStateChange: (showProgress: Boolean) -> Unit) {
delay(DELAY_SDK_DIALOG_CLOSE)
store.dispatchNavigationAction { push(route) }
appRouter.push(route)
onProgressStateChange(false)
}
}

View file

@ -4,33 +4,46 @@ import arrow.fx.coroutines.resourceScope
import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemError
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
import com.tangem.core.analytics.utils.TrackingContextProxy
import com.tangem.domain.card.ScanCardException
import com.tangem.domain.card.ScanCardUseCase
import com.tangem.domain.card.ScanFailsRequester
import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
import com.tangem.tap.common.analytics.events.TangemSdkErrorEvent
import com.tangem.tap.common.extensions.dispatchNavigationAction
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.domain.scanCard.chains.*
import com.tangem.tap.domain.scanCard.utils.ScanCardExceptionConverter
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.features.onboarding.OnboardingHelper
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.utils.extensions.DELAY_SDK_DIALOG_CLOSE
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import javax.inject.Inject
import javax.inject.Singleton
internal object UseCaseScanProcessor {
@Singleton
@Suppress("LongParameterList")
internal class UseCaseScanProcessor @Inject constructor(
private val scanCardUseCase: ScanCardUseCase,
private val scanFailsRequester: ScanFailsRequester,
private val appRouter: AppRouter,
private val trackingContextProxy: TrackingContextProxy,
private val wasTwinsOnboardingShownUseCase: WasTwinsOnboardingShownUseCase,
private val cardRepository: CardRepository,
private val onboardingHelper: OnboardingHelper,
) {
private val scanCardExceptionConverter = ScanCardExceptionConverter()
suspend fun scan(
cardId: String? = null,
allowsRequestAccessCodeFromRepository: Boolean = false,
): CompletionResult<ScanResponse> {
val scanCardUseCase = store.inject(DaggerGraphState::scanCardUseCase)
return scanCardUseCase(cardId, allowsRequestAccessCodeFromRepository)
.fold(
ifLeft = { scanCardException ->
@ -53,7 +66,6 @@ internal object UseCaseScanProcessor {
onFailure: suspend (error: TangemError) -> Unit,
onSuccess: suspend (scanResponse: ScanResponse) -> Unit,
) = progressScope(onProgressStateChange) {
val scanCardUseCase = store.inject(DaggerGraphState::scanCardUseCase)
val chains = buildList {
add(
FailedScansCounterChain(
@ -61,8 +73,8 @@ internal object UseCaseScanProcessor {
),
)
add(AnalyticsChain(Basic.CardWasScanned(analyticsSource)))
add(DisclaimerChain(store, disclaimerWillShow))
add(CheckForOnboardingChain(store))
add(DisclaimerChain(appRouter, cardRepository, disclaimerWillShow))
add(CheckForOnboardingChain(trackingContextProxy, wasTwinsOnboardingShownUseCase, onboardingHelper))
}
scanCardUseCase(cardId, afterScanChains = chains).fold(
@ -73,7 +85,7 @@ internal object UseCaseScanProcessor {
private fun showScanFailsDialog(source: AnalyticsParam.ScreensSources) {
scope.launch {
store.inject(DaggerGraphState::scanFailsRequester).show(source)
scanFailsRequester.show(source)
}
}
@ -86,7 +98,6 @@ internal object UseCaseScanProcessor {
is ScanCardException.ChainException -> proceedWithScanChainException(
exception,
onWalletNotCreated,
onFailure,
)
is ScanCardException.UnknownException,
is ScanCardException.UserCancelled,
@ -109,16 +120,12 @@ internal object UseCaseScanProcessor {
private suspend fun proceedWithScanChainException(
exception: ScanCardException.ChainException,
onWalletNotCreated: suspend () -> Unit,
onFailure: suspend (error: TangemError) -> Unit,
) {
when (exception) {
is ScanChainException.OnboardingNeeded -> {
navigateTo(exception.onboardingRoute)
onWalletNotCreated()
}
is ScanChainException.DisclaimerWasCanceled -> {
onFailure(scanCardExceptionConverter.convertBack(exception))
}
}
}
@ -136,6 +143,6 @@ internal object UseCaseScanProcessor {
private suspend inline fun navigateTo(route: AppRoute) {
delay(DELAY_SDK_DIALOG_CLOSE)
store.dispatchNavigationAction { push(route) }
appRouter.push(route)
}
}

View file

@ -3,18 +3,16 @@ package com.tangem.tap.domain.scanCard.chains
import arrow.core.left
import arrow.core.right
import com.tangem.common.routing.AppRoute
import com.tangem.core.analytics.utils.TrackingContextProxy
import com.tangem.domain.card.ScanCardException
import com.tangem.domain.card.common.util.twinsIsTwinned
import com.tangem.domain.core.chain.Chain
import com.tangem.domain.core.chain.ResultChain
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.common.redux.AppState
import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
import com.tangem.tap.features.onboarding.OnboardingHelper
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.utils.extensions.DELAY_SDK_DIALOG_CLOSE
import kotlinx.coroutines.delay
import org.rekotlin.Store
/**
* Handles the verification process to determine if the scanned card requires onboarding.
@ -22,19 +20,17 @@ import org.rekotlin.Store
* Returns:
* - [ScanChainException.OnboardingNeeded] if onboarding required.
*
* @param store the [Store] that holds the state of the app.
*
* @see Chain for more information about the Chain interface.
*/
class CheckForOnboardingChain(
private val store: Store<AppState>,
private val trackingContextProxy: TrackingContextProxy,
private val wasTwinsOnboardingShownUseCase: WasTwinsOnboardingShownUseCase,
private val onboardingHelper: OnboardingHelper,
) : ResultChain<ScanCardException, ScanResponse>() {
override suspend fun launch(previousChainResult: ScanResponse): ScanChainResult {
val trackingContextProxy = store.inject(DaggerGraphState::trackingContextProxy)
return when {
OnboardingHelper.isOnboardingCase(previousChainResult) -> {
onboardingHelper.isOnboardingCase(previousChainResult) -> {
trackingContextProxy.addContext(previousChainResult)
ScanChainException.OnboardingNeeded(
AppRoute.Onboarding(
@ -46,8 +42,7 @@ class CheckForOnboardingChain(
else -> {
trackingContextProxy.setContext(previousChainResult)
val wasTwinsOnboardingShown = store.inject(DaggerGraphState::wasTwinsOnboardingShownUseCase)
.invokeSync()
val wasTwinsOnboardingShown = wasTwinsOnboardingShownUseCase.invokeSync()
// If twins was twinned previously but twins welcome not shown
if (previousChainResult.twinsIsTwinned() && !wasTwinsOnboardingShown) {

View file

@ -2,33 +2,30 @@ package com.tangem.tap.domain.scanCard.chains
import arrow.core.right
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.domain.card.ScanCardException
import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.core.chain.Chain
import com.tangem.domain.core.chain.ResultChain
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.common.extensions.dispatchNavigationAction
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.features.disclaimer.createDisclaimer
import org.rekotlin.Store
/**
* Handles disclaimer display after the card scanning operation.
*
* Returns [ScanChainException.DisclaimerWasCanceled] if the disclaimer is dismissed by the user.
*
* @param store the [Store] that holds the state of the app, used here to dispatch actions related to disclaimers.
* @param disclaimerWillShow an optional function to be invoked when a disclaimer is about to be shown. Default is an
* empty function.
*
* @see Chain for more information about the Chain interface.
*/
internal class DisclaimerChain(
private val store: Store<AppState>,
private val appRouter: AppRouter,
private val cardRepository: CardRepository,
private val disclaimerWillShow: () -> Unit = {},
) : ResultChain<ScanCardException, ScanResponse>() {
override suspend fun launch(previousChainResult: ScanResponse): ScanChainResult {
val disclaimer = previousChainResult.card.createDisclaimer()
val disclaimer = previousChainResult.card.createDisclaimer(cardRepository)
return if (disclaimer.isAccepted()) {
previousChainResult.right()
@ -36,9 +33,7 @@ internal class DisclaimerChain(
disclaimerWillShow()
// TODO: [REDACTED_JIRA]
store.dispatchNavigationAction {
push(route = AppRoute.Disclaimer(isTosAccepted = false))
}
appRouter.push(route = AppRoute.Disclaimer(isTosAccepted = false))
previousChainResult.right()
}

View file

@ -5,15 +5,6 @@ import com.tangem.domain.card.ScanCardException
sealed class ScanChainException : ScanCardException.ChainException() {
/**
* May be returned from [DisclaimerChain]
* */
class DisclaimerWasCanceled : ScanChainException() {
@Suppress("UnusedPrivateMember")
private fun readResolve(): Any = DisclaimerWasCanceled()
}
/**
* May be returned from [CheckForOnboardingChain]
*

View file

@ -33,7 +33,6 @@ internal class ScanCardExceptionConverter : TwoWayConverter<TangemError, ScanCar
private fun concertScanChainException(value: ScanCardException.ChainException): TangemSdkError {
return when (val e = value as? ScanChainException) {
is ScanChainException.DisclaimerWasCanceled -> TangemSdkError.UserCancelled()
is ScanChainException.OnboardingNeeded,
null,
-> TangemSdkError.ExceptionError(e?.cause)

View file

@ -23,6 +23,7 @@ import com.tangem.crypto.bip39.DefaultMnemonic
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
@ -72,6 +73,7 @@ internal class DefaultTangemSdkManager(
private val dynamicAddressesFeatureToggles: DynamicAddressesFeatureToggles,
private val blockchainToDeriveFinder: BlockchainToDeriveFinder,
private val analyticsErrorHandler: AnalyticsErrorHandler,
private val cardRepository: CardRepository,
) : TangemSdkManager {
private val tangemSdk: TangemSdk
@ -146,6 +148,7 @@ internal class DefaultTangemSdkManager(
shouldCheckIsAlreadyActivated = shouldCheckIsAlreadyActivated,
isDynamicAddressesEnabled = dynamicAddressesFeatureToggles.isDynamicAddressesEnabled,
onboardingV2FeatureToggles = onboardingV2FeatureToggles,
cardRepository = cardRepository,
),
cardId = cardId,
initialMessage = message,
@ -453,6 +456,7 @@ internal class DefaultTangemSdkManager(
twinPublicKey = secondCardPublicKey,
issuerKeys = issuerKeyPair,
isDynamicAddressesEnabled = dynamicAddressesFeatureToggles.isDynamicAddressesEnabled,
cardRepository = cardRepository,
),
cardId = cardId,
initialMessage = initialMessage,

View file

@ -21,6 +21,7 @@ import com.tangem.domain.card.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.card.common.TapWorkarounds.isVisa
import com.tangem.domain.card.common.TwinsHelper
import com.tangem.domain.card.common.visa.VisaUtilities
import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.CardDTO.Companion.RING_BATCH_IDS
import com.tangem.domain.models.scan.CardDTO.Companion.RING_BATCH_PREFIX
@ -34,13 +35,10 @@ import com.tangem.operations.backup.StartPrimaryCardLinkingTask
import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask
import com.tangem.operations.files.ReadFilesTask
import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.domain.TapSdkError
import com.tangem.tap.domain.visa.VisaCardScanHandler
import com.tangem.tap.mainScope
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.scope
import com.tangem.tap.store
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@ -53,6 +51,7 @@ internal class ScanProductTask(
private val onboardingV2FeatureToggles: OnboardingV2FeatureToggles?,
private val shouldCheckIsAlreadyActivated: Boolean,
private val isDynamicAddressesEnabled: Boolean,
private val cardRepository: CardRepository,
override val allowsRequestAccessCodeFromRepository: Boolean = false,
) : CardSessionRunnable<ScanResponse> {
@ -80,7 +79,11 @@ internal class ScanProductTask(
readVisaCard(
session = session,
cardDto = cardDto,
scanWalletProcessor = ScanWalletProcessor(blockchainToDeriveFinder, isDynamicAddressesEnabled),
scanWalletProcessor = ScanWalletProcessor(
blockchainToDeriveFinder = blockchainToDeriveFinder,
isDynamicAddressesEnabled = isDynamicAddressesEnabled,
cardRepository = cardRepository,
),
callback = callback,
)
return
@ -88,7 +91,11 @@ internal class ScanProductTask(
val commandProcessor = when {
cardDto.isTangemTwins -> ScanTwinProcessor()
else -> ScanWalletProcessor(blockchainToDeriveFinder, isDynamicAddressesEnabled)
else -> ScanWalletProcessor(
blockchainToDeriveFinder = blockchainToDeriveFinder,
isDynamicAddressesEnabled = isDynamicAddressesEnabled,
cardRepository = cardRepository,
)
}
commandProcessor.proceed(cardDto, session) { processorResult ->
when (processorResult) {
@ -114,7 +121,7 @@ internal class ScanProductTask(
return if (shouldCheckIsAlreadyActivated) {
PreflightReadMode.FullCardReadWithAccessCodeCheck
} else {
return super.preflightReadMode()
super.preflightReadMode()
}
}
@ -171,6 +178,7 @@ internal class ScanProductTask(
private class ScanWalletProcessor(
private val blockchainToDeriveFinder: BlockchainToDeriveFinder?,
private val isDynamicAddressesEnabled: Boolean,
private val cardRepository: CardRepository,
) : ProductCommandProcessor<ScanResponse> {
var primaryCard: PrimaryCard? = null
@ -262,8 +270,7 @@ private class ScanWalletProcessor(
callback: (result: CompletionResult<ScanResponse>) -> Unit,
) {
mainScope.launch {
val isActivationInProgress = store.inject(DaggerGraphState::cardRepository)
.isActivationInProgress(card.cardId)
val isActivationInProgress = cardRepository.isActivationInProgress(card.cardId)
@Suppress("ComplexCondition")
if (card.backupStatus == CardDTO.BackupStatus.NoBackup &&

View file

@ -4,6 +4,7 @@ import com.tangem.common.CompletionResult
import com.tangem.common.KeyPair
import com.tangem.common.core.CardSession
import com.tangem.common.core.CardSessionRunnable
import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.operations.PreflightReadMode
import com.tangem.operations.PreflightReadTask
@ -13,6 +14,7 @@ class FinalizeTwinTask(
private val twinPublicKey: ByteArray,
private val issuerKeys: KeyPair,
private val isDynamicAddressesEnabled: Boolean,
private val cardRepository: CardRepository,
) : CardSessionRunnable<ScanResponse> {
override val allowsRequestAccessCodeFromRepository: Boolean = false
@ -35,6 +37,7 @@ class FinalizeTwinTask(
shouldCheckIsAlreadyActivated = false,
isDynamicAddressesEnabled = isDynamicAddressesEnabled,
onboardingV2FeatureToggles = null,
cardRepository = cardRepository,
).run(session, callback)
is CompletionResult.Failure ->
callback(CompletionResult.Failure(readResult.error))

View file

@ -1,18 +1,15 @@
package com.tangem.tap.features.disclaimer
import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.models.scan.CardDTO
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.store
import java.util.Locale
fun CardDTO.createDisclaimer(): Disclaimer {
val dataProvider = provideDisclaimerDataProvider(cardId)
fun CardDTO.createDisclaimer(cardRepository: CardRepository): Disclaimer {
val dataProvider = provideDisclaimerDataProvider(cardId, cardRepository)
return TangemDisclaimer(dataProvider)
}
private fun provideDisclaimerDataProvider(cardId: String): DisclaimerDataProvider {
val cardRepository = store.inject(DaggerGraphState::cardRepository)
private fun provideDisclaimerDataProvider(cardId: String, cardRepository: CardRepository): DisclaimerDataProvider {
return object : DisclaimerDataProvider {
override fun getLanguage(): String = Locale.getDefault().language
override fun getCardId(): String = cardId

View file

@ -40,7 +40,6 @@ import com.tangem.domain.wallets.usecase.UpdateRemoteWalletsInfoUseCase
import com.tangem.feature.swap.analytics.StoriesEvents
import com.tangem.security.DeviceSecurityInfoProvider
import com.tangem.tap.network.exchangeServices.SellService
import com.tangem.tap.proxy.AppStateHolder
import com.tangem.tap.routing.configurator.AppRouterConfig
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.logging.TangemLogger
@ -77,7 +76,6 @@ internal class MainViewModel @Inject constructor(
private val sendPushTokenUseCase: SendPushTokenUseCase,
private val apiConfigsManager: ApiConfigsManager,
private val multiQuoteUpdater: MultiQuoteUpdater,
private val appStateHolder: AppStateHolder,
private val appRouterConfig: AppRouterConfig,
private val sellService: SellService,
private val deviceSecurityInfoProvider: DeviceSecurityInfoProvider,
@ -176,8 +174,6 @@ internal class MainViewModel @Inject constructor(
private fun initializeOffRamp() {
viewModelScope.launch {
appStateHolder.sellService = sellService
sellService.update()
}
}

View file

@ -2,19 +2,18 @@ package com.tangem.tap.features.onboarding
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.card.common.util.twinsIsTwinned
import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.store
import javax.inject.Inject
import javax.inject.Singleton
/**
[REDACTED_AUTHOR]
*/
object OnboardingHelper {
@Singleton
class OnboardingHelper @Inject constructor(
private val cardRepository: CardRepository,
) {
suspend fun isOnboardingCase(response: ScanResponse): Boolean {
val cardRepository = store.inject(DaggerGraphState::cardRepository)
val cardId = response.card.cardId
return when {
@ -22,7 +21,7 @@ object OnboardingHelper {
// if (response.visaCardActivationStatus == null) error("Visa card activation status is null")
//
// response.visaCardActivationStatus !is VisaCardActivationStatus.Activated
return true
true
}
response.cardTypesResolver.isTangemTwins() -> {