diff --git a/app/src/main/java/com/tangem/tap/di/AppStateHolderModule.kt b/app/src/main/java/com/tangem/tap/di/AppStateHolderModule.kt index 5016fe7d2e..e1bf363dd7 100644 --- a/app/src/main/java/com/tangem/tap/di/AppStateHolderModule.kt +++ b/app/src/main/java/com/tangem/tap/di/AppStateHolderModule.kt @@ -1,6 +1,6 @@ package com.tangem.tap.di -import com.tangem.core.navigation.NavigationStateHolder +import com.tangem.core.navigation.ReduxNavController import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.wallets.legacy.WalletsStateHolder import com.tangem.tap.proxy.AppStateHolder @@ -20,7 +20,7 @@ internal interface AppStateHolderModule { @Binds @Singleton - fun bindsNavigationStateHolder(appStateHolder: AppStateHolder): NavigationStateHolder + fun bindsNavigationStateHolder(appStateHolder: AppStateHolder): ReduxNavController @Binds @Singleton diff --git a/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt index 4897db1d24..eaf0da329c 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt @@ -1,7 +1,11 @@ package com.tangem.tap.di.domain +import com.tangem.domain.settings.CanUseBiometryUseCase import com.tangem.domain.settings.IsUserAlreadyRateAppUseCase +import com.tangem.domain.settings.ShouldShowSaveWalletScreenUseCase import com.tangem.domain.settings.repositories.SettingsRepository +import com.tangem.tap.domain.TangemSdkManager +import com.tangem.tap.domain.settings.DefaultLegacySettingsRepository import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -17,4 +21,20 @@ internal object SettingsDomainModule { fun providesGetWalletsUseCase(settingsRepository: SettingsRepository): IsUserAlreadyRateAppUseCase { return IsUserAlreadyRateAppUseCase(settingsRepository = settingsRepository) } + + @Provides + @ViewModelScoped + fun providesShouldShowSaveWalletScreenUseCase( + settingsRepository: SettingsRepository, + ): ShouldShowSaveWalletScreenUseCase { + return ShouldShowSaveWalletScreenUseCase(settingsRepository = settingsRepository) + } + + @Provides + @ViewModelScoped + fun providesCanUseBiometryUseCase(tangemSdkManager: TangemSdkManager): CanUseBiometryUseCase { + return CanUseBiometryUseCase( + legacySettingsRepository = DefaultLegacySettingsRepository(tangemSdkManager = tangemSdkManager), + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt b/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt index b89e7cbe68..cdcbc7fd9a 100644 --- a/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt +++ b/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt @@ -1,7 +1,8 @@ package com.tangem.tap.proxy +import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction -import com.tangem.core.navigation.NavigationStateHolder +import com.tangem.core.navigation.ReduxNavController import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.redux.ReduxStateHolder @@ -23,7 +24,7 @@ import javax.inject.Inject * Holds objects from old modules, that missing in DI graph. * Object sets manually to use in new modules and [AppStateHolder] proxies its to DI. */ -class AppStateHolder @Inject constructor() : WalletsStateHolder, NavigationStateHolder, ReduxStateHolder { +class AppStateHolder @Inject constructor() : WalletsStateHolder, ReduxNavController, ReduxStateHolder { override var userWalletsListManager: UserWalletsListManager? = null set(value) { @@ -53,6 +54,8 @@ class AppStateHolder @Inject constructor() : WalletsStateHolder, NavigationState mainStore?.dispatch(action) } + override fun getBackStack(): List = mainStore?.state?.navigationState?.backStack.orEmpty() + override fun dispatch(action: Action) { mainStore?.dispatch(action) } diff --git a/core/navigation/src/main/java/com/tangem/core/navigation/NavigationStateHolder.kt b/core/navigation/src/main/java/com/tangem/core/navigation/ReduxNavController.kt similarity index 53% rename from core/navigation/src/main/java/com/tangem/core/navigation/NavigationStateHolder.kt rename to core/navigation/src/main/java/com/tangem/core/navigation/ReduxNavController.kt index 7504b75656..ff8e8ced9e 100644 --- a/core/navigation/src/main/java/com/tangem/core/navigation/NavigationStateHolder.kt +++ b/core/navigation/src/main/java/com/tangem/core/navigation/ReduxNavController.kt @@ -1,12 +1,14 @@ package com.tangem.core.navigation /** - * Navigation state holder + * Navigation controller that based on redux actions * [REDACTED_AUTHOR] */ -interface NavigationStateHolder { +interface ReduxNavController { /** Navigate by [action] */ fun navigate(action: NavigationAction) + + fun getBackStack(): List } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/di/TokenDetailsRouterModule.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/di/TokenDetailsRouterModule.kt index 3a0dc79713..b87eb5b538 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/di/TokenDetailsRouterModule.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/di/TokenDetailsRouterModule.kt @@ -1,6 +1,6 @@ package com.tangem.feature.tokendetails.di -import com.tangem.core.navigation.NavigationStateHolder +import com.tangem.core.navigation.ReduxNavController import com.tangem.feature.tokendetails.presentation.router.DefaultTokenDetailsRouter import com.tangem.features.tokendetails.navigation.TokenDetailsRouter import dagger.Module @@ -15,7 +15,7 @@ internal object TokenDetailsRouterModule { @Provides @ActivityScoped - fun provideTokenDetailsRouter(navigationStateHolder: NavigationStateHolder): TokenDetailsRouter { - return DefaultTokenDetailsRouter(navigationStateHolder) + fun provideTokenDetailsRouter(reduxNavController: ReduxNavController): TokenDetailsRouter { + return DefaultTokenDetailsRouter(reduxNavController) } } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/DefaultTokenDetailsRouter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/DefaultTokenDetailsRouter.kt index 797a2d9194..ef23464cb0 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/DefaultTokenDetailsRouter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/DefaultTokenDetailsRouter.kt @@ -2,20 +2,20 @@ package com.tangem.feature.tokendetails.presentation.router import androidx.fragment.app.Fragment import com.tangem.core.navigation.NavigationAction -import com.tangem.core.navigation.NavigationStateHolder +import com.tangem.core.navigation.ReduxNavController import com.tangem.feature.tokendetails.presentation.TokenDetailsFragment internal class DefaultTokenDetailsRouter( - private val navigationStateHolder: NavigationStateHolder, + private val reduxNavController: ReduxNavController, ) : InnerTokenDetailsRouter { override fun getEntryFragment(): Fragment = TokenDetailsFragment() override fun popBackStack() { - navigationStateHolder.navigate(NavigationAction.PopBackTo()) + reduxNavController.navigate(NavigationAction.PopBackTo()) } override fun openUrl(url: String) { - navigationStateHolder.navigate(NavigationAction.OpenUrl(url = url)) + reduxNavController.navigate(NavigationAction.OpenUrl(url = url)) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/di/WalletRouterModule.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/di/WalletRouterModule.kt index 721bffead3..1e64fc4122 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/di/WalletRouterModule.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/di/WalletRouterModule.kt @@ -1,6 +1,6 @@ package com.tangem.feature.wallet.di -import com.tangem.core.navigation.NavigationStateHolder +import com.tangem.core.navigation.ReduxNavController import com.tangem.feature.wallet.presentation.router.DefaultWalletRouter import com.tangem.features.wallet.navigation.WalletRouter import dagger.Module @@ -15,7 +15,7 @@ internal object WalletRouterModule { @Provides @ActivityScoped - fun provideWalletRouter(navigationStateHolder: NavigationStateHolder): WalletRouter { - return DefaultWalletRouter(navigationStateHolder = navigationStateHolder) + fun provideWalletRouter(reduxNavController: ReduxNavController): WalletRouter { + return DefaultWalletRouter(reduxNavController = reduxNavController) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index 1aa417f71f..d5b55532ef 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -18,7 +18,7 @@ import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction -import com.tangem.core.navigation.NavigationStateHolder +import com.tangem.core.navigation.ReduxNavController import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.wallet.presentation.WalletFragment @@ -30,7 +30,7 @@ import com.tangem.features.tokendetails.navigation.TokenDetailsRouter import kotlin.properties.Delegates /** Default implementation of wallet feature router */ -internal class DefaultWalletRouter(private val navigationStateHolder: NavigationStateHolder) : InnerWalletRouter { +internal class DefaultWalletRouter(private val reduxNavController: ReduxNavController) : InnerWalletRouter { private var navController: NavHostController by Delegates.notNull() private var fragmentManager: FragmentManager by Delegates.notNull() @@ -71,7 +71,7 @@ internal class DefaultWalletRouter(private val navigationStateHolder: Navigation } } - override fun popBackStack() { + override fun popBackStack(screen: AppScreen?) { /* * It's hack that avoid issue with closing the wallet screen. * We are using NavGraph only inside feature so first backstack's element is entry of NavGraph and @@ -79,7 +79,11 @@ internal class DefaultWalletRouter(private val navigationStateHolder: Navigation * If backstack contains only NavGraph entry and wallet screen entry then we close the wallet fragment. */ if (navController.backQueue.size == BACKSTACK_ENTRY_COUNT_TO_CLOSE_WALLET_SCREEN) { - fragmentManager.popBackStack() + if (screen != null) { + reduxNavController.navigate(action = NavigationAction.PopBackTo(screen)) + } else { + fragmentManager.popBackStack() + } } else { navController.popBackStack() } @@ -92,19 +96,19 @@ internal class DefaultWalletRouter(private val navigationStateHolder: Navigation override fun openDetailsScreen() { // FIXME: Prepare details screen (e.g. dispatch action: `DetailsAction.PrepareScreen`) // [REDACTED_JIRA] - navigationStateHolder.navigate(action = NavigationAction.NavigateTo(AppScreen.Details)) + reduxNavController.navigate(action = NavigationAction.NavigateTo(AppScreen.Details)) } override fun openOnboardingScreen() { - navigationStateHolder.navigate(action = NavigationAction.NavigateTo(AppScreen.OnboardingWallet)) + reduxNavController.navigate(action = NavigationAction.NavigateTo(AppScreen.OnboardingWallet)) } override fun openTxHistoryWebsite(url: String) { - navigationStateHolder.navigate(action = NavigationAction.OpenUrl(url)) + reduxNavController.navigate(action = NavigationAction.OpenUrl(url)) } override fun openTokenDetails(currency: CryptoCurrency) { - navigationStateHolder.navigate( + reduxNavController.navigate( action = NavigationAction.NavigateTo( screen = AppScreen.WalletDetails, // TODO: [REDACTED_JIRA] @@ -113,10 +117,12 @@ internal class DefaultWalletRouter(private val navigationStateHolder: Navigation ) } - override fun openStoriesScreen() { - navigationStateHolder.navigate(action = NavigationAction.NavigateTo(screen = AppScreen.Home)) + override fun openSaveUserWalletScreen() { + reduxNavController.navigate(action = NavigationAction.NavigateTo(AppScreen.SaveWallet)) } + override fun isWalletLastScreen(): Boolean = reduxNavController.getBackStack().lastOrNull() == AppScreen.Wallet + private companion object { const val BACKSTACK_ENTRY_COUNT_TO_CLOSE_WALLET_SCREEN = 2 } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt index 510e8034ef..71a7c8bf17 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt @@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.router import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import androidx.fragment.app.FragmentManager +import com.tangem.core.navigation.AppScreen import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId import com.tangem.features.wallet.navigation.WalletRouter @@ -28,7 +29,7 @@ internal interface InnerWalletRouter : WalletRouter { fun Initialize(fragmentManager: FragmentManager) /** Pop back stack */ - fun popBackStack() + fun popBackStack(screen: AppScreen? = null) /** Open organize tokens screen */ fun openOrganizeTokensScreen(userWalletId: UserWalletId) @@ -45,6 +46,9 @@ internal interface InnerWalletRouter : WalletRouter { /** Open token details screen */ fun openTokenDetails(currency: CryptoCurrency) - /** Open stories screen */ - fun openStoriesScreen() + /** Open save user wallet screen */ + fun openSaveUserWalletScreen() + + /** Is wallet last screen */ + fun isWalletLastScreen(): Boolean } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 288a73f73e..2633a939df 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -8,6 +8,7 @@ import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.common.Provider import com.tangem.common.doOnFailure import com.tangem.common.doOnSuccess +import com.tangem.core.navigation.AppScreen import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency @@ -17,7 +18,9 @@ import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.redux.ReduxStateHolder +import com.tangem.domain.settings.CanUseBiometryUseCase import com.tangem.domain.settings.IsUserAlreadyRateAppUseCase +import com.tangem.domain.settings.ShouldShowSaveWalletScreenUseCase import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase import com.tangem.domain.tokens.GetPrimaryCurrencyUseCase import com.tangem.domain.tokens.GetTokenListUseCase @@ -81,6 +84,9 @@ internal class WalletViewModel @Inject constructor( private val unlockWalletsUseCase: UnlockWalletsUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase, + private val shouldShowSaveWalletScreenUseCase: ShouldShowSaveWalletScreenUseCase, + private val canUseBiometryUseCase: CanUseBiometryUseCase, + private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase, private val reduxStateHolder: ReduxStateHolder, private val dispatchers: CoroutineDispatcherProvider, ) : ViewModel(), DefaultLifecycleObserver, WalletClickIntents { @@ -123,6 +129,14 @@ internal class WalletViewModel @Inject constructor( private val notificationsJobHolder = JobHolder() override fun onCreate(owner: LifecycleOwner) { + viewModelScope.launch(dispatchers.main) { + delay(timeMillis = 1_800) + + if (router.isWalletLastScreen() && shouldShowSaveWalletScreenUseCase() && canUseBiometryUseCase()) { + router.openSaveUserWalletScreen() + } + } + getWalletsUseCase() .flowWithLifecycle(owner.lifecycle) .distinctUntilChanged() @@ -280,7 +294,11 @@ internal class WalletViewModel @Inject constructor( private fun getCardTypeResolver(index: Int): CardTypesResolver = getWallet(index).scanResponse.cardTypesResolver - override fun onBackClick() = router.popBackStack() + override fun onBackClick() { + viewModelScope.launch(dispatchers.main) { + router.popBackStack(screen = if (shouldSaveUserWalletsUseCase()) AppScreen.Welcome else AppScreen.Home) + } + } override fun onScanCardClick() { val prevRequestPolicyStatus = getBiometricsStatusUseCase() @@ -525,7 +543,7 @@ internal class WalletViewModel @Inject constructor( val either = deleteWalletUseCase(userWalletId) val state = requireNotNull(uiState as? WalletState.ContentState) - if (state.walletsListConfig.wallets.size <= 1 && either.isRight()) router.openStoriesScreen() + if (state.walletsListConfig.wallets.size <= 1 && either.isRight()) onBackClick() } }