Updated on 2026-08-14
This commit is contained in:
parent
7a10191880
commit
e4e18ffddd
11 changed files with 226 additions and 3 deletions
|
|
@ -2,6 +2,7 @@ package com.tangem.tap
|
|||
|
||||
import com.tangem.TangemSdkLogger
|
||||
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.analytics.filter.OneTimeEventFilter
|
||||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||
import com.tangem.core.navigation.share.ShareManager
|
||||
|
|
@ -116,4 +117,6 @@ interface ApplicationEntryPoint {
|
|||
fun getUrlOpener(): UrlOpener
|
||||
|
||||
fun getShareManager(): ShareManager
|
||||
|
||||
fun getAppRouter(): AppRouter
|
||||
}
|
||||
|
|
@ -27,9 +27,13 @@ import androidx.lifecycle.flowWithLifecycle
|
|||
import androidx.lifecycle.lifecycleScope
|
||||
import arrow.core.getOrElse
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import com.arkivanov.decompose.value.observe
|
||||
import com.arkivanov.essenty.lifecycle.asEssentyLifecycle
|
||||
import com.google.android.material.snackbar.BaseTransientBottomBar
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.di.RootAppComponentContext
|
||||
import com.tangem.core.deeplink.DeepLinksRegistry
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
|
|
@ -60,6 +64,7 @@ import com.tangem.tap.common.DialogManager
|
|||
import com.tangem.tap.common.OnActivityResultCallback
|
||||
import com.tangem.tap.common.SnackbarHandler
|
||||
import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder
|
||||
import com.tangem.tap.common.extensions.showFragmentAllowingStateLoss
|
||||
import com.tangem.tap.common.redux.NotificationsHandler
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
|
||||
|
|
@ -72,6 +77,8 @@ import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
|
|||
import com.tangem.tap.features.welcome.ui.WelcomeFragment
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphAction
|
||||
import com.tangem.tap.routing.RoutingComponent
|
||||
import com.tangem.tap.routing.configurator.AppRouterConfig
|
||||
import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler
|
||||
import com.tangem.wallet.BuildConfig
|
||||
import com.tangem.wallet.R
|
||||
|
|
@ -162,6 +169,16 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
@Inject
|
||||
lateinit var stakingRouter: StakingRouter
|
||||
|
||||
@Inject
|
||||
@RootAppComponentContext
|
||||
internal lateinit var rootComponentContext: AppComponentContext
|
||||
|
||||
@Inject
|
||||
internal lateinit var appRouterConfig: AppRouterConfig
|
||||
|
||||
@Inject
|
||||
internal lateinit var routingComponentFactory: RoutingComponent.Factory
|
||||
|
||||
internal val viewModel: MainViewModel by viewModels()
|
||||
|
||||
private lateinit var appThemeModeFlow: SharedFlow<AppThemeMode?>
|
||||
|
|
@ -188,6 +205,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
observeAppThemeModeUpdates()
|
||||
|
||||
setContentView(R.layout.activity_main)
|
||||
installRouting()
|
||||
initContent()
|
||||
|
||||
checkForNotificationPermission()
|
||||
|
|
@ -199,6 +217,29 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
}
|
||||
}
|
||||
|
||||
private fun installRouting() {
|
||||
val routingComponent = routingComponentFactory.create(
|
||||
context = rootComponentContext,
|
||||
)
|
||||
|
||||
appRouterConfig.routerScope = lifecycleScope
|
||||
appRouterConfig.componentRouter = routingComponent.router
|
||||
|
||||
routingComponent.stack.observe(lifecycle.asEssentyLifecycle()) { childStack ->
|
||||
appRouterConfig.baclStack = childStack.backStack.map { it.configuration }
|
||||
|
||||
when (val child = childStack.active.instance) {
|
||||
is RoutingComponent.Child.Initial -> Unit
|
||||
is RoutingComponent.Child.LegacyFragment -> {
|
||||
supportFragmentManager.showFragmentAllowingStateLoss(child.name, child.fragmentProvider)
|
||||
}
|
||||
is RoutingComponent.Child.LegacyIntent -> {
|
||||
startActivity(child.intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeStateUpdates() {
|
||||
viewModel.state
|
||||
.flowWithLifecycle(lifecycle)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import com.tangem.LogFormat
|
|||
import com.tangem.TangemSdkLogger
|
||||
import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder
|
||||
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.filter.OneTimeEventFilter
|
||||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||
|
|
@ -193,6 +194,9 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
|
|||
|
||||
private val shareManager
|
||||
get() = entryPoint.getShareManager()
|
||||
|
||||
private val appRouter: AppRouter
|
||||
get() = entryPoint.getAppRouter()
|
||||
// endregion
|
||||
|
||||
override fun onCreate() {
|
||||
|
|
@ -284,6 +288,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
|
|||
detailsEntryPoint = detailsEntryPoint,
|
||||
urlOpener = urlOpener,
|
||||
shareManager = shareManager,
|
||||
appRouter = appRouter,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -28,9 +28,53 @@ import com.tangem.tap.features.tokens.impl.presentation.TokensListFragment
|
|||
import com.tangem.tap.features.welcome.ui.WelcomeFragment
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.wallet.R
|
||||
import timber.log.Timber
|
||||
|
||||
fun FragmentManager.showFragmentAllowingStateLoss(name: String, fragmentProvider: Provider<Fragment>) {
|
||||
Timber.d("Showing $name route")
|
||||
|
||||
val isPoppedBack = popBackStackImmediate(name, 0)
|
||||
|
||||
if (!isPoppedBack) {
|
||||
val fragment = fragmentProvider()
|
||||
|
||||
if (fragment is DialogFragment) {
|
||||
fragment.showDialog(fragmentManager = this, name)
|
||||
} else {
|
||||
fragment.showFragment(fragmentManager = this, name)
|
||||
}
|
||||
|
||||
Timber.d("Route $name is shown")
|
||||
} else {
|
||||
Timber.d("Route $name is found in backstack and shown")
|
||||
}
|
||||
}
|
||||
|
||||
private fun DialogFragment.showDialog(fragmentManager: FragmentManager, name: String) {
|
||||
val transaction = fragmentManager.beginTransaction()
|
||||
|
||||
try {
|
||||
transaction.addToBackStack(name)
|
||||
show(transaction, name)
|
||||
} catch (e: IllegalStateException) {
|
||||
transaction.add(this, name)
|
||||
transaction.addToBackStack(name)
|
||||
|
||||
transaction.commitAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Fragment.showFragment(fragmentManager: FragmentManager, name: String) {
|
||||
val transaction = fragmentManager.beginTransaction()
|
||||
|
||||
transaction.replace(R.id.fragment_container, this, name)
|
||||
transaction.addToBackStack(name)
|
||||
|
||||
transaction.commitAllowingStateLoss()
|
||||
}
|
||||
|
||||
fun FragmentActivity.openFragment(
|
||||
screen: AppScreen,
|
||||
addToBackstack: Boolean,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.tap.di.routing
|
||||
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.tap.routing.ProxyAppRouter
|
||||
import com.tangem.tap.routing.configurator.AppRouterConfig
|
||||
import com.tangem.tap.routing.configurator.MutableAppRouterConfig
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object AppRouterModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideAppRouter(config: AppRouterConfig, dispatchers: CoroutineDispatcherProvider): AppRouter =
|
||||
ProxyAppRouter(config, dispatchers)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideAppRouterConfigurator(): AppRouterConfig = MutableAppRouterConfig()
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.proxy.redux
|
|||
|
||||
import com.tangem.TangemSdkLogger
|
||||
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.navigation.email.EmailSender
|
||||
import com.tangem.core.navigation.share.ShareManager
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
|
|
@ -85,4 +86,5 @@ data class DaggerGraphState(
|
|||
val stakingRouter: StakingRouter? = null,
|
||||
val urlOpener: UrlOpener? = null,
|
||||
val shareManager: ShareManager? = null,
|
||||
val appRouter: AppRouter? = null,
|
||||
) : StateType
|
||||
61
app/src/main/java/com/tangem/tap/routing/ProxyAppRouter.kt
Normal file
61
app/src/main/java/com/tangem/tap/routing/ProxyAppRouter.kt
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
package com.tangem.tap.routing
|
||||
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.tap.routing.configurator.AppRouterConfig
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
internal class ProxyAppRouter(
|
||||
private val config: AppRouterConfig,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : AppRouter {
|
||||
|
||||
private val routerScope: CoroutineScope
|
||||
get() = requireNotNull(config.routerScope) {
|
||||
"Router scope is not set in config"
|
||||
}
|
||||
|
||||
private val innerRouter: Router
|
||||
get() = requireNotNull(config.componentRouter) {
|
||||
"Inner router is not set in config"
|
||||
}
|
||||
|
||||
override val stack: List<AppRoute>
|
||||
get() = requireNotNull(config.stack) {
|
||||
"Stack is not set in config"
|
||||
}
|
||||
|
||||
override fun push(route: AppRoute, onComplete: (isSuccess: Boolean) -> Unit) {
|
||||
routerScope.launch(dispatchers.mainImmediate) {
|
||||
innerRouter.push(route, onComplete)
|
||||
}
|
||||
}
|
||||
|
||||
override fun replaceAll(vararg routes: AppRoute, onComplete: (isSuccess: Boolean) -> Unit) {
|
||||
routerScope.launch(dispatchers.mainImmediate) {
|
||||
innerRouter.replaceAll(*routes, onComplete = onComplete)
|
||||
}
|
||||
}
|
||||
|
||||
override fun pop(onComplete: (isSuccess: Boolean) -> Unit) {
|
||||
routerScope.launch(dispatchers.mainImmediate) {
|
||||
innerRouter.pop(onComplete)
|
||||
}
|
||||
}
|
||||
|
||||
override fun popTo(route: AppRoute, onComplete: (isSuccess: Boolean) -> Unit) {
|
||||
routerScope.launch(dispatchers.mainImmediate) {
|
||||
innerRouter.popTo(route, onComplete)
|
||||
}
|
||||
}
|
||||
|
||||
override fun popTo(routeClass: KClass<out AppRoute>, onComplete: (isSuccess: Boolean) -> Unit) {
|
||||
routerScope.launch(dispatchers.mainImmediate) {
|
||||
innerRouter.popTo(routeClass, onComplete)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.tap.routing.configurator
|
||||
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
internal interface AppRouterConfig {
|
||||
|
||||
var routerScope: CoroutineScope?
|
||||
var componentRouter: Router?
|
||||
var stack: List<AppRoute>?
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.tap.routing.configurator
|
||||
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
internal class MutableAppRouterConfig : AppRouterConfig {
|
||||
|
||||
override var routerScope: CoroutineScope? = null
|
||||
override var componentRouter: Router? = null
|
||||
override var stack: List<AppRoute>? = null
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue