Updated on 2026-08-14
This commit is contained in:
commit
48f20925f6
53 changed files with 541 additions and 76 deletions
|
|
@ -7,6 +7,7 @@ import com.tangem.core.navigation.AppScreen
|
|||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.domain.wallets.legacy.asLockable
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.dispatchWithMain
|
||||
import kotlinx.coroutines.*
|
||||
import timber.log.Timber
|
||||
import kotlin.time.Duration
|
||||
|
|
@ -103,12 +104,14 @@ internal class LockUserWalletsTimer(
|
|||
|- Millis passed: ${currentTime - startTime}
|
||||
""".trimIndent(),
|
||||
)
|
||||
userWalletsListManager.lock()
|
||||
|
||||
if (preferencesStorage.wasApplicationStopped) {
|
||||
preferencesStorage.shouldOpenWelcomeScreenOnResume = true
|
||||
} else {
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Welcome))
|
||||
store.dispatchWithMain(NavigationAction.PopBackTo(AppScreen.Welcome))
|
||||
}
|
||||
|
||||
userWalletsListManager.lock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,12 +19,14 @@ import androidx.core.content.ContextCompat
|
|||
import androidx.core.os.bundleOf
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.flowWithLifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import arrow.core.getOrElse
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import com.google.android.material.snackbar.BaseTransientBottomBar
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.deeplink.DeepLinksRegistry
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
|
|
@ -35,10 +37,13 @@ import com.tangem.data.card.sdk.CardSdkLifecycleObserver
|
|||
import com.tangem.domain.apptheme.model.AppThemeMode
|
||||
import com.tangem.domain.card.ScanCardUseCase
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.tokens.GetPolkadotCheckHasImmortalUseCase
|
||||
import com.tangem.domain.tokens.GetPolkadotCheckHasResetUseCase
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManagerFeatureToggles
|
||||
import com.tangem.domain.wallets.legacy.asLockable
|
||||
import com.tangem.feature.qrscanning.QrScanningRouter
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
|
||||
import com.tangem.features.managetokens.navigation.ManageTokensUi
|
||||
import com.tangem.features.send.api.navigation.SendRouter
|
||||
import com.tangem.features.tester.api.TesterRouter
|
||||
|
|
@ -95,6 +100,7 @@ val userWalletsListManagerSafe: UserWalletsListManager?
|
|||
val userWalletsListManager: UserWalletsListManager
|
||||
get() = userWalletsListManagerSafe!!
|
||||
|
||||
@Suppress("LargeClass")
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbackHolder {
|
||||
|
||||
|
|
@ -141,6 +147,15 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
@Inject
|
||||
lateinit var userWalletsListManagerFeatureToggles: UserWalletsListManagerFeatureToggles
|
||||
|
||||
@Inject
|
||||
lateinit var getPolkadotCheckHasResetUseCase: GetPolkadotCheckHasResetUseCase
|
||||
|
||||
@Inject
|
||||
lateinit var getPolkadotCheckHasImmortalUseCase: GetPolkadotCheckHasImmortalUseCase
|
||||
|
||||
@Inject
|
||||
lateinit var analyticsEventsHandler: AnalyticsEventHandler
|
||||
|
||||
internal val viewModel: MainViewModel by viewModels()
|
||||
|
||||
private lateinit var appThemeModeFlow: SharedFlow<AppThemeMode?>
|
||||
|
|
@ -168,6 +183,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
|
||||
checkForNotificationPermission()
|
||||
observeStateUpdates()
|
||||
observePolkadotAccountHealthCheck()
|
||||
|
||||
if (intent != null) {
|
||||
deepLinksRegistry.launch(intent)
|
||||
|
|
@ -457,4 +473,25 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.POST_NOTIFICATIONS), 0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun observePolkadotAccountHealthCheck() {
|
||||
lifecycleScope.launch {
|
||||
getPolkadotCheckHasResetUseCase()
|
||||
.flowWithLifecycle(lifecycle, minActiveState = Lifecycle.State.CREATED)
|
||||
.distinctUntilChanged()
|
||||
.collect {
|
||||
analyticsEventsHandler.send(WalletScreenAnalyticsEvent.Token.PolkadotAccountReset(it.second))
|
||||
}
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
getPolkadotCheckHasImmortalUseCase()
|
||||
.flowWithLifecycle(lifecycle, minActiveState = Lifecycle.State.CREATED)
|
||||
.distinctUntilChanged()
|
||||
.collect {
|
||||
analyticsEventsHandler.send(
|
||||
WalletScreenAnalyticsEvent.Token.PolkadotImmortalTransactions(it.second),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -46,7 +46,7 @@ class DialogManager : StoreSubscriber<GlobalState> {
|
|||
|
||||
dialog = when (state.dialog) {
|
||||
is AppDialog.SimpleOkDialogRes -> SimpleOkDialog.create(state.dialog, context)
|
||||
is StateDialog.ScanFailsDialog -> ScanFailsDialog.create(context)
|
||||
is StateDialog.ScanFailsDialog -> ScanFailsDialog.create(context, state.dialog.source)
|
||||
is AppDialog.AddressInfoDialog -> AddressInfoBottomSheetDialog(state.dialog, context)
|
||||
is AppDialog.TestActionsDialog -> TestActionsBottomSheetDialog(state.dialog, context)
|
||||
is AppDialog.RussianCardholdersWarningDialog -> RussianCardholdersWarningBottomSheetDialog(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.common.redux.global
|
|||
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.datasource.config.models.ChatConfig
|
||||
|
|
@ -51,7 +52,11 @@ sealed class GlobalAction : Action {
|
|||
}
|
||||
|
||||
object ScanFailsCounter {
|
||||
data class ChooseBehavior(val result: CompletionResult<ScanResponse>) : GlobalAction()
|
||||
data class ChooseBehavior(
|
||||
val result: CompletionResult<ScanResponse>,
|
||||
val analyticsSource: AnalyticsParam.ScreensSources,
|
||||
) : GlobalAction()
|
||||
|
||||
object Reset : GlobalAction()
|
||||
object Increment : GlobalAction()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.common.CompletionResult
|
|||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
import com.tangem.datasource.config.models.Config
|
||||
|
|
@ -54,14 +55,7 @@ private fun handleAction(action: Action, appState: () -> AppState?) {
|
|||
when (action.result) {
|
||||
is CompletionResult.Success -> store.dispatch(GlobalAction.ScanFailsCounter.Reset)
|
||||
is CompletionResult.Failure -> {
|
||||
if (action.result.error is TangemSdkError.UserCancelled) {
|
||||
store.dispatch(GlobalAction.ScanFailsCounter.Increment)
|
||||
if (store.state.globalState.scanCardFailsCounter >= 2) {
|
||||
store.dispatchDialogShow(StateDialog.ScanFailsDialog)
|
||||
}
|
||||
} else {
|
||||
store.dispatch(GlobalAction.ScanFailsCounter.Reset)
|
||||
}
|
||||
handleFailureChooseBehaviour(action.result, action.analyticsSource)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -184,6 +178,26 @@ private fun handleAction(action: Action, appState: () -> AppState?) {
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleFailureChooseBehaviour(
|
||||
result: CompletionResult.Failure<ScanResponse>,
|
||||
analyticsSource: AnalyticsParam.ScreensSources,
|
||||
) {
|
||||
if (result.error is TangemSdkError.UserCancelled) {
|
||||
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
|
||||
}
|
||||
store.dispatchDialogShow(StateDialog.ScanFailsDialog(scanFailsSource))
|
||||
}
|
||||
} else {
|
||||
store.dispatch(GlobalAction.ScanFailsCounter.Reset)
|
||||
}
|
||||
}
|
||||
|
||||
private fun restoreAppCurrency() {
|
||||
scope.launch {
|
||||
val currency = store.inject(DaggerGraphState::appCurrencyRepository)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ import android.content.Context
|
|||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
import com.tangem.tap.common.extensions.dispatchDialogHide
|
||||
import com.tangem.tap.common.feedback.ScanFailsEmail
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
|
|
@ -15,12 +17,18 @@ import com.tangem.wallet.R
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
object ScanFailsDialog {
|
||||
fun create(context: Context): AlertDialog {
|
||||
fun create(context: Context, source: StateDialog.ScanFailsSource): AlertDialog {
|
||||
return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog).apply {
|
||||
setTitle(context.getString(R.string.common_warning))
|
||||
setMessage(R.string.alert_troubleshooting_scan_card_title)
|
||||
setPositiveButton(R.string.alert_button_request_support) { _, _ ->
|
||||
Analytics.send(Basic.ButtonSupport())
|
||||
val sourceAnalytics = when (source) {
|
||||
StateDialog.ScanFailsSource.MAIN -> AnalyticsParam.ScreensSources.Main
|
||||
StateDialog.ScanFailsSource.SIGN_IN -> AnalyticsParam.ScreensSources.SignIn
|
||||
StateDialog.ScanFailsSource.SETTINGS -> AnalyticsParam.ScreensSources.Settings
|
||||
StateDialog.ScanFailsSource.INTRO -> AnalyticsParam.ScreensSources.Intro
|
||||
}
|
||||
Analytics.send(Basic.ButtonSupport(sourceAnalytics))
|
||||
store.dispatch(GlobalAction.SendEmail(ScanFailsEmail()))
|
||||
}
|
||||
setNeutralButton(R.string.common_cancel) { _, _ -> }
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ import android.content.Context
|
|||
import com.tangem.domain.card.ScanCardUseCase
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.tokens.GetPolkadotCheckHasImmortalUseCase
|
||||
import com.tangem.domain.tokens.GetPolkadotCheckHasResetUseCase
|
||||
import com.tangem.domain.tokens.repository.PolkadotAccountHealthCheckRepository
|
||||
import com.tangem.tap.domain.TangemSdkManager
|
||||
import com.tangem.tap.domain.scanCard.repository.DefaultScanCardRepository
|
||||
import com.tangem.tap.network.exchangeServices.DefaultRampManager
|
||||
|
|
@ -57,4 +60,20 @@ internal object ActivityModule {
|
|||
fun provideActivityDelayedWorkCoroutineScope(): CoroutineScope {
|
||||
return CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetPolkadotCheckHasResetUseCase(
|
||||
polkadotAccountHealthCheckRepository: PolkadotAccountHealthCheckRepository,
|
||||
): GetPolkadotCheckHasResetUseCase {
|
||||
return GetPolkadotCheckHasResetUseCase(polkadotAccountHealthCheckRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetPolkadotCheckHasImmortalUseCase(
|
||||
polkadotAccountHealthCheckRepository: PolkadotAccountHealthCheckRepository,
|
||||
): GetPolkadotCheckHasImmortalUseCase {
|
||||
return GetPolkadotCheckHasImmortalUseCase(polkadotAccountHealthCheckRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -343,4 +343,12 @@ internal object TokensDomainModule {
|
|||
): IsAmountSubtractAvailableUseCase {
|
||||
return IsAmountSubtractAvailableUseCase(currenciesRepository, dispatchers)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun provideRunPolkadotAccountHealthCheckUseCase(
|
||||
repository: PolkadotAccountHealthCheckRepository,
|
||||
): RunPolkadotAccountHealthCheckUseCase {
|
||||
return RunPolkadotAccountHealthCheckUseCase(repository)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ package com.tangem.tap.domain.scanCard
|
|||
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
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
|
||||
|
|
@ -27,7 +27,7 @@ internal class DefaultScanCardProcessor : ScanCardProcessor {
|
|||
|
||||
@Suppress("LongParameterList")
|
||||
override suspend fun scan(
|
||||
analyticsEvent: AnalyticsEvent?,
|
||||
analyticsSource: AnalyticsParam.ScreensSources,
|
||||
cardId: String?,
|
||||
onProgressStateChange: suspend (showProgress: Boolean) -> Unit,
|
||||
onWalletNotCreated: suspend () -> Unit,
|
||||
|
|
@ -37,7 +37,7 @@ internal class DefaultScanCardProcessor : ScanCardProcessor {
|
|||
) {
|
||||
if (isNewCardScanningEnabled) {
|
||||
UseCaseScanProcessor.scan(
|
||||
analyticsEvent,
|
||||
analyticsSource,
|
||||
cardId,
|
||||
onProgressStateChange,
|
||||
onWalletNotCreated,
|
||||
|
|
@ -47,7 +47,7 @@ internal class DefaultScanCardProcessor : ScanCardProcessor {
|
|||
)
|
||||
} else {
|
||||
LegacyScanProcessor.scan(
|
||||
analyticsEvent,
|
||||
analyticsSource,
|
||||
cardId,
|
||||
onProgressStateChange,
|
||||
onWalletNotCreated,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import com.tangem.common.doOnFailure
|
|||
import com.tangem.common.doOnSuccess
|
||||
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.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.domain.common.TapWorkarounds.canSkipBackup
|
||||
|
|
@ -45,7 +47,7 @@ internal object LegacyScanProcessor {
|
|||
|
||||
@Suppress("LongParameterList")
|
||||
suspend fun scan(
|
||||
analyticsEvent: AnalyticsEvent?,
|
||||
analyticsSource: AnalyticsParam.ScreensSources,
|
||||
cardId: String?,
|
||||
onProgressStateChange: suspend (showProgress: Boolean) -> Unit,
|
||||
onWalletNotCreated: suspend () -> Unit,
|
||||
|
|
@ -59,7 +61,8 @@ internal object LegacyScanProcessor {
|
|||
|
||||
val result = tangemSdkManager.scanProduct(cardId)
|
||||
|
||||
store.dispatchOnMain(GlobalAction.ScanFailsCounter.ChooseBehavior(result))
|
||||
val analyticsEvent = Basic.CardWasScanned(analyticsSource)
|
||||
store.dispatchOnMain(GlobalAction.ScanFailsCounter.ChooseBehavior(result, analyticsSource))
|
||||
|
||||
result
|
||||
.doOnFailure { error ->
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import arrow.fx.coroutines.resourceScope
|
|||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemError
|
||||
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.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
|
|
@ -44,7 +44,7 @@ internal object UseCaseScanProcessor {
|
|||
|
||||
@Suppress("LongParameterList")
|
||||
suspend fun scan(
|
||||
analyticsEvent: AnalyticsEvent?,
|
||||
analyticsSource: AnalyticsParam.ScreensSources,
|
||||
cardId: String?,
|
||||
onProgressStateChange: suspend (showProgress: Boolean) -> Unit,
|
||||
onWalletNotCreated: suspend () -> Unit,
|
||||
|
|
@ -55,9 +55,7 @@ internal object UseCaseScanProcessor {
|
|||
val scanCardUseCase = store.inject(DaggerGraphState::scanCardUseCase)
|
||||
val chains = buildList {
|
||||
add(FailedScansCounterChain(UseCaseScanProcessor::showMaxUnsuccessfulScansReachedDialog))
|
||||
if (analyticsEvent != null) {
|
||||
add(AnalyticsChain(analyticsEvent))
|
||||
}
|
||||
add(AnalyticsChain(Basic.CardWasScanned(analyticsSource)))
|
||||
add(DisclaimerChain(store, disclaimerWillShow))
|
||||
add(CheckForOnboardingChain(store, store.state.globalState.tapWalletManager))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,6 +81,10 @@ internal class BiometricUserWalletsListManager(
|
|||
state.update { State() }
|
||||
}
|
||||
|
||||
override fun isLockable(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override suspend fun select(userWalletId: UserWalletId): CompletionResult<UserWallet> = catching {
|
||||
if (state.value.selectedUserWalletId == userWalletId) {
|
||||
return@catching findSelectedUserWallet()!!
|
||||
|
|
|
|||
|
|
@ -118,6 +118,10 @@ internal class GeneralUserWalletsListManager(
|
|||
}
|
||||
}
|
||||
|
||||
override fun isLockable(): Boolean {
|
||||
return implementation.value.isLockable()
|
||||
}
|
||||
|
||||
private fun subscribeOnCurrentManager() {
|
||||
appPreferencesStore.get(key = PreferencesKeys.SAVE_USER_WALLETS_KEY, default = false)
|
||||
.distinctUntilChanged()
|
||||
|
|
|
|||
|
|
@ -85,6 +85,10 @@ internal class RuntimeUserWalletsListManager : UserWalletsListManager {
|
|||
state.value.userWallet ?: walletNotFound()
|
||||
}
|
||||
|
||||
override fun isLockable(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
private fun saveInternal(userWallet: UserWallet): CompletionResult<Unit> = catching {
|
||||
state.update { prevState ->
|
||||
prevState.copy(
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import com.tangem.common.core.TangemSdkError
|
|||
import com.tangem.common.core.UserCodeRequestPolicy
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
|
@ -580,7 +579,7 @@ class DetailsMiddleware {
|
|||
)
|
||||
|
||||
store.inject(DaggerGraphState::scanCardProcessor).scan(
|
||||
analyticsEvent = Basic.CardWasScanned(CoreAnalyticsParam.ScannedFrom.MyWallets),
|
||||
analyticsSource = CoreAnalyticsParam.ScreensSources.Settings,
|
||||
onWalletNotCreated = {
|
||||
// No need to rollback policy, continue with the policy set before the card scan
|
||||
store.dispatchWithMain(DetailsAction.ScanAndSaveUserWallet.Success)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import androidx.compose.runtime.MutableState
|
|||
import androidx.compose.runtime.mutableStateOf
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
|
|
@ -146,7 +147,7 @@ internal class DetailsViewModel(
|
|||
}
|
||||
|
||||
private fun sendFeedback() {
|
||||
Analytics.send(Basic.ButtonSupport())
|
||||
Analytics.send(Basic.ButtonSupport(AnalyticsParam.ScreensSources.Settings))
|
||||
if (feedbackManagerFeatureToggles.isLocalLogsEnabled) {
|
||||
mainScope.launch {
|
||||
val email = getSupportFeedbackEmailUseCase()
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
package com.tangem.tap.features.home.redux
|
||||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.rekotlin.Action
|
||||
|
||||
|
|
@ -16,11 +13,9 @@ sealed class HomeAction : Action {
|
|||
/**
|
||||
* Action for scanning card
|
||||
*
|
||||
* @property analyticsEvent analytics event
|
||||
* @property scope lifecycle scope. It will be canceled when lifecycle-aware component is destroyed
|
||||
*/
|
||||
data class ReadCard(
|
||||
val analyticsEvent: AnalyticsEvent? = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.Introduction),
|
||||
val scope: CoroutineScope,
|
||||
) : HomeAction()
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import com.tangem.common.doOnResult
|
|||
import com.tangem.common.doOnSuccess
|
||||
import com.tangem.common.extensions.guard
|
||||
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.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
|
|
@ -60,7 +60,7 @@ private fun handleHomeAction(action: Action) {
|
|||
}
|
||||
is HomeAction.ReadCard -> {
|
||||
action.scope.launch {
|
||||
readCard(action.analyticsEvent)
|
||||
readCard()
|
||||
}
|
||||
}
|
||||
is HomeAction.GoToShop -> {
|
||||
|
|
@ -76,13 +76,13 @@ private fun handleHomeAction(action: Action) {
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun readCard(analyticsEvent: AnalyticsEvent?) {
|
||||
private suspend fun readCard() {
|
||||
store.inject(DaggerGraphState::cardSdkConfigRepository).setAccessCodeRequestPolicy(
|
||||
isBiometricsRequestPolicy = preferencesStorage.shouldSaveAccessCodes,
|
||||
)
|
||||
|
||||
store.inject(DaggerGraphState::scanCardProcessor).scan(
|
||||
analyticsEvent = analyticsEvent,
|
||||
analyticsSource = AnalyticsParam.ScreensSources.Intro,
|
||||
onProgressStateChange = { showProgress ->
|
||||
if (showProgress) {
|
||||
store.dispatch(HomeAction.ScanInProgress(scanInProgress = true))
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import com.tangem.domain.userwallets.UserWalletIdBuilder
|
|||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.saveWallet.redux.SaveWalletAction
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import kotlinx.coroutines.delay
|
||||
|
|
@ -42,7 +43,8 @@ object OnboardingHelper {
|
|||
response.cardTypesResolver.isWallet2() || response.cardTypesResolver.isShibaWallet() -> {
|
||||
val emptyWallets = response.card.wallets.isEmpty()
|
||||
val activationInProgress = onboardingManager?.isActivationInProgress(cardId)
|
||||
val isNoBackup = response.card.backupStatus == CardDTO.BackupStatus.NoBackup
|
||||
val isNoBackup = response.card.backupStatus == CardDTO.BackupStatus.NoBackup &&
|
||||
!DemoHelper.isDemoCard(response)
|
||||
emptyWallets || activationInProgress == true || isNoBackup
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import android.view.MenuInflater
|
|||
import android.view.MenuItem
|
||||
import androidx.core.view.MenuProvider
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.tap.common.feedback.SupportInfo
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
|
|
@ -21,7 +22,7 @@ class OnboardingMenuProvider : MenuProvider {
|
|||
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) {
|
||||
R.id.menu_item_chat_support -> {
|
||||
Analytics.send(Basic.ButtonSupport())
|
||||
Analytics.send(Basic.ButtonSupport(AnalyticsParam.ScreensSources.Intro))
|
||||
// changed on email support [REDACTED_TASK_KEY]
|
||||
store.dispatch(GlobalAction.SendEmail(SupportInfo()))
|
||||
true
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import com.tangem.common.CardIdFormatter
|
|||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.CardIdDisplayFormat
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.ui.extensions.setStatusBarColor
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
|
|
@ -502,7 +503,7 @@ class OnboardingWalletFragment :
|
|||
private fun makeSeedPhraseRouter(): SeedPhraseRouter = SeedPhraseRouter(
|
||||
onBack = ::legacyOnBackHandler,
|
||||
onOpenChat = {
|
||||
Analytics.send(Basic.ButtonSupport())
|
||||
Analytics.send(Basic.ButtonSupport(AnalyticsParam.ScreensSources.Intro))
|
||||
// changed on email support [REDACTED_TASK_KEY]
|
||||
store.dispatch(GlobalAction.SendEmail(SupportInfo()))
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.app.Dialog
|
|||
import android.content.Context
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.tap.common.extensions.dispatchDialogHide
|
||||
import com.tangem.tap.common.feedback.SupportInfo
|
||||
|
|
@ -21,7 +22,7 @@ object WalletActivationErrorDialog {
|
|||
setPositiveButton(R.string.common_ok) { _, _ -> dialog.onConfirm() }
|
||||
setNegativeButton(R.string.common_support) { _, _ ->
|
||||
// changed on email support [REDACTED_TASK_KEY]
|
||||
Analytics.send(Basic.ButtonSupport())
|
||||
Analytics.send(Basic.ButtonSupport(AnalyticsParam.ScreensSources.Intro))
|
||||
store.dispatch(GlobalAction.SendEmail(SupportInfo()))
|
||||
}
|
||||
setOnDismissListener { store.dispatchDialogHide() }
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import com.tangem.core.navigation.AppScreen
|
|||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.domain.userwallets.UserWalletBuilder
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.legacy.isLockable
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
|
|
@ -209,7 +208,7 @@ internal class SaveWalletMiddleware {
|
|||
}
|
||||
|
||||
private suspend fun provideLockableUserWalletsListManagerIfNot() {
|
||||
if (store.state.globalState.userWalletsListManager?.isLockable == true) return
|
||||
if (store.state.globalState.userWalletsListManager?.isLockable() == true) return
|
||||
|
||||
val context = foregroundActivityObserver.foregroundActivity?.applicationContext.guard {
|
||||
val error = IllegalStateException("No activities in foreground")
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.tap.features.send.ui.dialogs
|
|||
import android.content.Context
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.tap.common.feedback.SendTransactionFailedEmail
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
|
|
@ -21,7 +22,7 @@ object RequestFeeErrorDialog {
|
|||
setTitle(R.string.common_fee_error)
|
||||
setMessage(context.getString(R.string.alert_failed_to_send_transaction_message, errorMessage))
|
||||
setNegativeButton(R.string.details_row_title_contact_to_support) { _, _ ->
|
||||
Analytics.send(Basic.ButtonSupport())
|
||||
Analytics.send(Basic.ButtonSupport(AnalyticsParam.ScreensSources.Send))
|
||||
store.dispatch(GlobalAction.SendEmail(SendTransactionFailedEmail(errorMessage)))
|
||||
}
|
||||
setPositiveButton(R.string.common_retry) { _, _ -> dialog.onRetry() }
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.blockchain.common.Blockchain
|
|||
import com.tangem.blockchain.common.BlockchainSdkError
|
||||
import com.tangem.common.module.ModuleMessageConverter
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.sdk.extensions.localizedDescription
|
||||
import com.tangem.tap.common.extensions.stripZeroPlainString
|
||||
|
|
@ -33,7 +34,7 @@ object SendTransactionFailsDialog {
|
|||
setTitle(R.string.alert_failed_to_send_transaction_title)
|
||||
setMessage(context.getString(R.string.alert_failed_to_send_transaction_message, errorMessage))
|
||||
setNeutralButton(R.string.details_row_title_contact_to_support) { _, _ ->
|
||||
Analytics.send(Basic.ButtonSupport())
|
||||
Analytics.send(Basic.ButtonSupport(AnalyticsParam.ScreensSources.Send))
|
||||
store.dispatch(GlobalAction.SendEmail(SendTransactionFailedEmail(errorMessage)))
|
||||
}
|
||||
setPositiveButton(R.string.common_cancel) { _, _ -> }
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ internal class WelcomeMiddleware {
|
|||
isBiometricsRequestPolicy = preferencesStorage.shouldSaveAccessCodes,
|
||||
)
|
||||
store.inject(DaggerGraphState::scanCardProcessor).scan(
|
||||
analyticsEvent = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.SignIn),
|
||||
analyticsSource = AnalyticsParam.ScreensSources.SignIn,
|
||||
onSuccess = { scanResponse ->
|
||||
scope.launch { onCardScanned(scanResponse) }
|
||||
},
|
||||
|
|
|
|||
|
|
@ -57,11 +57,13 @@ sealed class AnalyticsParam {
|
|||
object BlockchainSdk : Error("Blockchain Sdk Error")
|
||||
}
|
||||
|
||||
sealed class ScannedFrom(val value: String) {
|
||||
object Introduction : ScannedFrom("Introduction")
|
||||
object Main : ScannedFrom("Main")
|
||||
object SignIn : ScannedFrom("Sign In")
|
||||
object MyWallets : ScannedFrom("My Wallets")
|
||||
sealed class ScreensSources(val value: String) {
|
||||
data object Settings : ScreensSources("Settings")
|
||||
data object Main : ScreensSources("Main")
|
||||
data object SignIn : ScreensSources("Sign In")
|
||||
data object Send : ScreensSources("Send")
|
||||
data object Intro : ScreensSources("Introduction")
|
||||
data object MyWallets : ScreensSources("My Wallets")
|
||||
}
|
||||
|
||||
sealed class TxSentFrom(val value: String) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ sealed class Basic(
|
|||
) : AnalyticsEvent("Basic", event, params, error) {
|
||||
|
||||
class CardWasScanned(
|
||||
source: AnalyticsParam.ScannedFrom,
|
||||
source: AnalyticsParam.ScreensSources,
|
||||
) : Basic(
|
||||
event = "Card Was Scanned",
|
||||
params = mapOf(
|
||||
|
|
@ -76,5 +76,10 @@ sealed class Basic(
|
|||
|
||||
class WalletOpened : Basic(event = "Wallet Opened")
|
||||
|
||||
class ButtonSupport : Basic("Request Support")
|
||||
class ButtonSupport(source: AnalyticsParam.ScreensSources) : Basic(
|
||||
event = "Request Support",
|
||||
params = mapOf(
|
||||
AnalyticsParam.SOURCE to source.value,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -72,6 +72,12 @@ class AppPreferencesStore(
|
|||
return this[key]?.let(adapter::fromJson).orEmpty()
|
||||
}
|
||||
|
||||
/** Get set of data [T] by string [key] */
|
||||
inline fun <reified T> MutablePreferences.getObjectSet(key: Preferences.Key<String>): Set<T>? {
|
||||
val adapter = moshi.adapter<Set<T>>(Types.newParameterizedType(Set::class.java, T::class.java))
|
||||
return this[key]?.let(adapter::fromJson)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set data [T] by string [key] to [MutablePreferences]
|
||||
*
|
||||
|
|
@ -97,4 +103,10 @@ class AppPreferencesStore(
|
|||
|
||||
this[key] = adapter.toJson(value)
|
||||
}
|
||||
|
||||
/** Sets set of data [T] by string [key] to [MutablePreferences] */
|
||||
inline fun <reified T> MutablePreferences.setObjectSet(key: Preferences.Key<String>, value: Set<T>) {
|
||||
val adapter = moshi.adapter<Set<T>>(Types.newParameterizedType(Set::class.java, T::class.java))
|
||||
this[key] = adapter.toJson(value)
|
||||
}
|
||||
}
|
||||
|
|
@ -63,6 +63,16 @@ object PreferencesKeys {
|
|||
|
||||
val APP_LOGS_KEY by lazy { stringPreferencesKey(name = "app_logs") }
|
||||
|
||||
val POLKADOT_HEALTH_CHECK_LAST_INDEXED_TX_KEY by lazy {
|
||||
stringPreferencesKey(name = "POLKADOT_HEALTH_CHECK_LAST_INDEXED_TX")
|
||||
}
|
||||
val POLKADOT_HEALTH_CHECKED_RESET_ACCOUNTS_KEY by lazy {
|
||||
stringPreferencesKey(name = "POLKADOT_HEALTH_CHECKED_RESET_ACCOUNTS")
|
||||
}
|
||||
val POLKADOT_HEALTH_CHECKED_IMMUTABLE_ACCOUNTS_KEY by lazy {
|
||||
stringPreferencesKey(name = "POLKADOT_HEALTH_CHECKED_IMMUTABLE_ACCOUNTS")
|
||||
}
|
||||
|
||||
val SEND_TAP_HELP_PREVIEW_KEY by lazy { booleanPreferencesKey(name = "sendTapHelpPreview") }
|
||||
|
||||
fun getStart2CoinTOSAcceptedKey(region: String?) = booleanPreferencesKey(name = "start2Coin_tos_accepted_$region")
|
||||
|
|
|
|||
|
|
@ -133,3 +133,12 @@ suspend inline fun <reified V> AppPreferencesStore.getObjectMap(key: Preferences
|
|||
?.let(adapter::fromJson)
|
||||
.orEmpty()
|
||||
}
|
||||
|
||||
/** Get set of data [T] by string [key], or empty if data is not found */
|
||||
suspend inline fun <reified T> AppPreferencesStore.getObjectSetSync(key: Preferences.Key<String>): Set<T> {
|
||||
val adapter = moshi.adapter<Set<T>>(Types.newParameterizedType(Set::class.java, T::class.java))
|
||||
return data.firstOrNull()
|
||||
?.get(key)
|
||||
?.let(adapter::fromJson)
|
||||
.orEmpty()
|
||||
}
|
||||
|
|
@ -2,5 +2,9 @@ package com.tangem.core.navigation
|
|||
|
||||
interface StateDialog {
|
||||
|
||||
object ScanFailsDialog : StateDialog
|
||||
data class ScanFailsDialog(val source: ScanFailsSource) : StateDialog
|
||||
|
||||
enum class ScanFailsSource {
|
||||
MAIN, SIGN_IN, SETTINGS, INTRO;
|
||||
}
|
||||
}
|
||||
|
|
@ -119,4 +119,18 @@ internal object TokensDataModule {
|
|||
fun provideCurrencyChecksRepository(walletManagersFacade: WalletManagersFacade): CurrencyChecksRepository {
|
||||
return DefaultCurrencyChecksRepository(walletManagersFacade = walletManagersFacade)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providePolkadotAccountHealthCheckRepository(
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
appPreferencesStore: AppPreferencesStore,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): PolkadotAccountHealthCheckRepository {
|
||||
return DefaultPolkadotAccountHealthCheckRepository(
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -26,6 +26,7 @@ import kotlinx.coroutines.flow.channelFlow
|
|||
import kotlinx.coroutines.flow.collectLatest
|
||||
import timber.log.Timber
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class DefaultNetworksRepository(
|
||||
private val networksStatusesStore: NetworksStatusesStore,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,179 @@
|
|||
package com.tangem.data.tokens.repository
|
||||
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import com.tangem.blockchain.blockchains.polkadot.AccountCheckProvider
|
||||
import com.tangem.blockchain.blockchains.polkadot.network.accounthealthcheck.ExtrinsicListItemResponse
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.BlockchainSdkError
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys.POLKADOT_HEALTH_CHECKED_IMMUTABLE_ACCOUNTS_KEY
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys.POLKADOT_HEALTH_CHECKED_RESET_ACCOUNTS_KEY
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys.POLKADOT_HEALTH_CHECK_LAST_INDEXED_TX_KEY
|
||||
import com.tangem.datasource.local.preferences.utils.getObjectMap
|
||||
import com.tangem.datasource.local.preferences.utils.getObjectSetSync
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.tokens.repository.PolkadotAccountHealthCheckRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.collections.set
|
||||
|
||||
internal class DefaultPolkadotAccountHealthCheckRepository(
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : PolkadotAccountHealthCheckRepository {
|
||||
|
||||
private val hasImmortalTransaction = MutableSharedFlow<Pair<String, Boolean>>()
|
||||
private val hasResetTransaction = MutableSharedFlow<Pair<String, Boolean>>()
|
||||
|
||||
private val mutex = Mutex()
|
||||
private val mutexes = ConcurrentHashMap<String, Mutex>()
|
||||
|
||||
override suspend fun runCheck(userWalletId: UserWalletId, network: Network) {
|
||||
// Run Polkadot account health check
|
||||
if (Blockchain.fromId(network.id.value) != Blockchain.Polkadot) return
|
||||
|
||||
val walletManager = walletManagersFacade.getOrCreateWalletManager(userWalletId, network)
|
||||
val address = requireNotNull(walletManager?.wallet?.address) {
|
||||
Timber.e("Address is null")
|
||||
return
|
||||
}
|
||||
val accountCheckProvider = requireNotNull(walletManager as? AccountCheckProvider) {
|
||||
Timber.e("Unable to cast wallet manager to AccountCheckProvider")
|
||||
return
|
||||
}
|
||||
|
||||
// use a separate mutexForKey for each key to avoid multiple calls block() to the same key
|
||||
// also used mutex to safe create mutexForKey, otherwise it can lead to multiple calls for the same key
|
||||
val mutexForKey = mutex.withLock { mutexes.getOrPut(address) { Mutex() } }
|
||||
mutexForKey.withLock {
|
||||
withContext(dispatchers.io) {
|
||||
checkHasReset(accountCheckProvider, address)
|
||||
checkHasImmortal(accountCheckProvider, address)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun subscribeToHasImmortalResults() = hasImmortalTransaction.asSharedFlow()
|
||||
|
||||
override fun subscribeToHasResetResults() = hasResetTransaction.asSharedFlow()
|
||||
|
||||
private suspend fun checkHasReset(polkadotManager: AccountCheckProvider, address: String) {
|
||||
val checkedAddresses = appPreferencesStore.getObjectSetSync<String>(POLKADOT_HEALTH_CHECKED_RESET_ACCOUNTS_KEY)
|
||||
if (checkedAddresses.contains(address)) return
|
||||
runCatching {
|
||||
val accountInfo = requireNotNull(polkadotManager.getAccountInfo().account) {
|
||||
Timber.e("Account info is null")
|
||||
}
|
||||
val nonce = accountInfo.nonce
|
||||
val extrinsicCount = accountInfo.countExtrinsic
|
||||
|
||||
// Account was reset
|
||||
if (nonce != null && extrinsicCount != null) {
|
||||
val hasReset = nonce < extrinsicCount
|
||||
updateChecked(address, POLKADOT_HEALTH_CHECKED_RESET_ACCOUNTS_KEY)
|
||||
hasResetTransaction.emit(address to hasReset)
|
||||
}
|
||||
}.onFailure {
|
||||
if ((it as? BlockchainSdkError.CustomError)?.customMessage == ACCOUNT_NOT_FOUND) {
|
||||
updateChecked(address, POLKADOT_HEALTH_CHECKED_RESET_ACCOUNTS_KEY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun checkHasImmortal(accountCheckerProvider: AccountCheckProvider, address: String) {
|
||||
val checkedAddresses =
|
||||
appPreferencesStore.getObjectSetSync<String>(POLKADOT_HEALTH_CHECKED_IMMUTABLE_ACCOUNTS_KEY)
|
||||
if (checkedAddresses.contains(address)) return
|
||||
|
||||
runCatching {
|
||||
do {
|
||||
// Getting batch of extrinsics to check
|
||||
val lastChecked = appPreferencesStore.getObjectMap<Long>(POLKADOT_HEALTH_CHECK_LAST_INDEXED_TX_KEY)
|
||||
val lastExtrinsic = lastChecked[address]
|
||||
val extrinsicListResult = accountCheckerProvider.getExtrinsicList(afterExtrinsicId = lastExtrinsic)
|
||||
extrinsicListResult.extrinsic
|
||||
?.forEach { tx ->
|
||||
// Checking extrinsic one by one
|
||||
if (checkTx(tx, address, accountCheckerProvider)) return
|
||||
}
|
||||
} while (!extrinsicListResult.extrinsic.isNullOrEmpty())
|
||||
|
||||
// We checked all transactions up to current moment and did not found an `immortal` transaction
|
||||
hasImmortalTransaction.emit(address to false)
|
||||
updateChecked(address, POLKADOT_HEALTH_CHECKED_IMMUTABLE_ACCOUNTS_KEY)
|
||||
clearLastCheckedTransaction(address)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun checkTx(
|
||||
tx: ExtrinsicListItemResponse,
|
||||
address: String,
|
||||
accountCheckerProvider: AccountCheckProvider,
|
||||
): Boolean {
|
||||
val hash = tx.hash
|
||||
val id = tx.id
|
||||
if (hash != null && id != null) {
|
||||
val details = accountCheckerProvider.getExtrinsicDetail(hash)
|
||||
|
||||
// We found an `immortal` transaction
|
||||
if (details.lifetime == null) {
|
||||
hasImmortalTransaction.emit(address to true)
|
||||
updateChecked(address, POLKADOT_HEALTH_CHECKED_IMMUTABLE_ACCOUNTS_KEY)
|
||||
clearLastCheckedTransaction(address)
|
||||
return true
|
||||
}
|
||||
|
||||
// Saving last checked transaction
|
||||
updateLastCheckedTransaction(address, id)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private suspend fun updateLastCheckedTransaction(address: String, txId: Long) {
|
||||
appPreferencesStore.editData {
|
||||
val savedList = it.getObjectMap<Long>(POLKADOT_HEALTH_CHECK_LAST_INDEXED_TX_KEY)
|
||||
val updatedList = savedList.toMutableMap()
|
||||
updatedList[address] = txId
|
||||
it.setObjectMap(POLKADOT_HEALTH_CHECK_LAST_INDEXED_TX_KEY, updatedList)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun clearLastCheckedTransaction(address: String) {
|
||||
appPreferencesStore.editData { mutablePreferences ->
|
||||
val savedList = mutablePreferences.getObjectMap<Long>(POLKADOT_HEALTH_CHECK_LAST_INDEXED_TX_KEY)
|
||||
val updatedList = savedList.toMutableMap()
|
||||
updatedList.remove(address)
|
||||
mutablePreferences.setObjectMap(POLKADOT_HEALTH_CHECK_LAST_INDEXED_TX_KEY, updatedList)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun updateChecked(address: String, key: Preferences.Key<String>) {
|
||||
appPreferencesStore.editData { mutablePreferences ->
|
||||
val savedList = mutablePreferences.getObjectSet<String>(key)
|
||||
val updatedList = savedList?.toMutableSet() ?: mutableSetOf()
|
||||
updatedList.add(address)
|
||||
|
||||
if (updatedList.isNotEmpty()) {
|
||||
mutablePreferences.setObjectSet(
|
||||
key,
|
||||
updatedList.toSet(),
|
||||
)
|
||||
} else {
|
||||
mutablePreferences.remove(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val ACCOUNT_NOT_FOUND = "Record Not Found"
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ package com.tangem.domain.card
|
|||
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
|
||||
interface ScanCardProcessor {
|
||||
|
|
@ -13,7 +13,7 @@ interface ScanCardProcessor {
|
|||
): CompletionResult<ScanResponse>
|
||||
|
||||
suspend fun scan(
|
||||
analyticsEvent: AnalyticsEvent? = null,
|
||||
analyticsSource: AnalyticsParam.ScreensSources,
|
||||
cardId: String? = null,
|
||||
onProgressStateChange: suspend (showProgress: Boolean) -> Unit = {},
|
||||
onWalletNotCreated: suspend () -> Unit = {},
|
||||
|
|
|
|||
|
|
@ -399,12 +399,27 @@ class DemoConfig {
|
|||
"AB02000000048063",
|
||||
"AB02000000023736",
|
||||
"AB02000000058187",
|
||||
"AB02000000000007",
|
||||
"AC03000000076229",
|
||||
"AF04000000000118",
|
||||
|
||||
// Wallet 2
|
||||
"AF04000000012006",
|
||||
"AF04000000012014",
|
||||
"AF04000000012022",
|
||||
"AF04000000012030",
|
||||
"AF15000000257889",
|
||||
"AF15000000257897",
|
||||
"AF15000000637809",
|
||||
"AF15000000640282",
|
||||
"AF15000001187424",
|
||||
"AF15000001187408",
|
||||
"AF15000001187416",
|
||||
"AF15000001195781",
|
||||
"AF15000001195773",
|
||||
"AF15000001195799",
|
||||
"AF19000000000038",
|
||||
"AC19000000000064",
|
||||
)
|
||||
|
||||
@Suppress("ClassOrdering")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.domain.tokens
|
||||
|
||||
import com.tangem.domain.tokens.repository.PolkadotAccountHealthCheckRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class GetPolkadotCheckHasImmortalUseCase(
|
||||
private val polkadotAccountHealthCheckRepository: PolkadotAccountHealthCheckRepository,
|
||||
) {
|
||||
operator fun invoke(): Flow<Pair<String, Boolean>> =
|
||||
polkadotAccountHealthCheckRepository.subscribeToHasImmortalResults()
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.domain.tokens
|
||||
|
||||
import com.tangem.domain.tokens.repository.PolkadotAccountHealthCheckRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class GetPolkadotCheckHasResetUseCase(
|
||||
private val polkadotAccountHealthCheckRepository: PolkadotAccountHealthCheckRepository,
|
||||
) {
|
||||
|
||||
operator fun invoke(): Flow<Pair<String, Boolean>> =
|
||||
polkadotAccountHealthCheckRepository.subscribeToHasResetResults()
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.domain.tokens
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.tokens.repository.PolkadotAccountHealthCheckRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
class RunPolkadotAccountHealthCheckUseCase(
|
||||
private val polkadotAccountHealthCheckRepository: PolkadotAccountHealthCheckRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(userWalletId: UserWalletId, network: Network): Either<Throwable, Unit> = Either.catch {
|
||||
polkadotAccountHealthCheckRepository.runCheck(userWalletId, network)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.domain.tokens.repository
|
||||
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface PolkadotAccountHealthCheckRepository {
|
||||
|
||||
suspend fun runCheck(userWalletId: UserWalletId, network: Network)
|
||||
|
||||
fun subscribeToHasImmortalResults(): Flow<Pair<String, Boolean>>
|
||||
|
||||
fun subscribeToHasResetResults(): Flow<Pair<String, Boolean>>
|
||||
}
|
||||
|
|
@ -84,6 +84,11 @@ interface UserWalletsListManager {
|
|||
*/
|
||||
suspend fun get(userWalletId: UserWalletId): CompletionResult<UserWallet>
|
||||
|
||||
/**
|
||||
* Indicates that the [UserWalletsListManager] supports [UserWalletsListManager.Lockable]
|
||||
* */
|
||||
fun isLockable(): Boolean
|
||||
|
||||
interface Lockable : UserWalletsListManager {
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -6,12 +6,6 @@ import com.tangem.domain.wallets.models.UserWallet
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
/**
|
||||
* Indicates that the [UserWalletsListManager] implements [UserWalletsListManager.Lockable]
|
||||
* */
|
||||
val UserWalletsListManager.isLockable: Boolean
|
||||
get() = this is UserWalletsListManager.Lockable
|
||||
|
||||
/**
|
||||
* Indicates that the [UserWalletsListManager] is locked
|
||||
*
|
||||
|
|
@ -48,16 +42,6 @@ suspend fun UserWalletsListManager.unlockIfLockable(type: UnlockType = UnlockTyp
|
|||
return asLockable()?.unlock(type) ?: CompletionResult.Failure(UserWalletsListError.UnableToUnlockUserWallets())
|
||||
}
|
||||
|
||||
/**
|
||||
* Call [UserWalletsListManager.Lockable.lock] if [UserWalletsListManager] implements [UserWalletsListManager.Lockable]
|
||||
* or do nothing otherwise
|
||||
*
|
||||
* @see UserWalletsListManager.Lockable.lock
|
||||
* */
|
||||
fun UserWalletsListManager.lockIfLockable() {
|
||||
asLockable()?.lock()
|
||||
}
|
||||
|
||||
/**
|
||||
* Safe cast [UserWalletsListManager] to [UserWalletsListManager.Lockable]
|
||||
*
|
||||
|
|
@ -65,5 +49,8 @@ fun UserWalletsListManager.lockIfLockable() {
|
|||
* [UserWalletsListManager.Lockable] otherwise
|
||||
* */
|
||||
fun UserWalletsListManager.asLockable(): UserWalletsListManager.Lockable? {
|
||||
return this as? UserWalletsListManager.Lockable
|
||||
if (this.isLockable()) {
|
||||
return this as? UserWalletsListManager.Lockable
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
|
@ -163,7 +163,11 @@ internal class DefaultWalletRouter(
|
|||
}
|
||||
|
||||
override fun openScanFailedDialog() {
|
||||
reduxNavController.navigate(action = NavigationAction.OpenDialog(StateDialog.ScanFailsDialog))
|
||||
reduxNavController.navigate(
|
||||
action = NavigationAction.OpenDialog(
|
||||
StateDialog.ScanFailsDialog(StateDialog.ScanFailsSource.MAIN),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ sealed class WalletScreenAnalyticsEvent {
|
|||
|
||||
data object WalletOpened : Basic(event = "Wallet Opened")
|
||||
|
||||
class CardWasScanned(source: AnalyticsParam.ScannedFrom) : Basic(
|
||||
class CardWasScanned(source: AnalyticsParam.ScreensSources) : Basic(
|
||||
event = "Card Was Scanned",
|
||||
params = mapOf(
|
||||
AnalyticsParam.SOURCE to source.value,
|
||||
|
|
@ -48,6 +48,26 @@ sealed class WalletScreenAnalyticsEvent {
|
|||
)
|
||||
}
|
||||
|
||||
sealed class Token(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent(category = "Token", event = event, params = params) {
|
||||
|
||||
class PolkadotAccountReset(hasReset: Boolean) : Token(
|
||||
event = "Polkadot Account Reset",
|
||||
params = mapOf(
|
||||
AnalyticsParam.STATE to if (hasReset) "Yes" else "No",
|
||||
),
|
||||
)
|
||||
|
||||
class PolkadotImmortalTransactions(hasImmortalTransaction: Boolean) : Token(
|
||||
event = "Polkadot Immortal Transactions",
|
||||
params = mapOf(
|
||||
AnalyticsParam.STATE to if (hasImmortalTransaction) "Yes" else "No",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
sealed class MainScreen(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
|||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
|
||||
import com.tangem.domain.tokens.GetTokenListUseCase
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
|
|
@ -29,6 +30,7 @@ internal class MultiWalletContentLoader(
|
|||
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
|
||||
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
|
||||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
|
||||
) : WalletContentLoader(id = userWallet.walletId) {
|
||||
|
||||
override fun create(): List<WalletSubscriber> {
|
||||
|
|
@ -42,6 +44,7 @@ internal class MultiWalletContentLoader(
|
|||
getTokenListUseCase = getTokenListUseCase,
|
||||
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
|
||||
applyTokenListSortingUseCase = applyTokenListSortingUseCase,
|
||||
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
|
||||
),
|
||||
MultiWalletWarningsSubscriber(
|
||||
userWallet = userWallet,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
|||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
|
||||
import com.tangem.domain.tokens.GetTokenListUseCase
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
|
|
@ -26,6 +27,7 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
|
|||
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
|
||||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
|
||||
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
|
||||
) {
|
||||
|
||||
fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): WalletContentLoader {
|
||||
|
|
@ -41,6 +43,7 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
|
|||
reduxStateHolder = reduxStateHolder,
|
||||
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
|
||||
applyTokenListSortingUseCase = applyTokenListSortingUseCase,
|
||||
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
|
|||
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.tokens.GetCardTokensListUseCase
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
|
|
@ -24,6 +25,7 @@ internal class SingleWalletWithTokenContentLoader(
|
|||
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
|
||||
private val getCardTokensListUseCase: GetCardTokensListUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
|
||||
) : WalletContentLoader(id = userWallet.walletId) {
|
||||
|
||||
override fun create(): List<WalletSubscriber> {
|
||||
|
|
@ -36,6 +38,7 @@ internal class SingleWalletWithTokenContentLoader(
|
|||
walletWithFundsChecker = walletWithFundsChecker,
|
||||
getCardTokensListUseCase = getCardTokensListUseCase,
|
||||
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
|
||||
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
|
||||
),
|
||||
MultiWalletWarningsSubscriber(
|
||||
userWallet = userWallet,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
|
|||
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.tokens.GetCardTokensListUseCase
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
|
|
@ -21,6 +22,7 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor(
|
|||
private val getCardTokensListUseCase: GetCardTokensListUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
|
||||
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
|
||||
) {
|
||||
|
||||
fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): SingleWalletWithTokenContentLoader {
|
||||
|
|
@ -34,6 +36,7 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor(
|
|||
getCardTokensListUseCase = getCardTokensListUseCase,
|
||||
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
|
||||
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
|
||||
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,9 @@ import arrow.core.Either
|
|||
import arrow.core.getOrElse
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.model.NetworkGroup
|
||||
import com.tangem.domain.tokens.model.TokenList
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
|
|
@ -33,6 +35,7 @@ internal abstract class BasicTokenListSubscriber(
|
|||
private val tokenListAnalyticsSender: TokenListAnalyticsSender,
|
||||
private val walletWithFundsChecker: WalletWithFundsChecker,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
|
||||
) : WalletSubscriber() {
|
||||
|
||||
private val sendAnalyticsJobHolder = JobHolder()
|
||||
|
|
@ -53,6 +56,8 @@ internal abstract class BasicTokenListSubscriber(
|
|||
coroutineScope.launch {
|
||||
onTokenListReceived(maybeTokenList)
|
||||
}.saveIn(onTokenListReceivedJobHolder)
|
||||
|
||||
coroutineScope.launch { startCheck(maybeTokenList) }
|
||||
},
|
||||
flow2 = getSelectedAppCurrencyUseCase().distinctUntilChanged(),
|
||||
transform = { maybeTokenList, maybeAppCurrency ->
|
||||
|
|
@ -72,6 +77,21 @@ internal abstract class BasicTokenListSubscriber(
|
|||
)
|
||||
}
|
||||
|
||||
private suspend fun startCheck(maybeTokenList: Either<TokenListError, TokenList>) {
|
||||
// Run Polkadot account health check
|
||||
maybeTokenList.getOrNull()?.let { tokenList ->
|
||||
val cryptoCurrencies = when (tokenList) {
|
||||
is TokenList.GroupedByNetwork -> tokenList.groups.flatMap(NetworkGroup::currencies)
|
||||
is TokenList.Ungrouped -> tokenList.currencies
|
||||
is TokenList.Empty -> emptyList()
|
||||
}
|
||||
|
||||
cryptoCurrencies.forEach {
|
||||
runPolkadotAccountHealthCheckUseCase(userWallet.walletId, it.currency.network)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open suspend fun onTokenListReceived(maybeTokenList: Either<TokenListError, TokenList>) {
|
||||
/* no-op */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import arrow.core.getOrElse
|
|||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
|
||||
import com.tangem.domain.tokens.GetTokenListUseCase
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.TokenList
|
||||
|
|
@ -24,6 +25,7 @@ internal class MultiWalletTokenListSubscriber(
|
|||
tokenListAnalyticsSender: TokenListAnalyticsSender,
|
||||
walletWithFundsChecker: WalletWithFundsChecker,
|
||||
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
|
||||
) : BasicTokenListSubscriber(
|
||||
userWallet = userWallet,
|
||||
stateHolder = stateHolder,
|
||||
|
|
@ -31,6 +33,7 @@ internal class MultiWalletTokenListSubscriber(
|
|||
tokenListAnalyticsSender = tokenListAnalyticsSender,
|
||||
walletWithFundsChecker = walletWithFundsChecker,
|
||||
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
|
||||
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
|
||||
) {
|
||||
|
||||
override fun tokenListFlow(): MaybeTokenListFlow = getTokenListUseCase(userWallet.walletId)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.subscribers
|
|||
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.tokens.GetCardTokensListUseCase
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
|
||||
|
|
@ -17,6 +18,7 @@ internal class SingleWalletWithTokenListSubscriber(
|
|||
tokenListAnalyticsSender: TokenListAnalyticsSender,
|
||||
walletWithFundsChecker: WalletWithFundsChecker,
|
||||
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
|
||||
) : BasicTokenListSubscriber(
|
||||
userWallet = userWallet,
|
||||
stateHolder = stateHolder,
|
||||
|
|
@ -24,6 +26,7 @@ internal class SingleWalletWithTokenListSubscriber(
|
|||
tokenListAnalyticsSender = tokenListAnalyticsSender,
|
||||
walletWithFundsChecker = walletWithFundsChecker,
|
||||
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
|
||||
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
|
||||
) {
|
||||
|
||||
override fun tokenListFlow(): MaybeTokenListFlow = getCardTokensListUseCase(userWallet.walletId)
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onGenerateMissedAddressesClick(missedAddressCurrencies: List<CryptoCurrency>) {
|
||||
analyticsEventHandler.send(Basic.CardWasScanned(AnalyticsParam.ScannedFrom.Main))
|
||||
analyticsEventHandler.send(Basic.CardWasScanned(AnalyticsParam.ScreensSources.Main))
|
||||
analyticsEventHandler.send(MainScreen.NoticeScanYourCardTapped)
|
||||
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue