Updated on 2026-08-14
This commit is contained in:
parent
d42cb2a79e
commit
00c5775f03
7 changed files with 70 additions and 29 deletions
|
|
@ -28,6 +28,7 @@ import com.arkivanov.decompose.value.Value
|
||||||
import com.arkivanov.essenty.backhandler.BackHandler
|
import com.arkivanov.essenty.backhandler.BackHandler
|
||||||
import com.tangem.common.routing.AppRoute
|
import com.tangem.common.routing.AppRoute
|
||||||
import com.tangem.core.ui.UiDependencies
|
import com.tangem.core.ui.UiDependencies
|
||||||
|
import com.tangem.core.ui.components.haze.ProvideHaze
|
||||||
import com.tangem.core.ui.components.snackbar.TangemSnackbarHost
|
import com.tangem.core.ui.components.snackbar.TangemSnackbarHost
|
||||||
import com.tangem.core.ui.components.snackbar.TangemTopSnackbarHost
|
import com.tangem.core.ui.components.snackbar.TangemTopSnackbarHost
|
||||||
import com.tangem.core.ui.message.EventMessageEffect
|
import com.tangem.core.ui.message.EventMessageEffect
|
||||||
|
|
@ -72,7 +73,9 @@ internal fun RootContent(
|
||||||
when (val instance = child.instance) {
|
when (val instance = child.instance) {
|
||||||
is RoutingComponent.Child.Initial -> Unit
|
is RoutingComponent.Child.Initial -> Unit
|
||||||
is RoutingComponent.Child.ComposableComponent -> {
|
is RoutingComponent.Child.ComposableComponent -> {
|
||||||
instance.component.Content(Modifier.fillMaxSize())
|
ProvideHaze {
|
||||||
|
instance.component.Content(Modifier.fillMaxSize())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
is RoutingComponent.Child.LegacyIntent -> {
|
is RoutingComponent.Child.LegacyIntent -> {
|
||||||
// TODO: Remove and use it's own router: [REDACTED_JIRA]
|
// TODO: Remove and use it's own router: [REDACTED_JIRA]
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,12 @@ import androidx.compose.animation.core.CubicBezierEasing
|
||||||
import androidx.compose.animation.core.FiniteAnimationSpec
|
import androidx.compose.animation.core.FiniteAnimationSpec
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.CompositingStrategy
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.layout.layout
|
import androidx.compose.ui.layout.layout
|
||||||
import com.arkivanov.decompose.extensions.compose.stack.animation.*
|
import com.arkivanov.decompose.extensions.compose.stack.animation.*
|
||||||
import com.tangem.common.routing.AppRoute
|
import com.tangem.common.routing.AppRoute
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
object RoutingTransitionAnimationFactory {
|
object RoutingTransitionAnimationFactory {
|
||||||
|
|
||||||
|
|
@ -18,13 +20,15 @@ object RoutingTransitionAnimationFactory {
|
||||||
is AppRoute.Home,
|
is AppRoute.Home,
|
||||||
-> fade(tween(400)).plus(scale(tween(400)))
|
-> fade(tween(400)).plus(scale(tween(400)))
|
||||||
is AppRoute.Wallet,
|
is AppRoute.Wallet,
|
||||||
-> slideAndFade(directions = setOf(Direction.ENTER_BACK, Direction.EXIT_BACK))
|
-> slideAndFade(
|
||||||
.plus(
|
slideDirections = setOf(Direction.ENTER_BACK, Direction.EXIT_BACK),
|
||||||
scaleWithDirection(
|
fadeDirections = emptySet(),
|
||||||
directions = setOf(Direction.ENTER_FRONT, Direction.EXIT_FRONT),
|
).plus(
|
||||||
animationSpec = tween(400),
|
scaleWithDirection(
|
||||||
),
|
directions = setOf(Direction.ENTER_FRONT, Direction.EXIT_FRONT),
|
||||||
)
|
animationSpec = tween(400),
|
||||||
|
),
|
||||||
|
)
|
||||||
else -> slideAndFade()
|
else -> slideAndFade()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -52,28 +56,64 @@ object RoutingTransitionAnimationFactory {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param slideDirections directions in which the horizontal slide is applied.
|
||||||
|
* `null` (default) means slide in all directions.
|
||||||
|
* @param fadeDirections directions in which the alpha fade is applied.
|
||||||
|
* `null` (default) means fade in all directions. Pass `emptySet()` to disable the fade
|
||||||
|
* entirely — useful for screens that own a `hazeEffect` (e.g. WalletTopBar's progressive
|
||||||
|
* blur), where wrapping the screen in an animated `graphicsLayer { alpha = ... }` causes
|
||||||
|
* a visible blink over the blurred region.
|
||||||
|
*/
|
||||||
@Suppress("MagicNumber")
|
@Suppress("MagicNumber")
|
||||||
private fun slideAndFade(directions: Set<Direction>? = null): StackAnimator {
|
private fun slideAndFade(
|
||||||
|
slideDirections: Set<Direction>? = null,
|
||||||
|
fadeDirections: Set<Direction>? = null,
|
||||||
|
): StackAnimator {
|
||||||
val easing = CubicBezierEasing(a = 0.55f, b = 0.0f, c = 0.0f, d = 1f)
|
val easing = CubicBezierEasing(a = 0.55f, b = 0.0f, c = 0.0f, d = 1f)
|
||||||
|
|
||||||
return stackAnimator(
|
val slide = stackAnimator(
|
||||||
animationSpec = tween(durationMillis = 400, easing = easing),
|
animationSpec = tween(durationMillis = 400, easing = easing),
|
||||||
) { factor, direction, content ->
|
) { factor, direction, content ->
|
||||||
content(
|
content(
|
||||||
if (directions == null || directions.contains(direction)) {
|
if (slideDirections == null || slideDirections.contains(direction)) {
|
||||||
Modifier.offsetXFactor(factor)
|
Modifier.offsetXFactor(factor)
|
||||||
} else {
|
} else {
|
||||||
Modifier
|
Modifier
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}.plus(
|
}
|
||||||
fade(
|
|
||||||
animationSpec = tween(
|
val fade = directionalFade(
|
||||||
delayMillis = 50,
|
animationSpec = tween(
|
||||||
durationMillis = 300,
|
delayMillis = 50,
|
||||||
easing = easing,
|
durationMillis = 300,
|
||||||
),
|
easing = easing,
|
||||||
),
|
),
|
||||||
|
directions = fadeDirections,
|
||||||
|
)
|
||||||
|
|
||||||
|
return slide.plus(fade)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like `decompose.fade(...)` but only applies the alpha `graphicsLayer` when `direction`
|
||||||
|
* is in [directions]. `null` directions = always fade (matches stock `fade()` behavior).
|
||||||
|
* `emptySet()` directions = never fade (modifier passes through untouched).
|
||||||
|
*/
|
||||||
|
private fun directionalFade(
|
||||||
|
animationSpec: FiniteAnimationSpec<Float>,
|
||||||
|
directions: Set<Direction>?,
|
||||||
|
): StackAnimator = stackAnimator(animationSpec) { factor, direction, content ->
|
||||||
|
content(
|
||||||
|
if (directions == null || directions.contains(direction)) {
|
||||||
|
Modifier.graphicsLayer {
|
||||||
|
alpha = 1f - abs(factor)
|
||||||
|
compositingStrategy = CompositingStrategy.Offscreen
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Modifier
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
||||||
|
|
||||||
@OptIn(ExperimentalHazeMaterialsApi::class)
|
@OptIn(ExperimentalHazeMaterialsApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
internal fun ProvideHaze(content: @Composable () -> Unit) {
|
fun ProvideHaze(content: @Composable () -> Unit) {
|
||||||
val hazeState = rememberHazeState()
|
val hazeState = rememberHazeState()
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
LocalHazeState provides hazeState,
|
LocalHazeState provides hazeState,
|
||||||
|
|
@ -47,7 +47,7 @@ fun isHazeBlurEffectivelyEnabled(state: HazeState = LocalHazeState.current): Boo
|
||||||
@Composable
|
@Composable
|
||||||
fun Modifier.hazeEffectTangem(
|
fun Modifier.hazeEffectTangem(
|
||||||
state: HazeState = LocalHazeState.current,
|
state: HazeState = LocalHazeState.current,
|
||||||
style: HazeStyle = HazeStyle.Unspecified,
|
style: HazeStyle = CupertinoMaterials.ultraThin(),
|
||||||
configure: HazeEffectScope.() -> Unit = {},
|
configure: HazeEffectScope.() -> Unit = {},
|
||||||
): Modifier {
|
): Modifier {
|
||||||
val isGlobalBlurEnabled = isHazeBlurEffectivelyEnabled(state)
|
val isGlobalBlurEnabled = isHazeBlurEffectivelyEnabled(state)
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||||
* @param modifier Modifier to be applied to the button.
|
* @param modifier Modifier to be applied to the button.
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun SecondaryTangemButton(buttonUM: TangemButtonUM, modifier: Modifier = Modifier) {
|
fun SecondaryTangemButton(buttonUM: TangemButtonUM, modifier: Modifier = Modifier, withHazeEffect: Boolean = true) {
|
||||||
SecondaryTangemButton(
|
SecondaryTangemButton(
|
||||||
onClick = buttonUM.onClick,
|
onClick = buttonUM.onClick,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
|
@ -40,6 +40,7 @@ fun SecondaryTangemButton(buttonUM: TangemButtonUM, modifier: Modifier = Modifie
|
||||||
size = buttonUM.size,
|
size = buttonUM.size,
|
||||||
shape = buttonUM.shape,
|
shape = buttonUM.shape,
|
||||||
onLongClick = buttonUM.onLongClick,
|
onLongClick = buttonUM.onLongClick,
|
||||||
|
withHazeEffect = withHazeEffect,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,6 +71,7 @@ fun SecondaryTangemButton(
|
||||||
size: TangemButtonSize = TangemButtonSize.X15,
|
size: TangemButtonSize = TangemButtonSize.X15,
|
||||||
shape: TangemButtonShape = TangemButtonShape.Default,
|
shape: TangemButtonShape = TangemButtonShape.Default,
|
||||||
onLongClick: (() -> Unit)? = null,
|
onLongClick: (() -> Unit)? = null,
|
||||||
|
withHazeEffect: Boolean = true,
|
||||||
) {
|
) {
|
||||||
val backgroundModifier = if (isEnabled) {
|
val backgroundModifier = if (isEnabled) {
|
||||||
Modifier.background(TangemTheme.colors2.button.backgroundSecondary)
|
Modifier.background(TangemTheme.colors2.button.backgroundSecondary)
|
||||||
|
|
@ -86,7 +88,7 @@ fun SecondaryTangemButton(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.clip(shape.toShape(size))
|
.clip(shape.toShape(size))
|
||||||
.hazeEffectTangem()
|
.then(if (withHazeEffect) Modifier.hazeEffectTangem() else Modifier)
|
||||||
.then(backgroundModifier),
|
.then(backgroundModifier),
|
||||||
text = text,
|
text = text,
|
||||||
contentColor = contentColor,
|
contentColor = contentColor,
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,11 @@
|
||||||
|
|
||||||
package com.tangem.core.ui.res
|
package com.tangem.core.ui.res
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
|
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
|
||||||
import androidx.compose.foundation.text.selection.TextSelectionColors
|
import androidx.compose.foundation.text.selection.TextSelectionColors
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import com.tangem.core.ui.components.haze.ProvideHaze
|
import com.tangem.core.ui.components.haze.ProvideHaze
|
||||||
import com.tangem.core.ui.components.haze.hazeSourceTangem
|
|
||||||
import com.tangem.core.ui.res.generated.TangemDimens3
|
import com.tangem.core.ui.res.generated.TangemDimens3
|
||||||
import com.tangem.core.ui.res.generated.TangemTypography3
|
import com.tangem.core.ui.res.generated.TangemTypography3
|
||||||
import com.tangem.core.ui.res.generated.darkColors3
|
import com.tangem.core.ui.res.generated.darkColors3
|
||||||
|
|
@ -55,9 +52,7 @@ fun TangemThemeRedesign(content: @Composable () -> Unit) {
|
||||||
LocalTextSelectionColors provides TangemTextSelectionColors2,
|
LocalTextSelectionColors provides TangemTextSelectionColors2,
|
||||||
) {
|
) {
|
||||||
ProvideHaze {
|
ProvideHaze {
|
||||||
Box(Modifier.hazeSourceTangem(zIndex = 1f)) {
|
content()
|
||||||
content()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,7 @@ internal fun TokenMarketBlock(tokenMarketBlockUM: TokenMarketBlockUM, modifier:
|
||||||
tangemIconUM = TangemIconUM.Icon(iconRes = CoreR.drawable.ic_arrow_expand_24),
|
tangemIconUM = TangemIconUM.Icon(iconRes = CoreR.drawable.ic_arrow_expand_24),
|
||||||
shape = TangemButtonShape.Rounded,
|
shape = TangemButtonShape.Rounded,
|
||||||
size = TangemButtonSize.X10,
|
size = TangemButtonSize.X10,
|
||||||
|
withHazeEffect = false,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.layoutId(TangemRowLayoutId.TAIL)
|
.layoutId(TangemRowLayoutId.TAIL)
|
||||||
.padding(start = TangemTheme.dimens2.x10),
|
.padding(start = TangemTheme.dimens2.x10),
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ markdown = "0.7.2"
|
||||||
markdownComposeView = "0.5.4"
|
markdownComposeView = "0.5.4"
|
||||||
usedesk = "4.4.0"
|
usedesk = "4.4.0"
|
||||||
sumsub = "1.38.0"
|
sumsub = "1.38.0"
|
||||||
haze = "1.7.1"
|
haze = "1.7.2"
|
||||||
kotlinpoet = "1.18.1"
|
kotlinpoet = "1.18.1"
|
||||||
customerio = "4.6.3"
|
customerio = "4.6.3"
|
||||||
surveysparrow = "1.2.9"
|
surveysparrow = "1.2.9"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue