Updated on 2026-08-14

This commit is contained in:
Tangem 2025-05-05 11:47:30 +03:00
parent 12fc61bbdc
commit 9334609daf
19 changed files with 297 additions and 206 deletions

View file

@ -43,6 +43,7 @@
android:resizeableActivity="@bool/resizeable_activity"
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:enableOnBackInvokedCallback="true"
tools:ignore="GoogleAppIndexingWarning"
tools:replace="android:allowBackup, android:fullBackupContent, android:label">

View file

@ -1,6 +1,5 @@
package com.tangem.tap.features.details.ui.common
import androidx.activity.compose.BackHandler
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.selection.selectable
@ -29,8 +28,6 @@ internal fun SettingsScreensScaffold(
) {
val backgroundColor = TangemTheme.colors.background.secondary
BackHandler(onBack = onBackClick)
Scaffold(
snackbarHost = { SnackbarHost(snackbarHostState) },
topBar = {

View file

@ -2,14 +2,18 @@ package com.tangem.tap.features.home
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import com.arkivanov.essenty.lifecycle.subscribe
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.components.SystemBarsIconsDisposable
import com.tangem.core.ui.res.LocalRootBackgroundColor
import com.tangem.core.ui.utils.findActivity
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.features.home.api.HomeComponent
@ -61,6 +65,17 @@ internal class DefaultHomeComponent @AssistedInject constructor(
onShopButtonClick = model::onShopClick,
onSearchTokensClick = model::onSearchClick,
)
val rootBackgroundColor = LocalRootBackgroundColor.current
val previousColor = remember { rootBackgroundColor.value }
val storiesBackgroundColor = Color(color = 0xFF010101)
DisposableEffect(Unit) {
rootBackgroundColor.value = storiesBackgroundColor
onDispose {
rootBackgroundColor.value = previousColor
}
}
}
override fun newState(state: HomeState) {

View file

@ -1,6 +1,7 @@
package com.tangem.tap.routing
import android.app.Activity
import android.os.Build
import android.os.Bundle
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
@ -8,7 +9,6 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -17,25 +17,31 @@ 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.fade
import com.arkivanov.decompose.extensions.compose.stack.animation.StackAnimation
import com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback.androidPredictiveBackAnimatable
import com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback.predictiveBackAnimation
import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation
import com.arkivanov.decompose.router.stack.ChildStack
import com.arkivanov.decompose.value.Value
import com.arkivanov.essenty.backhandler.BackHandler
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.LocalRootBackgroundColor
import com.tangem.core.ui.res.LocalSnackbarHostState
import com.tangem.core.ui.res.TangemTheme
import com.tangem.tap.routing.component.RoutingComponent
import com.tangem.tap.routing.transitions.RoutingTransitionAnimationFactory
@OptIn(ExperimentalDecomposeApi::class)
@Composable
internal fun RootContent(
stack: Value<ChildStack<AppRoute, RoutingComponent.Child>>,
wcContent: @Composable (modifier: Modifier) -> Unit,
backHandler: BackHandler,
uiDependencies: UiDependencies,
wcContent: @Composable (modifier: Modifier) -> Unit,
onBack: () -> Unit,
modifier: Modifier = Modifier,
) {
val context = LocalContext.current
@ -44,45 +50,62 @@ internal fun RootContent(
activity = context as Activity,
uiDependencies = uiDependencies,
) {
CompositionLocalProvider(
LocalIsNavigationRefactoringEnabled provides true,
) {
val snackbarHostState = LocalSnackbarHostState.current
val snackbarHostState = LocalSnackbarHostState.current
Box(Modifier.background(TangemTheme.colors.background.primary)) {
Children(
modifier = modifier,
animation = stackAnimation(fade()),
stack = stack,
) { child ->
when (val instance = child.instance) {
is RoutingComponent.Child.Initial -> Unit
is RoutingComponent.Child.ComposableComponent -> {
instance.component.Content(Modifier.fillMaxSize())
Box(Modifier.background(LocalRootBackgroundColor.current.value)) {
Children(
modifier = modifier,
animation = childrenAnimation(backHandler = backHandler, onBack = onBack),
stack = stack,
) { child ->
when (val instance = child.instance) {
is RoutingComponent.Child.Initial -> Unit
is RoutingComponent.Child.ComposableComponent -> {
instance.component.Content(Modifier.fillMaxSize())
}
is RoutingComponent.Child.LegacyIntent -> {
// TODO: Remove and use it's own router: [REDACTED_JIRA]
LaunchedEffect(instance) {
startActivity(context, instance.intent, Bundle.EMPTY)
}
is RoutingComponent.Child.LegacyIntent -> {
// TODO: Remove and use it's own router: [REDACTED_JIRA]
LaunchedEffect(instance) {
startActivity(context, instance.intent, Bundle.EMPTY)
}
}
is RoutingComponent.Child.LegacyFragment,
-> error("Unsupported child: $instance")
}
}
wcContent(Modifier.fillMaxSize())
TangemSnackbarHost(
modifier = Modifier
.align(Alignment.BottomCenter)
.navigationBarsPadding()
.padding(all = 16.dp),
hostState = snackbarHostState,
)
}
}
EventMessageEffect()
wcContent(Modifier.fillMaxSize())
TangemSnackbarHost(
modifier = Modifier
.align(Alignment.BottomCenter)
.navigationBarsPadding()
.padding(all = 16.dp),
hostState = snackbarHostState,
)
}
}
EventMessageEffect()
}
@OptIn(ExperimentalDecomposeApi::class)
private fun childrenAnimation(
backHandler: BackHandler,
onBack: () -> Unit,
): StackAnimation<AppRoute, RoutingComponent.Child> {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
predictiveBackAnimation(
backHandler = backHandler,
onBack = onBack,
selector = { backEvent, _, _ ->
androidPredictiveBackAnimatable(backEvent)
},
fallbackAnimation = stackAnimation {
RoutingTransitionAnimationFactory.create(it.configuration)
},
)
} else {
stackAnimation {
RoutingTransitionAnimationFactory.create(it.configuration)
}
}
}

View file

@ -2,11 +2,9 @@ package com.tangem.tap.routing.component
import android.content.Intent
import androidx.compose.runtime.Immutable
import androidx.fragment.app.Fragment
import com.tangem.common.routing.AppRoute
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.utils.Provider
internal interface RoutingComponent : ComposableContentComponent {
@ -16,11 +14,6 @@ internal interface RoutingComponent : ComposableContentComponent {
// TODO: Remove and find initial intent in RoutingComponent: [REDACTED_JIRA]
data object Initial : Child()
data class LegacyFragment(
val name: String,
val fragmentProvider: Provider<Fragment>,
) : Child()
data class LegacyIntent(val intent: Intent) : Child()
data class ComposableComponent(

View file

@ -74,6 +74,8 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
stack = stack,
uiDependencies = uiDependencies,
wcContent = { wcRoutingComponent.Content(it) },
backHandler = backHandler,
onBack = router::pop,
)
}

View file

@ -0,0 +1,92 @@
package com.tangem.tap.routing.transitions
import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.animation.core.tween
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.layout
import com.arkivanov.decompose.extensions.compose.stack.animation.*
import com.tangem.common.routing.AppRoute
import kotlin.compareTo
import kotlin.times
object RoutingTransitionAnimationFactory {
@Suppress("MagicNumber")
fun create(appRoute: AppRoute): StackAnimator {
return when (appRoute) {
is AppRoute.Onboarding,
is AppRoute.Welcome,
is AppRoute.Home,
-> fade(tween(400)).plus(scale(tween(400)))
is AppRoute.Wallet,
-> slideAndFade(directions = setOf(Direction.ENTER_BACK, Direction.EXIT_BACK))
.plus(
scaleWithDirection(
directions = setOf(Direction.ENTER_FRONT, Direction.EXIT_FRONT),
animationSpec = tween(400),
),
)
else -> slideAndFade()
}
}
private fun scaleWithDirection(
directions: Set<Direction>,
animationSpec: FiniteAnimationSpec<Float> = tween(),
frontFactor: Float = 1.15F,
backFactor: Float = 0.95F,
): StackAnimator = stackAnimator(animationSpec = animationSpec) { factor, direction, content ->
content(
Modifier.graphicsLayer {
val scaleFactor = if (directions.contains(direction)) {
if (factor >= 0F) {
factor * (frontFactor - 1F) + 1F
} else {
factor * (1F - backFactor) + 1F
}
} else {
1f
}
scaleX = scaleFactor
scaleY = scaleFactor
},
)
}
@Suppress("MagicNumber")
private fun slideAndFade(directions: Set<Direction>? = null): StackAnimator {
val easing = CubicBezierEasing(0.55f, 0.0f, 0.0f, 1f)
return stackAnimator(
animationSpec = tween(durationMillis = 400, easing = easing),
) { factor, direction, content ->
content(
if (directions == null || directions.contains(direction)) {
Modifier.offsetXFactor(factor)
} else {
Modifier
},
)
}.plus(
fade(
animationSpec = tween(
delayMillis = 50,
durationMillis = 300,
easing = easing,
),
),
)
}
@Suppress("MagicNumber")
private fun Modifier.offsetXFactor(factor: Float): Modifier = layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
layout(placeable.width, placeable.height) {
placeable.placeRelative(x = (placeable.width.toFloat() * factor * 0.15f).toInt(), y = 0)
}
}
}

View file

@ -87,6 +87,8 @@ fun TangemTheme(
DefaultHapticManager(view = view, vibratorHapticManager = vibratorHapticManager)
}
val rootBackgroundColor = rememberedColors.background.secondary
MaterialTheme(
colors = materialThemeColors(colors = themeColors, isDark = isDark),
) {
@ -105,6 +107,7 @@ fun TangemTheme(
CompositionLocalProvider(
LocalTangemShimmer provides TangemShimmer,
LocalMainBottomSheetColor provides remember { mutableStateOf(Color.Unspecified) },
LocalRootBackgroundColor provides remember { mutableStateOf(rootBackgroundColor) },
LocalTextSelectionColors provides TangemTextSelectionColors,
) {
ProvideTextStyle(
@ -308,13 +311,12 @@ val LocalMainBottomSheetColor = staticCompositionLocalOf<MutableState<Color>> {
error("No MainBottomSheetColor provided")
}
val LocalBladeAnimation = staticCompositionLocalOf<BladeAnimation> {
val LocalRootBackgroundColor = staticCompositionLocalOf<MutableState<Color>> {
error("No MainBottomSheetColor provided")
}
// TODO remove this after migration to new navigation
val LocalIsNavigationRefactoringEnabled = staticCompositionLocalOf<Boolean> {
false
val LocalBladeAnimation = staticCompositionLocalOf<BladeAnimation> {
error("No MainBottomSheetColor provided")
}
/**

View file

@ -1,7 +1,6 @@
package com.tangem.features.details.ui
import android.content.res.Configuration
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.scrollable
@ -41,8 +40,6 @@ internal fun DetailsScreen(
) {
val backgroundColor = TangemTheme.colors.background.secondary
BackHandler(onBack = state.popBack)
Scaffold(
modifier = modifier,
containerColor = backgroundColor,

View file

@ -1,6 +1,5 @@
package com.tangem.features.disclaimer.impl.component.impl
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
@ -24,8 +23,6 @@ internal class DefaultDisclaimerComponent @AssistedInject constructor(
@Composable
override fun Content(modifier: Modifier) {
val state by model.state.collectAsStateWithLifecycle()
BackHandler(onBack = state.popBack)
DisclaimerScreen(state = state)
}

View file

@ -1,6 +1,5 @@
package com.tangem.features.managetokens.component.impl
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
@ -42,8 +41,6 @@ internal class DefaultManageTokensComponent @AssistedInject constructor(
val state by model.state.collectAsStateWithLifecycle()
val bottomSheet by bottomSheetSlot.subscribeAsState()
BackHandler(onBack = state.popBack)
ManageTokensScreen(
modifier = modifier,
state = state,

View file

@ -124,10 +124,6 @@ internal class DefaultMarketsTokenDetailsComponent @AssistedInject constructor(
@Composable
override fun Content(modifier: Modifier) {
BackHandler {
navigateBack()
}
LifecycleStartEffect(Unit) {
model.isVisibleOnScreen.value = true
onStopOrDispose {

View file

@ -40,7 +40,7 @@ internal class DefaultMarketsEntryComponent @AssistedInject constructor(
source = stackNavigation,
serializer = Child.serializer(),
initialConfiguration = Child.TokenList,
handleBackButton = true,
handleBackButton = false,
childFactory = { configuration, factoryContext ->
marketsEntryChildFactory.createChild(
child = configuration,

View file

@ -2,7 +2,6 @@ package com.tangem.feature.referral.ui
import android.content.res.Configuration
import android.content.res.Resources
import androidx.activity.compose.BackHandler
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
@ -52,8 +51,6 @@ import kotlinx.coroutines.launch
internal fun ReferralScreen(stateHolder: ReferralStateHolder) {
val snackbarHostState = remember(::SnackbarHostState)
BackHandler(onBack = stateHolder.headerState.onBackClicked)
Scaffold(
topBar = {
AppBarWithBackButton(

View file

@ -1,12 +1,16 @@
package com.tangem.feature.stories.impl
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.common.ui.swapStoriesScreen.SwapStoriesScreen
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.res.LocalRootBackgroundColor
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.feature.stories.api.StoriesComponent
import com.tangem.feature.stories.impl.model.StoriesModel
import dagger.assisted.Assisted
@ -25,6 +29,16 @@ internal class DefaultStoriesComponent @AssistedInject constructor(
override fun Content(modifier: Modifier) {
val state = model.state.collectAsStateWithLifecycle()
SwapStoriesScreen(state.value)
val rootBackgroundColor = LocalRootBackgroundColor.current
val previousColor = remember { rootBackgroundColor.value }
DisposableEffect(Unit) {
rootBackgroundColor.value = TangemColorPalette.Black
onDispose {
rootBackgroundColor.value = previousColor
}
}
}
@AssistedFactory

View file

@ -1,7 +1,6 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.ui
import android.content.res.Configuration
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.*
import androidx.compose.material3.Scaffold
@ -53,7 +52,6 @@ internal fun TokenDetailsScreen(
tokenMarketBlockComponent: TokenMarketBlockComponent?,
txHistoryComponent: TxHistoryComponent?,
) {
BackHandler(onBack = state.topAppBarConfig.onBackClick)
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
Scaffold(

View file

@ -1,7 +1,6 @@
package com.tangem.feature.walletsettings.ui
import android.content.res.Configuration
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
@ -37,8 +36,6 @@ internal fun WalletSettingsScreen(
) {
val backgroundColor = TangemTheme.colors.background.secondary
BackHandler(onBack = state.popBack)
Scaffold(
modifier = modifier,
containerColor = backgroundColor,

View file

@ -1,10 +1,8 @@
package com.tangem.feature.wallet.child.wallet
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.arkivanov.decompose.extensions.compose.subscribeAsState
import com.arkivanov.decompose.router.slot.childSlot
@ -16,7 +14,6 @@ import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.decompose.ComposableDialogComponent
import com.tangem.core.ui.utils.findActivity
import com.tangem.feature.wallet.child.wallet.model.WalletModel
import com.tangem.feature.wallet.navigation.WalletRoute
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
@ -77,9 +74,6 @@ internal class WalletComponent @AssistedInject constructor(
@Composable
override fun Content(modifier: Modifier) {
val activity = LocalContext.current.findActivity()
BackHandler { activity.finish() }
val dialog by dialog.subscribeAsState()
WalletScreen(

View file

@ -8,6 +8,7 @@ import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideIn
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.*
@ -24,7 +25,6 @@ import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.geometry.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
@ -40,7 +40,6 @@ import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.paging.compose.collectAsLazyPagingItems
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheet
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
import com.tangem.common.ui.expressStatus.expressTransactionsItems
@ -59,7 +58,6 @@ import com.tangem.core.ui.components.snackbar.TangemSnackbar
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.event.StateEvent
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.LocalIsNavigationRefactoringEnabled
import com.tangem.core.ui.res.LocalMainBottomSheetColor
import com.tangem.core.ui.res.LocalWindowSize
import com.tangem.core.ui.res.TangemTheme
@ -336,108 +334,121 @@ private inline fun BaseScaffoldWithMarkets(
) {
val backgroundColor = LocalMainBottomSheetColor.current
var isSearchFieldFocused by remember { mutableStateOf(false) }
val isNavBarVisible = remember { mutableStateOf(true) }
BottomSheetStateEffects(
bottomSheetState = bottomSheetState,
alertConfig = alertConfig,
onBottomSheetStateChange = onBottomSheetStateChange,
navigationBarVisible = isNavBarVisible,
isSearchFieldFocused = isSearchFieldFocused,
)
TangemBottomSheetScaffold(
snackbarHost = {
WalletSnackbarHost(
snackbarHostState = it,
event = state.event,
modifier = Modifier
.padding(bottom = TangemTheme.dimens.spacing4)
.navigationBarsPadding(),
)
},
containerColor = TangemTheme.colors.background.secondary,
sheetContainerColor = backgroundColor.value,
scaffoldState = scaffoldState,
sheetPeekHeight = peekHeight,
sheetShape = TangemTheme.shapes.bottomSheetLarge,
sheetContent = {
// hide bottom sheet when back pressed
BackHandler(
isKeyboardVisible.not() &&
bottomSheetState.currentValue == TangemSheetValue.Expanded,
) {
coroutineScope.launch { bottomSheetState.partialExpand() }
}
Column(
modifier = Modifier.sizeIn(maxHeight = maxHeight - statusBarHeight),
) {
Hand(Modifier.drawBehind { drawRect(backgroundColor.value) })
Box(
Box {
TangemBottomSheetScaffold(
snackbarHost = {
WalletSnackbarHost(
snackbarHostState = it,
event = state.event,
modifier = Modifier
// expand bottom sheet when clicked on the header
.clickable(
enabled = bottomSheetState.currentValue == TangemSheetValue.PartiallyExpanded,
indication = null,
interactionSource = null,
) {
coroutineScope.launch { bottomSheetState.expand() }
}
.onFocusChanged {
isSearchFieldFocused = it.isFocused
},
) {
bottomSheetContent()
}
}
},
content = { paddingValues ->
Box {
MarketsHint(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = peekHeight + 12.dp)
.fillMaxWidth(fraction = .4f),
isVisible = showMarketsHint,
.padding(bottom = TangemTheme.dimens.spacing4)
.navigationBarsPadding(),
)
},
containerColor = TangemTheme.colors.background.secondary,
sheetContainerColor = backgroundColor.value,
scaffoldState = scaffoldState,
sheetPeekHeight = peekHeight,
sheetShape = TangemTheme.shapes.bottomSheetLarge,
sheetContent = {
// hide bottom sheet when back pressed
BackHandler(
isKeyboardVisible.not() &&
bottomSheetState.currentValue == TangemSheetValue.Expanded,
) {
coroutineScope.launch { bottomSheetState.partialExpand() }
}
Column {
WalletTopBar(config = state.topBarConfig)
TangemPullToRefreshContainer(config = selectedWallet.pullToRefreshConfig) {
content(paddingValues)
Column(
modifier = Modifier.sizeIn(maxHeight = maxHeight - statusBarHeight),
) {
Hand(Modifier.drawBehind { drawRect(backgroundColor.value) })
Box(
modifier = Modifier
// expand bottom sheet when clicked on the header
.clickable(
enabled = bottomSheetState.currentValue == TangemSheetValue.PartiallyExpanded,
indication = null,
interactionSource = null,
) {
coroutineScope.launch { bottomSheetState.expand() }
}
.onFocusChanged {
isSearchFieldFocused = it.isFocused
},
) {
bottomSheetContent()
}
}
},
content = { paddingValues ->
Box {
MarketsHint(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = peekHeight + 12.dp)
.fillMaxWidth(fraction = .4f),
isVisible = showMarketsHint,
)
BottomSheetScrim(
color = if (state.showMarketsOnboarding) {
Color.Black.copy(alpha = .65f)
} else {
BottomSheetDefaults.ScrimColor
},
visible = bottomSheetState.targetValue == TangemSheetValue.Expanded ||
state.showMarketsOnboarding,
onDismissRequest = {
coroutineScope.launch { bottomSheetState.partialExpand() }
state.onDismissMarketsOnboarding()
},
)
Column {
WalletTopBar(config = state.topBarConfig)
TangemPullToRefreshContainer(config = selectedWallet.pullToRefreshConfig) {
content(paddingValues)
}
}
MarketsTooltip(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 24.dp)
.fillMaxWidth(fraction = 0.7f),
isVisible = state.showMarketsOnboarding,
availableHeight = if (LocalIsNavigationRefactoringEnabled.current) {
maxHeight
} else {
maxHeight - statusBarHeight - bottomBarHeight
},
bottomSheetState = bottomSheetState,
)
}
},
)
BottomSheetScrim(
color = if (state.showMarketsOnboarding) {
Color.Black.copy(alpha = .65f)
} else {
BottomSheetDefaults.ScrimColor
},
visible = bottomSheetState.targetValue == TangemSheetValue.Expanded ||
state.showMarketsOnboarding,
onDismissRequest = {
coroutineScope.launch { bottomSheetState.partialExpand() }
state.onDismissMarketsOnboarding()
},
)
MarketsTooltip(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 24.dp)
.fillMaxWidth(fraction = 0.7f),
isVisible = state.showMarketsOnboarding,
availableHeight = maxHeight,
bottomSheetState = bottomSheetState,
)
}
},
)
AnimatedVisibility(
modifier = Modifier.align(Alignment.BottomCenter),
visible = isNavBarVisible.value,
) {
Box(
Modifier
.align(Alignment.BottomCenter)
.background(backgroundColor.value)
.height(bottomBarHeight)
.fillMaxWidth(),
)
}
}
LaunchedEffect(state.showMarketsOnboarding, bottomSheetState.targetValue) {
if (state.showMarketsOnboarding && bottomSheetState.targetValue == TangemSheetValue.Expanded) {
@ -599,49 +610,17 @@ private fun BottomSheetScrim(color: Color, visible: Boolean, onDismissRequest: (
private fun BottomSheetStateEffects(
bottomSheetState: TangemSheetState,
alertConfig: WalletAlertState?,
navigationBarVisible: MutableState<Boolean>,
onBottomSheetStateChange: (BottomSheetState) -> Unit,
isSearchFieldFocused: Boolean,
) {
val systemUiController = rememberSystemUiController()
val navigationBarColor = TangemTheme.colors.background.primary
LaunchedEffect(navigationBarColor) {
delay(timeMillis = 100)
when (bottomSheetState.currentValue) {
TangemSheetValue.Hidden,
TangemSheetValue.Expanded,
-> systemUiController.setNavigationBarColor(
color = Color.Transparent,
darkIcons = navigationBarColor.luminance() > 0.5f,
navigationBarContrastEnforced = true,
)
TangemSheetValue.PartiallyExpanded,
-> systemUiController.setNavigationBarColor(navigationBarColor)
}
}
LaunchedEffect(bottomSheetState.targetValue, navigationBarColor) {
LaunchedEffect(bottomSheetState.targetValue) {
when (bottomSheetState.targetValue) {
TangemSheetValue.Hidden,
TangemSheetValue.Expanded,
-> systemUiController.setNavigationBarColor(
color = Color.Transparent,
darkIcons = navigationBarColor.luminance() > 0.5f,
navigationBarContrastEnforced = true,
)
-> navigationBarVisible.value = false
TangemSheetValue.PartiallyExpanded,
-> systemUiController.setNavigationBarColor(navigationBarColor)
}
}
// make navigation bar transparent when leaving the screen
DisposableEffect(Unit) {
onDispose {
systemUiController.setNavigationBarColor(
color = Color.Transparent,
darkIcons = navigationBarColor.luminance() > 0.5f,
navigationBarContrastEnforced = false,
)
-> navigationBarVisible.value = true
}
}