Updated on 2026-08-14

This commit is contained in:
Tangem 2023-08-29 15:36:58 +08:00
parent 5d13d36cb3
commit 29fb436bc2
10 changed files with 84 additions and 31 deletions

View file

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

View file

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

View file

@ -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<AppScreen> = mainStore?.state?.navigationState?.backStack.orEmpty()
override fun dispatch(action: Action) {
mainStore?.dispatch(action)
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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