Updated on 2026-08-14

This commit is contained in:
Tangem 2024-06-11 15:52:15 +04:00
parent 084d4282f7
commit 7a10191880
6 changed files with 297 additions and 2 deletions

View file

@ -0,0 +1,18 @@
package com.tangem.tap.di.routing
import com.tangem.tap.routing.RoutingComponent
import com.tangem.tap.routing.impl.DefaultRoutingComponent
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityComponent
import dagger.hilt.android.scopes.ActivityScoped
@Module
@InstallIn(ActivityComponent::class)
internal interface RoutingComponentModule {
@Binds
@ActivityScoped
fun bindRoutingComponentFactory(factory: DefaultRoutingComponent.Factory): RoutingComponent.Factory
}

View file

@ -0,0 +1,33 @@
package com.tangem.tap.routing
import android.content.Intent
import androidx.fragment.app.Fragment
import com.arkivanov.decompose.router.stack.ChildStack
import com.arkivanov.decompose.value.Value
import com.tangem.common.routing.AppRoute
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.navigation.Router
import com.tangem.utils.Provider
internal interface RoutingComponent {
val router: Router
val stack: Value<ChildStack<AppRoute, Child>>
sealed class Child {
data object Initial : Child()
data class LegacyFragment(
val name: String,
val fragmentProvider: Provider<Fragment>,
) : Child()
data class LegacyIntent(val intent: Intent) : Child()
}
interface Factory {
fun create(context: AppComponentContext): RoutingComponent
}
}

View file

@ -0,0 +1,49 @@
package com.tangem.tap.routing.impl
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.router.stack.ChildStack
import com.arkivanov.decompose.router.stack.childStack
import com.arkivanov.decompose.value.Value
import com.arkivanov.essenty.backhandler.BackCallback
import com.tangem.common.routing.AppRoute
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.navigation.getOrCreateTyped
import com.tangem.tap.routing.RoutingComponent
import com.tangem.tap.routing.RoutingComponent.Child
import com.tangem.tap.routing.utils.ChildFactory
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@Suppress("LongParameterList")
internal class DefaultRoutingComponent @AssistedInject constructor(
@Assisted context: AppComponentContext,
private val childFactory: ChildFactory,
) : RoutingComponent, AppComponentContext by context {
private val backCallback = BackCallback(priority = Int.MIN_VALUE, onBack = router::pop)
override val stack: Value<ChildStack<AppRoute, Child>> = childStack(
source = navigationProvider.getOrCreateTyped(),
serializer = AppRoute.serializer(),
initialConfiguration = getInitialRoute(),
handleBackButton = false,
childFactory = ::child,
)
init {
backHandler.register(backCallback)
}
private fun getInitialRoute(): AppRoute = AppRoute.Initial
private fun child(route: AppRoute, context: ComponentContext): Child {
return childFactory.createChild(route, { childByContext(context) })
}
@AssistedFactory
interface Factory : RoutingComponent.Factory {
override fun create(context: AppComponentContext): DefaultRoutingComponent
}
}

View file

@ -0,0 +1,155 @@
package com.tangem.tap.routing.utils
import com.tangem.common.routing.AppRoute
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.feature.qrscanning.QrScanningRouter
import com.tangem.feature.referral.ReferralFragment
import com.tangem.feature.swap.presentation.SwapFragment
import com.tangem.features.details.component.DetailsComponent
import com.tangem.features.send.api.navigation.SendRouter
import com.tangem.features.staking.api.navigation.StakingRouter
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
import com.tangem.features.wallet.navigation.WalletRouter
import com.tangem.tap.features.customtoken.impl.presentation.AddCustomTokenFragment
import com.tangem.tap.features.details.ui.appcurrency.AppCurrencySelectorFragment
import com.tangem.tap.features.details.ui.appsettings.AppSettingsFragment
import com.tangem.tap.features.details.ui.cardsettings.CardSettingsFragment
import com.tangem.tap.features.details.ui.cardsettings.coderecovery.AccessCodeRecoveryFragment
import com.tangem.tap.features.details.ui.resetcard.ResetCardFragment
import com.tangem.tap.features.details.ui.securitymode.SecurityModeFragment
import com.tangem.tap.features.details.ui.walletconnect.WalletConnectFragment
import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment
import com.tangem.tap.features.home.HomeFragment
import com.tangem.tap.features.main.ui.ModalNotificationBottomSheetFragment
import com.tangem.tap.features.onboarding.products.note.OnboardingNoteFragment
import com.tangem.tap.features.onboarding.products.otherCards.OnboardingOtherCardsFragment
import com.tangem.tap.features.onboarding.products.twins.ui.OnboardingTwinsFragment
import com.tangem.tap.features.onboarding.products.wallet.ui.OnboardingWalletFragment
import com.tangem.tap.features.saveWallet.ui.SaveWalletBottomSheetFragment
import com.tangem.tap.features.tokens.impl.presentation.TokensListFragment
import com.tangem.tap.features.welcome.ui.WelcomeFragment
import com.tangem.tap.routing.RoutingComponent.Child
import com.tangem.utils.Provider
import dagger.hilt.android.scopes.ActivityScoped
import java.util.WeakHashMap
import javax.inject.Inject
@ActivityScoped
@Suppress("LongParameterList")
internal class ChildFactory @Inject constructor(
private val detailsComponentFactory: DetailsComponent.Factory,
private val sendRouter: SendRouter,
private val tokenDetailsRouter: TokenDetailsRouter,
private val walletRouter: WalletRouter,
private val qrScanningRouter: QrScanningRouter,
private val stakingRouter: StakingRouter,
) {
@Suppress("LongMethod", "CyclomaticComplexMethod")
fun createChild(route: AppRoute, contextFactory: (route: AppRoute) -> AppComponentContext): Child {
componentContexts[route] = contextFactory(route)
return when (route) {
is AppRoute.Initial -> {
Child.Initial
}
is AppRoute.AccessCodeRecovery -> {
route.asFragmentChild(Provider { AccessCodeRecoveryFragment() })
}
is AppRoute.AddCustomToken -> {
route.asFragmentChild(Provider { AddCustomTokenFragment() })
}
is AppRoute.AppCurrencySelector -> {
route.asFragmentChild(Provider { AppCurrencySelectorFragment() })
}
is AppRoute.ModalNotification -> {
route.asFragmentChild(Provider { ModalNotificationBottomSheetFragment() })
}
is AppRoute.SaveWallet -> {
route.asFragmentChild(Provider { SaveWalletBottomSheetFragment() })
}
is AppRoute.Send -> {
route.asFragmentChild(Provider { sendRouter.getEntryFragment() })
}
is AppRoute.AppSettings -> {
route.asFragmentChild(Provider { AppSettingsFragment() })
}
is AppRoute.CardSettings -> {
route.asFragmentChild(Provider { CardSettingsFragment() })
}
is AppRoute.Details -> {
route.asComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = DetailsComponent.Params(route.userWalletId),
componentFactory = detailsComponentFactory,
)
}
is AppRoute.DetailsSecurity -> {
route.asFragmentChild(Provider { SecurityModeFragment() })
}
is AppRoute.Disclaimer -> {
route.asFragmentChild(Provider { DisclaimerFragment() })
}
is AppRoute.Home -> {
route.asFragmentChild(Provider { HomeFragment() })
}
is AppRoute.ManageTokens -> {
route.asFragmentChild(Provider { TokensListFragment() })
}
is AppRoute.OnboardingNote -> {
route.asFragmentChild(Provider { OnboardingNoteFragment() })
}
is AppRoute.OnboardingOther -> {
route.asFragmentChild(Provider { OnboardingOtherCardsFragment() })
}
is AppRoute.OnboardingTwins -> {
route.asFragmentChild(Provider { OnboardingTwinsFragment() })
}
is AppRoute.OnboardingWallet -> {
route.asFragmentChild(Provider { OnboardingWalletFragment() })
}
is AppRoute.QrScanning -> {
route.asFragmentChild(Provider { qrScanningRouter.getEntryFragment() })
}
is AppRoute.ReferralProgram -> {
route.asFragmentChild(Provider { ReferralFragment() })
}
is AppRoute.ResetToFactory -> {
route.asFragmentChild(Provider { ResetCardFragment() })
}
is AppRoute.Swap -> {
route.asFragmentChild(Provider { SwapFragment() })
}
is AppRoute.Wallet -> {
route.asFragmentChild(Provider { walletRouter.getEntryFragment() })
}
is AppRoute.WalletConnectSessions -> {
route.asFragmentChild(Provider { WalletConnectFragment() })
}
is AppRoute.CurrencyDetails -> {
route.asFragmentChild(Provider { tokenDetailsRouter.getEntryFragment() })
}
is AppRoute.Welcome -> {
route.asFragmentChild(Provider { WelcomeFragment() })
}
is AppRoute.TesterMenu -> {
TODO("Will be implemented later")
}
is AppRoute.Staking -> {
route.asFragmentChild(Provider { stakingRouter.getEntryFragment() })
}
}
}
private fun contextProvider(
appRoute: AppRoute,
contextFactory: (route: AppRoute) -> AppComponentContext,
): Provider<AppComponentContext> = Provider {
componentContexts.getOrPut(appRoute) { contextFactory(appRoute) }
}
private companion object {
val componentContexts = WeakHashMap<AppRoute, AppComponentContext>()
}
}

View file

@ -0,0 +1,39 @@
package com.tangem.tap.routing.utils
import androidx.fragment.app.Fragment
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.bundle.RouteBundleParams
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.ComposableContentComponent
import com.tangem.tap.DecomposeFragment
import com.tangem.tap.routing.RoutingComponent.Child
import com.tangem.utils.Provider
internal fun AppRoute.asFragmentChild(fragmentProvider: Provider<Fragment>): Child {
val provider = Provider {
val bundle = (this as? RouteBundleParams)?.getBundle()
fragmentProvider().apply {
arguments = bundle
}
}
return Child.LegacyFragment(path, provider)
}
internal fun <C : ComposableContentComponent, P : Any, F : ComponentFactory<C, P>> AppRoute.asComponentChild(
contextProvider: Provider<AppComponentContext>,
params: P,
componentFactory: F,
): Child {
val fragmentProvider = Provider {
DecomposeFragment.newInstance(
contextProvider = contextProvider,
params = params,
componentFactory = componentFactory,
)
}
return Child.LegacyFragment(path, fragmentProvider)
}