Updated on 2026-08-14
This commit is contained in:
commit
13ef9b4242
17 changed files with 256 additions and 77 deletions
|
|
@ -19,6 +19,7 @@ import com.tangem.tap.common.OnActivityResultCallback
|
|||
import com.tangem.tap.common.SnackbarHandler
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.redux.NotificationsHandler
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.common.shop.googlepay.GooglePayService
|
||||
|
|
@ -27,6 +28,7 @@ import com.tangem.tap.common.shop.googlepay.GooglePayUtil.createPaymentsClient
|
|||
import com.tangem.tap.domain.TangemSdkManager
|
||||
import com.tangem.tap.domain.userWalletList.UserWalletsListManager
|
||||
import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation
|
||||
import com.tangem.tap.domain.userWalletList.di.provideRuntimeImplementation
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
|
||||
import com.tangem.tap.features.shop.redux.ShopAction
|
||||
import com.tangem.tap.features.welcome.redux.WelcomeAction
|
||||
|
|
@ -45,11 +47,8 @@ import kotlin.coroutines.CoroutineContext
|
|||
lateinit var tangemSdk: TangemSdk
|
||||
lateinit var tangemSdkManager: TangemSdkManager
|
||||
lateinit var backupService: BackupService
|
||||
lateinit var userWalletsListManager: UserWalletsListManager
|
||||
internal var lockUserWalletsTimer: LockUserWalletsTimer? = null
|
||||
private set
|
||||
var userWalletsListManagerSafe: UserWalletsListManager? = null
|
||||
private set
|
||||
var notificationsHandler: NotificationsHandler? = null
|
||||
|
||||
private val coroutineContext: CoroutineContext
|
||||
|
|
@ -60,6 +59,12 @@ private val mainCoroutineContext: CoroutineContext
|
|||
get() = Job() + Dispatchers.Main + FeatureCoroutineExceptionHandler.create("mainScope")
|
||||
val mainScope = CoroutineScope(mainCoroutineContext)
|
||||
|
||||
// TODO: Move to DI
|
||||
val userWalletsListManagerSafe: UserWalletsListManager?
|
||||
get() = store.state.globalState.userWalletsListManager
|
||||
val userWalletsListManager: UserWalletsListManager
|
||||
get() = userWalletsListManagerSafe!!
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbackHolder {
|
||||
|
||||
|
|
@ -83,14 +88,10 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
appStateHolder.tangemSdkManager = tangemSdkManager
|
||||
appStateHolder.tangemSdk = tangemSdk
|
||||
backupService = BackupService.init(tangemSdk, this)
|
||||
userWalletsListManager = UserWalletsListManager.provideBiometricImplementation(
|
||||
context = applicationContext,
|
||||
tangemSdkManager = tangemSdkManager,
|
||||
)
|
||||
appStateHolder.userWalletsListManager = userWalletsListManager
|
||||
userWalletsListManagerSafe = userWalletsListManager
|
||||
lockUserWalletsTimer = LockUserWalletsTimer(owner = this)
|
||||
|
||||
initUserWalletsListManager()
|
||||
|
||||
store.dispatch(
|
||||
ShopAction.CheckIfGooglePayAvailable(
|
||||
GooglePayService(createPaymentsClient(this), this),
|
||||
|
|
@ -98,6 +99,18 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
)
|
||||
}
|
||||
|
||||
private fun initUserWalletsListManager() {
|
||||
val manager = if (preferencesStorage.shouldSaveUserWallets) {
|
||||
UserWalletsListManager.provideBiometricImplementation(
|
||||
context = applicationContext,
|
||||
tangemSdkManager = tangemSdkManager,
|
||||
)
|
||||
} else {
|
||||
UserWalletsListManager.provideRuntimeImplementation()
|
||||
}
|
||||
store.dispatch(GlobalAction.UpdateUserWalletsListManager(manager))
|
||||
}
|
||||
|
||||
private fun systemActions() {
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
|
|
@ -200,11 +213,12 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
backStackIsEmpty -> {
|
||||
navigateToInitialScreen(intent)
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateToInitialScreen(intent: Intent?) {
|
||||
if (userWalletsListManager.hasUserWallets) {
|
||||
if (store.state.globalState.userWalletsListManager?.hasUserWallets == true) {
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Welcome))
|
||||
store.dispatchOnMain(WelcomeAction.HandleIntentIfNeeded(intent))
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.common.extensions
|
|||
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.StateDialog
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
|
|
@ -16,10 +17,25 @@ import org.rekotlin.Store
|
|||
|
||||
/**
|
||||
* Dispatch action with creating the new coroutine with the Main dispatcher
|
||||
*
|
||||
* @see dispatchWithMain
|
||||
*/
|
||||
fun Store<*>.dispatchOnMain(action: Action) {
|
||||
scope.launch(Dispatchers.Main) {
|
||||
store.dispatch(action)
|
||||
dispatch(action)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch action on the Main coroutine context
|
||||
*
|
||||
* @param action [Action] to be dispatched
|
||||
*
|
||||
* @see dispatchOnMain
|
||||
* */
|
||||
suspend fun Store<*>.dispatchWithMain(action: Action) {
|
||||
withMainContext {
|
||||
dispatch(action)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -27,9 +43,8 @@ fun Store<*>.dispatchNotification(resId: Int) {
|
|||
dispatchOnMain(GlobalAction.ShowNotification(resId))
|
||||
}
|
||||
|
||||
@Suppress("UnusedReceiverParameter")
|
||||
suspend fun Store<*>.onUserWalletSelected(userWallet: UserWallet, refresh: Boolean = false) {
|
||||
store.state.globalState.tapWalletManager.onWalletSelected(userWallet, refresh)
|
||||
suspend fun Store<AppState>.onUserWalletSelected(userWallet: UserWallet, refresh: Boolean = false) {
|
||||
state.globalState.tapWalletManager.onWalletSelected(userWallet, refresh)
|
||||
}
|
||||
|
||||
fun Store<*>.dispatchToastNotification(resId: Int) {
|
||||
|
|
@ -77,6 +92,7 @@ suspend fun Store<*>.onCardScanned(scanResponse: ScanResponse) {
|
|||
fun Store<*>.dispatchOpenUrl(url: String) {
|
||||
store.dispatch(NavigationAction.OpenUrl(url))
|
||||
}
|
||||
|
||||
fun Store<*>.dispatchShare(url: String) {
|
||||
store.dispatch(NavigationAction.Share(url))
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ import com.tangem.tap.common.redux.ToastNotificationAction
|
|||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
|
||||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
|
||||
import com.tangem.tap.domain.userWalletList.UserWalletsListManager
|
||||
import com.tangem.tap.features.details.redux.SecurityOption
|
||||
import org.rekotlin.Action
|
||||
|
||||
|
|
@ -105,4 +106,6 @@ sealed class GlobalAction : Action {
|
|||
object FetchUserCountry : GlobalAction() {
|
||||
data class Success(val countryCode: String) : GlobalAction()
|
||||
}
|
||||
|
||||
data class UpdateUserWalletsListManager(val manager: UserWalletsListManager) : GlobalAction()
|
||||
}
|
||||
|
|
@ -84,6 +84,12 @@ fun globalReducer(action: Action, state: AppState, appStateHolder: AppStateHolde
|
|||
is GlobalAction.FetchUserCountry.Success -> globalState.copy(
|
||||
userCountryCode = action.countryCode,
|
||||
)
|
||||
is GlobalAction.UpdateUserWalletsListManager -> {
|
||||
appStateHolder.userWalletsListManager = action.manager
|
||||
globalState.copy(
|
||||
userWalletsListManager = action.manager,
|
||||
)
|
||||
}
|
||||
else -> globalState
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,14 @@
|
|||
package com.tangem.tap.common.redux.global
|
||||
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.common.feedback.FeedbackManager
|
||||
import com.tangem.tap.common.redux.StateDialog
|
||||
import com.tangem.tap.domain.PayIdManager
|
||||
import com.tangem.tap.domain.TapWalletManager
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
|
||||
import com.tangem.tap.domain.userWalletList.UserWalletsListManager
|
||||
import com.tangem.tap.features.onboarding.OnboardingManager
|
||||
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
|
||||
import org.rekotlin.StateType
|
||||
|
|
@ -26,6 +27,7 @@ data class GlobalState(
|
|||
val dialog: StateDialog? = null,
|
||||
val exchangeManager: CurrencyExchangeManager = CurrencyExchangeManager.dummy(),
|
||||
val userCountryCode: String? = null,
|
||||
val userWalletsListManager: UserWalletsListManager? = null,
|
||||
) : StateType
|
||||
|
||||
typealias CryptoCurrencyName = String
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import com.tangem.tap.features.wallet.models.toBlockchainNetworks
|
|||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.middlewares.handleBasicAnalyticsEvent
|
||||
import com.tangem.tap.network.NetworkConnectivity
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.tap.userTokensRepository
|
||||
|
|
@ -113,8 +114,10 @@ class TapWalletManager {
|
|||
store.state.globalState.feedbackManager?.infoHolder?.setCardInfo(scanResponse)
|
||||
updateConfigManager(scanResponse)
|
||||
withMainContext {
|
||||
// Order is important
|
||||
store.dispatch(DisclaimerAction.SetDisclaimer(card.createDisclaimer()))
|
||||
store.dispatch(WalletAction.UserWalletChanged(userWallet))
|
||||
store.dispatch(WalletAction.UpdateCanSaveUserWallets(preferencesStorage.shouldSaveUserWallets))
|
||||
store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse))
|
||||
store.dispatch(WalletConnectAction.ResetState)
|
||||
store.dispatch(GlobalAction.SaveScanResponse(scanResponse))
|
||||
|
|
|
|||
|
|
@ -71,8 +71,6 @@ fun UserWalletsListManager.Companion.provideBiometricImplementation(
|
|||
)
|
||||
}
|
||||
|
||||
// TODO: Will be used in further MR's
|
||||
@Suppress("unused")
|
||||
fun UserWalletsListManager.Companion.provideRuntimeImplementation(): UserWalletsListManager {
|
||||
return RuntimeUserWalletsListManager()
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import com.tangem.common.CompletionResult
|
|||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.doOnFailure
|
||||
import com.tangem.common.doOnSuccess
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.common.flatMap
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
|
|
@ -12,6 +13,7 @@ import com.tangem.tap.common.analytics.events.AnalyticsParam
|
|||
import com.tangem.tap.common.analytics.events.Settings
|
||||
import com.tangem.tap.common.extensions.dispatchDialogShow
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.dispatchWithMain
|
||||
import com.tangem.tap.common.extensions.onUserWalletSelected
|
||||
import com.tangem.tap.common.redux.AppDialog
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
|
|
@ -20,11 +22,16 @@ import com.tangem.tap.common.redux.navigation.AppScreen
|
|||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.model.builders.UserWalletBuilder
|
||||
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
|
||||
import com.tangem.tap.domain.userWalletList.UserWalletsListManager
|
||||
import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation
|
||||
import com.tangem.tap.domain.userWalletList.di.provideRuntimeImplementation
|
||||
import com.tangem.tap.domain.userWalletList.isLockedSync
|
||||
import com.tangem.tap.domain.userWalletList.lockIfLockable
|
||||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.foregroundActivityObserver
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
|
|
@ -236,7 +243,7 @@ class DetailsMiddleware {
|
|||
delay(timeMillis = 100)
|
||||
}
|
||||
}
|
||||
store.dispatchOnMain(
|
||||
store.dispatchWithMain(
|
||||
DetailsAction.AppSettings.BiometricsStatusChanged(
|
||||
needEnrollBiometrics = tangemSdkManager.needEnrollBiometrics,
|
||||
),
|
||||
|
|
@ -252,13 +259,13 @@ class DetailsMiddleware {
|
|||
private fun toggleSaveWallets(state: DetailsState, enable: Boolean) = scope.launch {
|
||||
// Nothing to change
|
||||
if (preferencesStorage.shouldSaveUserWallets == enable) {
|
||||
store.dispatchOnMain(DetailsAction.AppSettings.SwitchPrivacySetting.Success)
|
||||
store.dispatchWithMain(DetailsAction.AppSettings.SwitchPrivacySetting.Success)
|
||||
return@launch
|
||||
}
|
||||
|
||||
toggleSaveWallets(state.scanResponse, enable)
|
||||
.doOnFailure {
|
||||
store.dispatchOnMain(
|
||||
store.dispatchWithMain(
|
||||
DetailsAction.AppSettings.SwitchPrivacySetting.Failure(
|
||||
prevState = !enable,
|
||||
setting = AppSetting.SaveWallets,
|
||||
|
|
@ -266,7 +273,7 @@ class DetailsMiddleware {
|
|||
)
|
||||
}
|
||||
.doOnSuccess {
|
||||
store.dispatchOnMain(DetailsAction.AppSettings.SwitchPrivacySetting.Success)
|
||||
store.dispatchWithMain(DetailsAction.AppSettings.SwitchPrivacySetting.Success)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -284,13 +291,13 @@ class DetailsMiddleware {
|
|||
private fun toggleSaveAccessCodes(state: DetailsState, enable: Boolean) = scope.launch {
|
||||
// Nothing to change
|
||||
if (preferencesStorage.shouldSaveAccessCodes == enable) {
|
||||
store.dispatchOnMain(DetailsAction.AppSettings.SwitchPrivacySetting.Success)
|
||||
store.dispatchWithMain(DetailsAction.AppSettings.SwitchPrivacySetting.Success)
|
||||
return@launch
|
||||
}
|
||||
|
||||
toggleSaveAccessCodes(state.scanResponse, state.appSettingsState.saveWallets, enable)
|
||||
.doOnFailure {
|
||||
store.dispatchOnMain(
|
||||
store.dispatchWithMain(
|
||||
DetailsAction.AppSettings.SwitchPrivacySetting.Failure(
|
||||
prevState = !enable,
|
||||
setting = AppSetting.SaveAccessCode,
|
||||
|
|
@ -298,7 +305,7 @@ class DetailsMiddleware {
|
|||
)
|
||||
}
|
||||
.doOnSuccess {
|
||||
store.dispatchOnMain(DetailsAction.AppSettings.SwitchPrivacySetting.Success)
|
||||
store.dispatchWithMain(DetailsAction.AppSettings.SwitchPrivacySetting.Success)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -327,6 +334,8 @@ class DetailsMiddleware {
|
|||
TangemSdkError.ExceptionError(IllegalStateException("scanResponse is null")),
|
||||
)
|
||||
|
||||
updateUserWalletsListManager(enableUserWalletsSaving = true)
|
||||
|
||||
return userWalletsListManager.save(userWallet)
|
||||
.flatMap {
|
||||
if (enableAccessCodesSaving) {
|
||||
|
|
@ -341,7 +350,7 @@ class DetailsMiddleware {
|
|||
preferencesStorage.shouldShowSaveUserWalletScreen = false
|
||||
preferencesStorage.shouldSaveUserWallets = true
|
||||
|
||||
store.onUserWalletSelected(userWallet)
|
||||
store.dispatchWithMain(WalletAction.UpdateCanSaveUserWallets(canSaveUserWallets = true))
|
||||
}
|
||||
.doOnFailure { error ->
|
||||
Timber.e(error, "Unable to save user wallet")
|
||||
|
|
@ -354,9 +363,11 @@ class DetailsMiddleware {
|
|||
.doOnSuccess {
|
||||
Analytics.send(Settings.AppSettings.SaveWalletSwitcherChanged(AnalyticsParam.OnOffState.Off))
|
||||
deleteSavedAccessCodes()
|
||||
updateUserWalletsListManager(enableUserWalletsSaving = false)
|
||||
preferencesStorage.shouldSaveUserWallets = false
|
||||
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
store.dispatchWithMain(WalletAction.UpdateCanSaveUserWallets(canSaveUserWallets = true))
|
||||
store.dispatchWithMain(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
}
|
||||
.doOnFailure { error ->
|
||||
Timber.e(error, "Unable to delete saved wallets")
|
||||
|
|
@ -388,5 +399,27 @@ class DetailsMiddleware {
|
|||
Timber.e(error, "Unable to delete saved access codes")
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun updateUserWalletsListManager(enableUserWalletsSaving: Boolean) {
|
||||
val manager = if (enableUserWalletsSaving) {
|
||||
createBiometricsUserWalletsManager() ?: return
|
||||
} else {
|
||||
UserWalletsListManager.provideRuntimeImplementation()
|
||||
}
|
||||
|
||||
store.dispatchWithMain(GlobalAction.UpdateUserWalletsListManager(manager))
|
||||
}
|
||||
|
||||
private fun createBiometricsUserWalletsManager(): UserWalletsListManager? {
|
||||
val context = foregroundActivityObserver.foregroundActivity?.applicationContext.guard {
|
||||
Timber.e(IllegalStateException("No activities in foreground"))
|
||||
return null
|
||||
}
|
||||
|
||||
return UserWalletsListManager.provideBiometricImplementation(
|
||||
context = context,
|
||||
tangemSdkManager = tangemSdkManager,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,13 +5,13 @@ import com.tangem.common.doOnResult
|
|||
import com.tangem.common.doOnSuccess
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.AnalyticsEvent
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.tap.common.analytics.events.IntroductionProcess
|
||||
import com.tangem.tap.common.analytics.events.Shop
|
||||
import com.tangem.tap.common.entities.IndeterminateProgressButton
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.dispatchOpenUrl
|
||||
import com.tangem.tap.common.extensions.eraseContext
|
||||
import com.tangem.tap.common.extensions.onCardScanned
|
||||
import com.tangem.tap.common.extensions.onUserWalletSelected
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
|
|
@ -94,29 +94,32 @@ private fun readCard(analyticsEvent: AnalyticsEvent?) = scope.launch {
|
|||
changeButtonState(ButtonState.ENABLED)
|
||||
},
|
||||
onSuccess = { scanResponse ->
|
||||
scope.launch {
|
||||
if (preferencesStorage.shouldSaveUserWallets) {
|
||||
val userWallet = UserWalletBuilder(scanResponse).build() ?: return@launch
|
||||
userWalletsListManager.save(userWallet)
|
||||
.doOnFailure { error ->
|
||||
Timber.e(error, "Unable to save user wallet")
|
||||
store.onCardScanned(scanResponse)
|
||||
}
|
||||
.doOnSuccess {
|
||||
scope.launch { store.onUserWalletSelected(userWallet) }
|
||||
}
|
||||
.doOnResult {
|
||||
navigateTo(AppScreen.Wallet)
|
||||
}
|
||||
} else {
|
||||
store.onCardScanned(scanResponse)
|
||||
navigateTo(AppScreen.Wallet)
|
||||
}
|
||||
}
|
||||
proceedWithScanResponse(scanResponse)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fun proceedWithScanResponse(scanResponse: ScanResponse) {
|
||||
scope.launch {
|
||||
val userWallet = UserWalletBuilder(scanResponse).build()
|
||||
if (userWallet == null) {
|
||||
Timber.e("User wallet not created")
|
||||
return@launch
|
||||
}
|
||||
|
||||
userWalletsListManager.save(userWallet)
|
||||
.doOnFailure { error ->
|
||||
Timber.e(error, "Unable to save user wallet")
|
||||
}
|
||||
.doOnSuccess {
|
||||
scope.launch { store.onUserWalletSelected(userWallet) }
|
||||
}
|
||||
.doOnResult {
|
||||
navigateTo(AppScreen.Wallet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun navigateTo(appScreen: AppScreen) {
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(appScreen))
|
||||
delay(timeMillis = 200)
|
||||
|
|
|
|||
|
|
@ -1,21 +1,27 @@
|
|||
package com.tangem.tap.features.onboarding
|
||||
|
||||
import com.tangem.common.doOnFailure
|
||||
import com.tangem.common.doOnSuccess
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.domain.common.ProductType
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.onCardScanned
|
||||
import com.tangem.tap.common.extensions.onUserWalletSelected
|
||||
import com.tangem.tap.common.extensions.removeContext
|
||||
import com.tangem.tap.common.extensions.setContext
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.model.builders.UserWalletBuilder
|
||||
import com.tangem.tap.features.saveWallet.redux.SaveWalletAction
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -75,7 +81,7 @@ object OnboardingHelper {
|
|||
// then open save wallet screen
|
||||
tangemSdkManager.canUseBiometry &&
|
||||
preferencesStorage.shouldShowSaveUserWalletScreen -> scope.launch {
|
||||
store.onCardScanned(scanResponse)
|
||||
proceedWithScanResponse(scanResponse)
|
||||
|
||||
delay(timeMillis = 1_200)
|
||||
|
||||
|
|
@ -90,7 +96,7 @@ object OnboardingHelper {
|
|||
}
|
||||
// If device has no biometry and save wallet screen has been shown, then go through old scenario
|
||||
else -> scope.launch {
|
||||
store.onCardScanned(scanResponse)
|
||||
proceedWithScanResponse(scanResponse)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -100,4 +106,19 @@ object OnboardingHelper {
|
|||
fun onInterrupted() {
|
||||
Analytics.removeContext()
|
||||
}
|
||||
|
||||
private suspend fun proceedWithScanResponse(scanResponse: ScanResponse) {
|
||||
val userWallet = UserWalletBuilder(scanResponse).build().guard {
|
||||
Timber.e("User wallet not created")
|
||||
return
|
||||
}
|
||||
|
||||
userWalletsListManager.save(userWallet)
|
||||
.doOnFailure { error ->
|
||||
Timber.e(error, "Unable to save user wallet")
|
||||
}
|
||||
.doOnSuccess {
|
||||
scope.launch { store.onUserWalletSelected(userWallet) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +1,26 @@
|
|||
package com.tangem.tap.features.saveWallet.redux
|
||||
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.doOnFailure
|
||||
import com.tangem.common.doOnSuccess
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.common.flatMap
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.MainScreen
|
||||
import com.tangem.tap.common.analytics.events.Onboarding
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.onUserWalletSelected
|
||||
import com.tangem.tap.common.extensions.dispatchWithMain
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.model.builders.UserWalletBuilder
|
||||
import com.tangem.tap.domain.userWalletList.UserWalletsListManager
|
||||
import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.foregroundActivityObserver
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
|
|
@ -21,6 +28,7 @@ import com.tangem.tap.tangemSdkManager
|
|||
import com.tangem.tap.userWalletsListManager
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Middleware
|
||||
import timber.log.Timber
|
||||
|
||||
internal class SaveWalletMiddleware {
|
||||
val middleware: Middleware<AppState> = { _, stateProvider ->
|
||||
|
|
@ -86,16 +94,19 @@ internal class SaveWalletMiddleware {
|
|||
}
|
||||
|
||||
scope.launch {
|
||||
val userWallet = UserWalletBuilder(scanResponse)
|
||||
.backupCardsIds(state.backupInfo?.backupCardsIds)
|
||||
.build() ?: return@launch
|
||||
val userWallet = userWalletsListManager.selectedUserWalletSync
|
||||
?: UserWalletBuilder(scanResponse)
|
||||
.backupCardsIds(state.backupInfo?.backupCardsIds)
|
||||
.build() ?: return@launch
|
||||
|
||||
provideBiometricUserWalletsListManager()
|
||||
|
||||
val isFirstSavedWallet = !userWalletsListManager.hasUserWallets
|
||||
|
||||
saveAccessCodeIfNeeded(state.backupInfo?.accessCode, userWallet.cardsInWallet)
|
||||
.flatMap { userWalletsListManager.save(userWallet, canOverride = true) }
|
||||
.doOnFailure { error ->
|
||||
store.dispatchOnMain(SaveWalletAction.Save.Error(error))
|
||||
store.dispatchWithMain(SaveWalletAction.Save.Error(error))
|
||||
}
|
||||
.doOnSuccess {
|
||||
preferencesStorage.shouldSaveUserWallets = true
|
||||
|
|
@ -104,14 +115,29 @@ internal class SaveWalletMiddleware {
|
|||
preferencesStorage.shouldSaveAccessCodes = isFirstSavedWallet ||
|
||||
preferencesStorage.shouldSaveAccessCodes
|
||||
|
||||
store.dispatchOnMain(SaveWalletAction.Save.Success)
|
||||
store.dispatchWithMain(SaveWalletAction.Save.Success)
|
||||
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet))
|
||||
store.onUserWalletSelected(userWallet)
|
||||
store.dispatchWithMain(NavigationAction.PopBackTo(AppScreen.Wallet))
|
||||
store.dispatchWithMain(WalletAction.UpdateCanSaveUserWallets(canSaveUserWallets = true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun provideBiometricUserWalletsListManager() {
|
||||
val context = foregroundActivityObserver.foregroundActivity?.applicationContext.guard {
|
||||
val error = IllegalStateException("No activities in foreground")
|
||||
Timber.e(error)
|
||||
store.dispatchWithMain(SaveWalletAction.Save.Error(TangemSdkError.ExceptionError(error)))
|
||||
return
|
||||
}
|
||||
val manager = UserWalletsListManager.provideBiometricImplementation(
|
||||
context = context,
|
||||
tangemSdkManager = tangemSdkManager,
|
||||
)
|
||||
|
||||
store.dispatchWithMain(GlobalAction.UpdateUserWalletsListManager(manager))
|
||||
}
|
||||
|
||||
private fun dismiss(state: SaveWalletState) {
|
||||
if (state.backupInfo != null) {
|
||||
// TODO: Remove after onboarding refactoring
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@ import java.math.BigDecimal
|
|||
|
||||
sealed class WalletAction : Action {
|
||||
|
||||
object PopBackToInitialScreen : WalletAction()
|
||||
|
||||
data class UpdateCanSaveUserWallets(val canSaveUserWallets: Boolean) : WalletAction()
|
||||
|
||||
data class ResetState(val newCard: CardDTO) : WalletAction()
|
||||
|
||||
data class SetIfTestnetCard(val isTestnet: Boolean) : WalletAction()
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ data class WalletState(
|
|||
val derivationsCheckIsScheduled: Boolean = false,
|
||||
val loadingUserTokens: Boolean = false,
|
||||
val walletCardsCount: Int? = null,
|
||||
val canSaveUserWallets: Boolean = false,
|
||||
) : StateType {
|
||||
|
||||
val walletsDataFromStores: List<WalletData>
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import com.tangem.tap.domain.loadedRates
|
|||
import com.tangem.tap.domain.model.WalletDataModel
|
||||
import com.tangem.tap.domain.model.WalletStoreModel
|
||||
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
|
||||
import com.tangem.tap.domain.userWalletList.lockIfLockable
|
||||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.home.redux.HomeAction
|
||||
import com.tangem.tap.features.send.redux.PrepareSendScreen
|
||||
|
|
@ -300,7 +301,7 @@ class WalletMiddleware {
|
|||
showSaveWalletIfNeeded()
|
||||
}
|
||||
is WalletAction.ChangeWallet -> {
|
||||
changeWallet()
|
||||
changeWallet(walletState)
|
||||
}
|
||||
is WalletAction.UserWalletChanged -> Unit
|
||||
is WalletAction.WalletStoresChanged -> {
|
||||
|
|
@ -311,6 +312,16 @@ class WalletMiddleware {
|
|||
tryToShowAppRatingWarning(action.walletStores)
|
||||
}
|
||||
is WalletAction.TotalFiatBalanceChanged -> Unit
|
||||
is WalletAction.PopBackToInitialScreen -> {
|
||||
userWalletsListManager.lockIfLockable()
|
||||
val screen = if (walletState.canSaveUserWallets) {
|
||||
AppScreen.Welcome
|
||||
} else {
|
||||
AppScreen.Home
|
||||
}
|
||||
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo(screen))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -375,9 +386,9 @@ class WalletMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun changeWallet() {
|
||||
private fun changeWallet(state: WalletState) {
|
||||
when {
|
||||
userWalletsListManager.hasUserWallets -> {
|
||||
state.canSaveUserWallets -> {
|
||||
Analytics.send(MainScreen.ButtonMyWallets())
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.WalletSelector))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -367,6 +367,11 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS
|
|||
),
|
||||
)
|
||||
}
|
||||
is WalletAction.UpdateCanSaveUserWallets -> {
|
||||
newState = newState.copy(
|
||||
canSaveUserWallets = action.canSaveUserWallets,
|
||||
)
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
appStateHolder.walletState = newState
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
|
|||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
|
||||
import com.tangem.tap.domain.statePrinter.printScanResponseState
|
||||
import com.tangem.tap.domain.statePrinter.printWalletState
|
||||
import com.tangem.tap.domain.userWalletList.lockIfLockable
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.features.wallet.redux.ErrorType
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
|
|
@ -44,7 +43,6 @@ import com.tangem.tap.features.wallet.ui.wallet.SingleWalletView
|
|||
import com.tangem.tap.features.wallet.ui.wallet.WalletView
|
||||
import com.tangem.tap.features.wallet.ui.wallet.saltPay.SaltPayWalletView
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import com.tangem.wallet.BuildConfig
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.FragmentWalletBinding
|
||||
|
|
@ -82,13 +80,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
this,
|
||||
object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
userWalletsListManager.lockIfLockable()
|
||||
val screen = if (userWalletsListManager.hasUserWallets) {
|
||||
AppScreen.Welcome
|
||||
} else {
|
||||
AppScreen.Home
|
||||
}
|
||||
store.dispatch(NavigationAction.PopBackTo(screen))
|
||||
store.dispatch(WalletAction.PopBackToInitialScreen)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
@ -202,7 +194,11 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
}
|
||||
}
|
||||
|
||||
val navigationIconRes = if (state.hasSavedWallets) R.drawable.ic_wallet_24 else R.drawable.ic_tap_card_24
|
||||
val navigationIconRes = if (state.canSaveUserWallets) {
|
||||
R.drawable.ic_wallet_24
|
||||
} else {
|
||||
R.drawable.ic_tap_card_24
|
||||
}
|
||||
binding.toolbar.setNavigationIcon(navigationIconRes)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,28 +3,55 @@ package com.tangem.tap.features.wallet.ui
|
|||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.domain.userWalletList.UserWalletsListManager
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import com.tangem.tap.walletStoresManager
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
// TODO: Kill me, please
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
internal class WalletViewModel : ViewModel(), StoreSubscriber<UserWalletsListManager?> {
|
||||
private var observeWalletStoresUpdatesJob: Job? = null
|
||||
set(value) {
|
||||
field?.cancel()
|
||||
field = value
|
||||
}
|
||||
|
||||
init {
|
||||
subscribeToUserWalletsListManagerUpdates()
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
store.unsubscribe(this)
|
||||
}
|
||||
|
||||
override fun newState(state: UserWalletsListManager?) {
|
||||
// Restarting observing of wallet store updates when the manager changes
|
||||
if (state != null) {
|
||||
bootstrapSelectedWalletStoresChanges(state)
|
||||
}
|
||||
}
|
||||
|
||||
internal class WalletViewModel : ViewModel() {
|
||||
fun launch() {
|
||||
bootstrapSelectedWalletStoresChanges()
|
||||
val manager = store.state.globalState.userWalletsListManager
|
||||
if (manager != null) {
|
||||
bootstrapSelectedWalletStoresChanges(manager)
|
||||
}
|
||||
bootstrapShowSaveWalletIfNeeded()
|
||||
}
|
||||
|
||||
private fun bootstrapSelectedWalletStoresChanges() {
|
||||
userWalletsListManager.selectedUserWallet
|
||||
private fun bootstrapSelectedWalletStoresChanges(manager: UserWalletsListManager) {
|
||||
observeWalletStoresUpdatesJob = manager.selectedUserWallet
|
||||
.map { it.walletId }
|
||||
.distinctUntilChanged()
|
||||
.flatMapLatest { selectedUserWalletId ->
|
||||
walletStoresManager.get(selectedUserWalletId)
|
||||
}
|
||||
|
|
@ -40,4 +67,14 @@ internal class WalletViewModel : ViewModel() {
|
|||
store.dispatchOnMain(WalletAction.ShowSaveWalletIfNeeded)
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribeToUserWalletsListManagerUpdates() {
|
||||
store.subscribe(this) { appState ->
|
||||
appState
|
||||
.skip { old, new ->
|
||||
old.globalState.userWalletsListManager == new.globalState.userWalletsListManager
|
||||
}
|
||||
.select { it.globalState.userWalletsListManager }
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue