Updated on 2026-08-14
This commit is contained in:
parent
d050fa2654
commit
bbb3c9d6bb
16 changed files with 112 additions and 115 deletions
|
|
@ -28,6 +28,7 @@ import com.tangem.domain.apptheme.GetAppThemeModeUseCase
|
|||
import com.tangem.domain.apptheme.repository.AppThemeModeRepository
|
||||
import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository
|
||||
import com.tangem.domain.card.ScanCardProcessor
|
||||
import com.tangem.domain.card.ScanFailsRequester
|
||||
import com.tangem.domain.card.repository.CardRepository
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
|
||||
|
|
@ -151,4 +152,6 @@ interface ApplicationEntryPoint {
|
|||
fun getAppsFlyerClientFactory(): AppsFlyerClient.Factory
|
||||
|
||||
fun getCustomerIoFeatureToggles(): CustomerIoFeatureToggles
|
||||
|
||||
fun getScanFailsRequester(): ScanFailsRequester
|
||||
}
|
||||
|
|
@ -239,6 +239,9 @@ open class TangemApplication : Application(), ImageLoaderFactory, Configuration.
|
|||
private val customerIoFeatureToggles: CustomerIoFeatureToggles
|
||||
get() = entryPoint.getCustomerIoFeatureToggles()
|
||||
|
||||
private val scanFailsRequester
|
||||
get() = entryPoint.getScanFailsRequester()
|
||||
|
||||
// endregion
|
||||
|
||||
private val appScope = MainScope()
|
||||
|
|
@ -368,6 +371,7 @@ open class TangemApplication : Application(), ImageLoaderFactory, Configuration.
|
|||
userWalletsListRepository = userWalletsListRepository,
|
||||
tangemHotSdk = tangemHotSdk,
|
||||
trackingContextProxy = trackingContextProxy,
|
||||
scanFailsRequester = scanFailsRequester,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@ import com.tangem.common.CompletionResult
|
|||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.card.ScanFailsRequester
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.tap.common.extensions.dispatchDialogShow
|
||||
import com.tangem.tap.common.extensions.dispatchWithMain
|
||||
import com.tangem.tap.common.extensions.inject
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
|
|
@ -54,12 +53,14 @@ private fun handleFailureChooseBehaviour(
|
|||
store.dispatch(GlobalAction.ScanFailsCounter.Increment)
|
||||
if (store.state.globalState.scanCardFailsCounter >= 2) {
|
||||
val scanFailsSource = when (analyticsSource) {
|
||||
is AnalyticsParam.ScreensSources.SignIn -> StateDialog.ScanFailsSource.SIGN_IN
|
||||
is AnalyticsParam.ScreensSources.Settings -> StateDialog.ScanFailsSource.SETTINGS
|
||||
is AnalyticsParam.ScreensSources.Intro -> StateDialog.ScanFailsSource.INTRO
|
||||
else -> StateDialog.ScanFailsSource.MAIN
|
||||
is AnalyticsParam.ScreensSources.SignIn -> ScanFailsRequester.Source.SIGN_IN
|
||||
is AnalyticsParam.ScreensSources.Settings -> ScanFailsRequester.Source.SETTINGS
|
||||
is AnalyticsParam.ScreensSources.Intro -> ScanFailsRequester.Source.INTRO
|
||||
else -> ScanFailsRequester.Source.MAIN
|
||||
}
|
||||
scope.launch {
|
||||
store.inject(DaggerGraphState::scanFailsRequester).show(scanFailsSource)
|
||||
}
|
||||
store.dispatchDialogShow(StateDialog.ScanFailsDialog(scanFailsSource))
|
||||
}
|
||||
} else {
|
||||
store.dispatch(GlobalAction.ScanFailsCounter.Reset)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.tap.domain.model
|
||||
|
||||
import com.tangem.domain.features.addCustomToken.CustomCurrency
|
||||
import com.tangem.tap.common.redux.global.CryptoCurrencyName
|
||||
import com.tangem.blockchain.common.Blockchain as SdkBlockchain
|
||||
import com.tangem.blockchain.common.Token as SdkToken
|
||||
|
|
@ -29,20 +28,4 @@ sealed interface Currency {
|
|||
) : Currency {
|
||||
override val currencySymbol: CryptoCurrencyName = blockchain.currency
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromCustomCurrency(customCurrency: CustomCurrency): Currency {
|
||||
return when (customCurrency) {
|
||||
is CustomCurrency.CustomBlockchain -> Blockchain(
|
||||
blockchain = customCurrency.network,
|
||||
derivationPath = customCurrency.derivationPath?.rawPath,
|
||||
)
|
||||
is CustomCurrency.CustomToken -> Token(
|
||||
token = customCurrency.token,
|
||||
blockchain = customCurrency.network,
|
||||
derivationPath = customCurrency.derivationPath?.rawPath,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,18 +9,19 @@ import com.tangem.core.analytics.models.AnalyticsParam
|
|||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.domain.card.ScanCardException
|
||||
import com.tangem.domain.card.ScanFailsRequester
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.tap.common.analytics.events.TangemSdkErrorEvent
|
||||
import com.tangem.tap.common.extensions.dispatchDialogShow
|
||||
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.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.utils.extensions.DELAY_SDK_DIALOG_CLOSE
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
internal object UseCaseScanProcessor {
|
||||
private val scanCardExceptionConverter = ScanCardExceptionConverter()
|
||||
|
|
@ -57,7 +58,7 @@ internal object UseCaseScanProcessor {
|
|||
val chains = buildList {
|
||||
add(
|
||||
FailedScansCounterChain(
|
||||
{ showMaxUnsuccessfulScansReachedDialog(analyticsSource) },
|
||||
{ showScanFailsDialog(analyticsSource) },
|
||||
),
|
||||
)
|
||||
add(AnalyticsChain(Basic.CardWasScanned(analyticsSource)))
|
||||
|
|
@ -71,14 +72,16 @@ internal object UseCaseScanProcessor {
|
|||
)
|
||||
}
|
||||
|
||||
private fun showMaxUnsuccessfulScansReachedDialog(source: AnalyticsParam.ScreensSources) {
|
||||
private fun showScanFailsDialog(source: AnalyticsParam.ScreensSources) {
|
||||
val scanFailsSource = when (source) {
|
||||
is AnalyticsParam.ScreensSources.SignIn -> StateDialog.ScanFailsSource.SIGN_IN
|
||||
is AnalyticsParam.ScreensSources.Settings -> StateDialog.ScanFailsSource.SETTINGS
|
||||
is AnalyticsParam.ScreensSources.Intro -> StateDialog.ScanFailsSource.INTRO
|
||||
else -> StateDialog.ScanFailsSource.MAIN
|
||||
is AnalyticsParam.ScreensSources.SignIn -> ScanFailsRequester.Source.SIGN_IN
|
||||
is AnalyticsParam.ScreensSources.Settings -> ScanFailsRequester.Source.SETTINGS
|
||||
is AnalyticsParam.ScreensSources.Intro -> ScanFailsRequester.Source.INTRO
|
||||
else -> ScanFailsRequester.Source.MAIN
|
||||
}
|
||||
scope.launch {
|
||||
store.inject(DaggerGraphState::scanFailsRequester).show(scanFailsSource)
|
||||
}
|
||||
store.dispatchDialogShow(StateDialog.ScanFailsDialog(scanFailsSource))
|
||||
}
|
||||
|
||||
private suspend fun proceedWithException(
|
||||
|
|
|
|||
|
|
@ -13,11 +13,7 @@ import com.tangem.domain.feedback.models.FeedbackEmailType
|
|||
import com.tangem.tap.common.analytics.events.ScanFailsDialogAnalytics
|
||||
import com.tangem.tap.features.scanfails.ui.ScanFailsUM
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -61,34 +62,43 @@ internal fun ScanFailsDialogContent(state: ScanFailsUM) {
|
|||
text = stringResourceSafe(R.string.alert_button_how_to_scan),
|
||||
onClick = state.onHowToScan,
|
||||
color = TangemTheme.colors.text.accent,
|
||||
modifier = Modifier.testTag(ScanFailsDialogTestTags.HOW_TO_SCAN_BUTTON),
|
||||
)
|
||||
|
||||
DialogTextButton(
|
||||
text = stringResourceSafe(R.string.alert_button_request_support),
|
||||
onClick = state.onRequestSupport,
|
||||
color = TangemTheme.colors.text.accent,
|
||||
modifier = Modifier.testTag(ScanFailsDialogTestTags.REQUEST_SUPPORT_BUTTON),
|
||||
)
|
||||
|
||||
DialogTextButton(
|
||||
text = stringResourceSafe(R.string.common_cancel),
|
||||
onClick = state.onDismiss,
|
||||
color = TangemTheme.colors.text.warning,
|
||||
modifier = Modifier.testTag(ScanFailsDialogTestTags.CANCEL_BUTTON),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DialogTextButton(text: String, onClick: () -> Unit, color: Color) {
|
||||
private fun DialogTextButton(text: String, onClick: () -> Unit, color: Color, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
text = text.uppercase(),
|
||||
color = color,
|
||||
style = TangemTheme.typography.button,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.clickable(onClick = onClick),
|
||||
modifier = modifier.clickable(onClick = onClick),
|
||||
)
|
||||
}
|
||||
|
||||
object ScanFailsDialogTestTags {
|
||||
const val HOW_TO_SCAN_BUTTON = "scan_fails_how_to_scan_button"
|
||||
const val REQUEST_SUPPORT_BUTTON = "scan_fails_request_support_button"
|
||||
const val CANCEL_BUTTON = "scan_fails_cancel_button"
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import com.tangem.domain.apptheme.repository.AppThemeModeRepository
|
|||
import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository
|
||||
import com.tangem.domain.card.ScanCardProcessor
|
||||
import com.tangem.domain.card.ScanCardUseCase
|
||||
import com.tangem.domain.card.ScanFailsRequester
|
||||
import com.tangem.domain.card.repository.CardRepository
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
|
|
@ -73,4 +74,5 @@ data class DaggerGraphState(
|
|||
val userWalletsListRepository: UserWalletsListRepository? = null,
|
||||
val tangemHotSdk: TangemHotSdk? = null,
|
||||
val trackingContextProxy: TrackingContextProxy? = null,
|
||||
val scanFailsRequester: ScanFailsRequester? = null,
|
||||
) : StateType
|
||||
|
|
@ -52,6 +52,7 @@ internal fun RootContent(
|
|||
wcContent: @Composable (modifier: Modifier) -> Unit,
|
||||
hotAccessCodeContent: @Composable (modifier: Modifier) -> Unit,
|
||||
rootDetectedWarningContent: @Composable (modifier: Modifier) -> Unit,
|
||||
scanFailsContent: @Composable (modifier: Modifier) -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
|
|
@ -89,6 +90,8 @@ internal fun RootContent(
|
|||
|
||||
rootDetectedWarningContent(Modifier.fillMaxSize())
|
||||
|
||||
scanFailsContent(Modifier.fillMaxSize())
|
||||
|
||||
TangemSnackbarHost(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ import com.tangem.hot.sdk.android.create
|
|||
import com.tangem.sdk.api.BackupServiceHolder
|
||||
import com.tangem.tap.common.SnackbarHandler
|
||||
import com.tangem.tap.common.analytics.events.Onboarding
|
||||
import com.tangem.tap.features.scanfails.ScanFailsComponent
|
||||
import com.tangem.tap.features.scanfails.ScanFailsRequesterProxy
|
||||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.hot.TangemHotSDKProxy
|
||||
import com.tangem.tap.features.root.RootDetectedWarningComponent
|
||||
|
|
@ -75,6 +77,8 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
|
|||
private val cardRepository: CardRepository,
|
||||
private val onboardingRepository: OnboardingRepository,
|
||||
private val trackingContextProxy: TrackingContextProxy,
|
||||
private val scanFailsComponentFactory: ScanFailsComponent.Factory,
|
||||
private val scanFailsRequesterProxy: ScanFailsRequesterProxy,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
|
||||
private val backupServiceHolder: BackupServiceHolder,
|
||||
|
|
@ -97,6 +101,11 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
|
|||
.create(child("rootDetectedWarningComponent"), Unit)
|
||||
}
|
||||
|
||||
private val scanFailsComponent: ScanFailsComponent by lazy {
|
||||
scanFailsComponentFactory
|
||||
.create(child("scanFailsComponent"), Unit)
|
||||
}
|
||||
|
||||
private val navigation = navigationProvider.getOrCreateTyped<AppRoute>()
|
||||
|
||||
private val stack: Value<ChildStack<AppRoute, Child>> = childStack(
|
||||
|
|
@ -197,6 +206,7 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
|
|||
wcContent = { wcRoutingComponent.Content(it) },
|
||||
hotAccessCodeContent = { hotAccessCodeRequestComponent.Content(it) },
|
||||
rootDetectedWarningContent = { rootDetectedWarningComponent.Content(it) },
|
||||
scanFailsContent = { scanFailsComponent.Content(it) },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -240,10 +250,12 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
|
|||
onCreate = {
|
||||
tangemHotSDKProxy.sdkState.value = TangemHotSdk.create(activity)
|
||||
hotAccessCodeRequesterProxy.componentRequester.value = hotAccessCodeRequestComponent
|
||||
scanFailsRequesterProxy.componentRequester.value = scanFailsComponent
|
||||
},
|
||||
onDestroy = {
|
||||
tangemHotSDKProxy.sdkState.value = null
|
||||
hotAccessCodeRequesterProxy.componentRequester.value = null
|
||||
scanFailsRequesterProxy.componentRequester.value = null
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue