Updated on 2026-08-14

This commit is contained in:
Tangem 2023-03-02 11:28:40 +03:00
commit 13ef9b4242
17 changed files with 256 additions and 77 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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