Updated on 2026-08-14

This commit is contained in:
Tangem 2025-03-31 18:44:49 +03:00
parent 377f2cd6fd
commit ded577faff
7 changed files with 77 additions and 68 deletions

View file

@ -489,7 +489,9 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
* `android:configChanges="uiMode"` is set in the manifest.
* */
updateAppBackground()
if (routingFeatureToggles.isNavigationRefactoringEnabled.not()) {
updateAppBackground()
}
}
private fun updateAppBackground() {

View file

@ -2,28 +2,36 @@ package com.tangem.tap.routing
import android.app.Activity
import android.os.Bundle
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat.startActivity
import com.arkivanov.decompose.ExperimentalDecomposeApi
import com.arkivanov.decompose.extensions.compose.stack.Children
import com.arkivanov.decompose.extensions.compose.stack.animation.*
import com.arkivanov.decompose.router.stack.ChildStack
import com.arkivanov.decompose.value.Value
import com.tangem.common.routing.AppRoute
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.components.snackbar.TangemSnackbarHost
import com.tangem.core.ui.message.EventMessageEffect
import com.tangem.core.ui.res.LocalIsNavigationRefactoringEnabled
import com.tangem.core.ui.res.LocalSnackbarHostState
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.WindowInsetsZero
import com.tangem.tap.routing.component.RoutingComponent
@OptIn(ExperimentalDecomposeApi::class)
@Composable
internal fun RootContent(
stack: ChildStack<AppRoute, RoutingComponent.Child>,
stack: Value<ChildStack<AppRoute, RoutingComponent.Child>>,
uiDependencies: UiDependencies,
modifier: Modifier = Modifier,
) {
@ -33,21 +41,15 @@ internal fun RootContent(
activity = context as Activity,
uiDependencies = uiDependencies,
) {
val snackbarHostState = LocalSnackbarHostState.current
CompositionLocalProvider(
LocalIsNavigationRefactoringEnabled provides true,
) {
val snackbarHostState = LocalSnackbarHostState.current
Scaffold(
modifier = modifier,
containerColor = TangemTheme.colors.background.primary,
snackbarHost = {
TangemSnackbarHost(
modifier = Modifier.padding(all = 16.dp),
hostState = snackbarHostState,
)
},
contentWindowInsets = WindowInsetsZero,
content = { paddingValues ->
Box(Modifier.background(TangemTheme.colors.background.primary)) {
Children(
modifier = Modifier.padding(paddingValues),
modifier = modifier,
animation = stackAnimation(fade()),
stack = stack,
) { child ->
when (val instance = child.instance) {
@ -63,8 +65,16 @@ internal fun RootContent(
-> error("Unsupported child: $instance")
}
}
},
)
TangemSnackbarHost(
modifier = Modifier
.align(Alignment.BottomCenter)
.navigationBarsPadding()
.padding(all = 16.dp),
hostState = snackbarHostState,
)
}
}
EventMessageEffect()
}

View file

@ -4,12 +4,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.extensions.compose.subscribeAsState
import com.arkivanov.decompose.router.stack.ChildStack
import com.arkivanov.decompose.router.stack.childStack
import com.arkivanov.decompose.value.Value
import com.arkivanov.decompose.value.subscribe
import com.arkivanov.essenty.backhandler.BackCallback
import com.arkivanov.essenty.lifecycle.doOnDestroy
import com.google.android.material.snackbar.Snackbar
import com.tangem.common.routing.AppRoute
@ -43,19 +41,15 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
AppComponentContext by context,
SnackbarHandler {
private val backCallback = BackCallback(priority = Int.MIN_VALUE, onBack = router::pop)
override val stack: Value<ChildStack<AppRoute, Child>> = childStack(
source = navigationProvider.getOrCreateTyped(),
initialStack = { getInitialStackOrInit() },
serializer = null, // AppRoute.serializer(), // Disabled until Nav refactoring completes
handleBackButton = false,
handleBackButton = routingFeatureToggles.isNavigationRefactoringEnabled,
childFactory = ::child,
)
init {
backHandler.register(backCallback)
lifecycle.doOnDestroy {
childFactory.doOnDestroy()
}
@ -77,8 +71,6 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
@Composable
override fun Content(modifier: Modifier) {
val stack by this.stack.subscribeAsState()
RootContent(
modifier = modifier,
stack = stack,

View file

@ -87,31 +87,28 @@ internal class ChildFactory @Inject constructor(
fun createChild(route: AppRoute, contextFactory: (route: AppRoute) -> AppComponentContext): Child {
return if (routingFeatureToggles.isNavigationRefactoringEnabled) {
createChildNew(route, contextFactory)
createChildNew(route, contextFactory(route))
} else {
createChildLegacy(route, contextFactory)
}
}
@Suppress("LongMethod", "CyclomaticComplexMethod")
private fun createChildNew(route: AppRoute, contextFactory: (route: AppRoute) -> AppComponentContext): Child {
componentContexts[route] = contextFactory(route)
// region Child creation
private fun createChildNew(route: AppRoute, context: AppComponentContext): Child {
return when (route) {
is AppRoute.Initial -> {
Child.Initial
}
is AppRoute.Details -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = DetailsComponent.Params(route.userWalletId),
componentFactory = detailsComponentFactory,
)
}
is AppRoute.Disclaimer -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = DisclaimerComponent.Params(route.isTosAccepted),
componentFactory = disclaimerComponentFactory,
)
@ -124,14 +121,14 @@ internal class ChildFactory @Inject constructor(
}
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = ManageTokensComponent.Params(route.userWalletId, source),
componentFactory = manageTokensComponentFactory,
)
}
is AppRoute.Welcome -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = WelcomeComponent.Params(
intent = route.intent,
),
@ -143,14 +140,14 @@ internal class ChildFactory @Inject constructor(
}
is AppRoute.WalletSettings -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = WalletSettingsComponent.Params(route.userWalletId),
componentFactory = walletSettingsComponentFactory,
)
}
is AppRoute.MarketsTokenDetails -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = MarketsTokenDetailsComponent.Params(
token = route.token,
appCurrency = route.appCurrency,
@ -167,7 +164,7 @@ internal class ChildFactory @Inject constructor(
}
is AppRoute.Onramp -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = OnrampComponent.Params(
userWalletId = route.userWalletId,
cryptoCurrency = route.currency,
@ -178,35 +175,35 @@ internal class ChildFactory @Inject constructor(
}
is AppRoute.OnrampSuccess -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = OnrampSuccessComponent.Params(route.externalTxId),
componentFactory = onrampSuccessComponentFactory,
)
}
is AppRoute.BuyCrypto -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = BuyCryptoComponent.Params(userWalletId = route.userWalletId),
componentFactory = buyCryptoComponentFactory,
)
}
is AppRoute.SellCrypto -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = SellCryptoComponent.Params(userWalletId = route.userWalletId),
componentFactory = sellCryptoComponentFactory,
)
}
is AppRoute.SwapCrypto -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = SwapSelectTokensComponent.Params(userWalletId = route.userWalletId),
componentFactory = swapSelectTokensComponentFactory,
)
}
is AppRoute.Onboarding -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = OnboardingEntryComponent.Params(
scanResponse = route.scanResponse,
mode = when (route.mode) {
@ -222,7 +219,7 @@ internal class ChildFactory @Inject constructor(
}
is AppRoute.Stories -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = StoriesComponent.Params(
storyId = route.storyId,
nextScreen = route.nextScreen,
@ -233,7 +230,7 @@ internal class ChildFactory @Inject constructor(
}
is AppRoute.CurrencyDetails -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = TokenDetailsComponent.Params(
userWalletId = route.userWalletId,
currency = route.currency,
@ -243,7 +240,7 @@ internal class ChildFactory @Inject constructor(
}
is AppRoute.Staking -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = StakingComponent.Params(
userWalletId = route.userWalletId,
cryptoCurrencyId = route.cryptoCurrencyId,
@ -254,7 +251,7 @@ internal class ChildFactory @Inject constructor(
}
is AppRoute.Swap -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = SwapComponent.Params(
currencyFrom = route.currencyFrom,
currencyTo = route.currencyTo,
@ -268,7 +265,7 @@ internal class ChildFactory @Inject constructor(
is AppRoute.Send -> {
if (sendFeatureToggles.isSendV2Enabled) {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = com.tangem.features.send.v2.api.SendComponent.Params(
userWalletId = route.userWalletId,
currency = route.currency,
@ -281,7 +278,7 @@ internal class ChildFactory @Inject constructor(
)
} else {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = SendComponent.Params(
userWalletId = route.userWalletId,
currency = route.currency,
@ -296,7 +293,7 @@ internal class ChildFactory @Inject constructor(
}
is AppRoute.Home -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = Unit,
componentFactory = homeComponentFactory,
)
@ -304,13 +301,13 @@ internal class ChildFactory @Inject constructor(
is AppRoute.WalletConnectSessions -> {
if (walletConnectFeatureToggles.isRedesignedWalletConnectEnabled) {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = Unit,
componentFactory = redesignedWalletConnectComponentFactory,
)
} else {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = Unit,
componentFactory = walletConnectComponentFactory,
)
@ -322,7 +319,7 @@ internal class ChildFactory @Inject constructor(
is AppRoute.QrScanning.Source.WalletConnect -> SourceType.WALLET_CONNECT
}
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = QrScanningComponent.Params(
source = source,
networkName = (route.source as? AppRoute.QrScanning.Source.Send)?.networkName,
@ -332,35 +329,35 @@ internal class ChildFactory @Inject constructor(
}
is AppRoute.AccessCodeRecovery -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = Unit,
componentFactory = accessCodeRecoveryComponentFactory,
)
}
is AppRoute.CardSettings -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = CardSettingsComponent.Params(userWalletId = route.userWalletId),
componentFactory = cardSettingsComponentFactory,
)
}
is AppRoute.AppCurrencySelector -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = Unit,
componentFactory = appCurrencySelectorComponentFactory,
)
}
is AppRoute.AppSettings -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = Unit,
componentFactory = appSettingsComponentFactory,
)
}
is AppRoute.DetailsSecurity -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = SecurityModeComponent.Params(
userWalletId = route.userWalletId,
),
@ -369,7 +366,7 @@ internal class ChildFactory @Inject constructor(
}
is AppRoute.ResetToFactory -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = ResetCardComponent.Params(
userWalletId = route.userWalletId,
cardId = route.cardId,
@ -381,21 +378,21 @@ internal class ChildFactory @Inject constructor(
}
is AppRoute.ReferralProgram -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = ReferralComponent.Params(route.userWalletId),
componentFactory = referralComponentFactory,
)
}
is AppRoute.PushNotification -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = Unit,
componentFactory = pushNotificationsComponentFactory,
)
}
is AppRoute.Wallet -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
context = context,
params = Unit,
componentFactory = walletComponentFactory,
)

View file

@ -40,11 +40,9 @@ internal fun <C : ComposableContentComponent, P : Any, F : ComponentFactory<P, C
}
internal fun <C : ComposableContentComponent, P : Any, F : ComponentFactory<P, C>> createComponentChild(
contextProvider: Provider<AppComponentContext>,
context: AppComponentContext,
params: P,
componentFactory: F,
): Child {
val context = contextProvider()
return Child.ComposableComponent(componentFactory.create(context, params))
}