Updated on 2026-08-14
This commit is contained in:
parent
94a3d2bb2d
commit
a285866cab
8 changed files with 98 additions and 111 deletions
|
|
@ -11,7 +11,7 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
internal fun LockedRectangle(modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier.background(
|
||||
color = TangemTheme.colors.background.secondary,
|
||||
color = TangemTheme.colors.field.primary,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius4),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ private fun LockedIcon(modifier: Modifier = Modifier) {
|
|||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
.background(
|
||||
color = TangemTheme.colors.background.secondary,
|
||||
color = TangemTheme.colors.field.primary,
|
||||
shape = CircleShape,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.components
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.res.TangemColorPalette
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
|
||||
/**
|
||||
|
|
@ -18,7 +16,6 @@ sealed class WalletBottomSheetConfig(
|
|||
open val title: TextReference,
|
||||
open val subtitle: TextReference,
|
||||
@DrawableRes open val iconResId: Int,
|
||||
open val tint: Color? = null,
|
||||
val primaryButtonConfig: ButtonConfig,
|
||||
val secondaryButtonConfig: ButtonConfig,
|
||||
) : TangemBottomSheetConfigContent {
|
||||
|
|
@ -38,7 +35,6 @@ sealed class WalletBottomSheetConfig(
|
|||
),
|
||||
),
|
||||
iconResId = R.drawable.ic_locked_24,
|
||||
tint = TangemColorPalette.Black,
|
||||
primaryButtonConfig = ButtonConfig(
|
||||
text = resourceReference(id = R.string.user_wallet_list_unlock_all),
|
||||
onClick = onUnlockClick,
|
||||
|
|
|
|||
|
|
@ -33,10 +33,7 @@ internal sealed class WalletTokensListState {
|
|||
* @property items content items
|
||||
*/
|
||||
data class Loading(
|
||||
override val items: ImmutableList<TokensListItemState.Token> = persistentListOf(
|
||||
TokensListItemState.Token(state = TokenItemState.Loading(id = FIRST_LOADING_TOKEN_ID)),
|
||||
TokensListItemState.Token(state = TokenItemState.Loading(id = SECOND_LOADING_TOKEN_ID)),
|
||||
),
|
||||
override val items: ImmutableList<TokensListItemState.Token> = persistentListOf(),
|
||||
) : ContentState(items = items, organizeTokensButton = OrganizeTokensButtonState.Hidden)
|
||||
|
||||
/**
|
||||
|
|
@ -107,8 +104,6 @@ internal sealed class WalletTokensListState {
|
|||
}
|
||||
|
||||
private companion object {
|
||||
const val FIRST_LOADING_TOKEN_ID = "Loading#1"
|
||||
const val SECOND_LOADING_TOKEN_ID = "Loading#2"
|
||||
const val LOCKED_TOKEN_ID = "Locked#1"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components.common
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -47,21 +46,14 @@ private fun BottomSheetContent(config: WalletBottomSheetConfig) {
|
|||
verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing40),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
val iconTint = config.tint
|
||||
if (iconTint != null) {
|
||||
Icon(
|
||||
painter = painterResource(id = config.iconResId),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(size = TangemTheme.dimens.size48),
|
||||
tint = iconTint,
|
||||
)
|
||||
} else {
|
||||
Image(
|
||||
painter = painterResource(id = config.iconResId),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(size = TangemTheme.dimens.size48),
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
painter = painterResource(id = config.iconResId),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(size = TangemTheme.dimens.size48),
|
||||
tint = when (config) {
|
||||
is WalletBottomSheetConfig.UnlockWallets -> TangemTheme.colors.icon.primary1
|
||||
},
|
||||
)
|
||||
|
||||
Text(
|
||||
text = config.title.resolveReference(),
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ internal fun LazyListScope.tokensListItems(state: WalletTokensListState, modifie
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
private fun LazyListScope.contentItems(
|
||||
items: ImmutableList<WalletTokensListState.TokensListItemState>,
|
||||
modifier: Modifier = Modifier,
|
||||
|
|
@ -49,12 +48,10 @@ private fun LazyListScope.contentItems(
|
|||
itemContent = { index, item ->
|
||||
MultiCurrencyContentItem(
|
||||
state = item,
|
||||
modifier = modifier
|
||||
.animateItemPlacement()
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = index,
|
||||
lastIndex = items.lastIndex,
|
||||
),
|
||||
modifier = modifier.roundedShapeItemDecoration(
|
||||
currentIndex = index,
|
||||
lastIndex = items.lastIndex,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.utils
|
||||
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.layout.absoluteOffset
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.coerceIn
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlin.math.sin
|
||||
|
||||
private val minOffset = 0.dp
|
||||
private val maxOffset = 40.dp
|
||||
|
||||
private const val MIN_ALPHA = 0f
|
||||
private const val MAX_ALPHA = 1f
|
||||
|
||||
private const val PI = Math.PI.toFloat()
|
||||
private const val HALF_PI = PI / 2
|
||||
private const val DOUBLE_PI = 2 * PI
|
||||
|
||||
private const val Y_AXIS_OFFSET = 0.5f
|
||||
private const val AMPLITUDE_MULTIPLIER = 0.5f
|
||||
|
||||
/**
|
||||
* Modifier extension for setting [absoluteOffset] and [alpha] animations of wallet change
|
||||
*
|
||||
* @param lazyListState lazy list state
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal fun Modifier.changeWalletAnimator(lazyListState: LazyListState) = composed {
|
||||
val offset by remember(lazyListState) { derivedStateOf { lazyListState.firstVisibleItemScrollOffset } }
|
||||
|
||||
val size by remember(lazyListState) {
|
||||
derivedStateOf { lazyListState.layoutInfo.visibleItemsInfo.firstOrNull()?.size ?: 1 }
|
||||
}
|
||||
|
||||
val contentOffsetY by rememberOffsetY(offset = offset, size = size)
|
||||
val contentAlpha by rememberAlpha(offset = offset, size = size)
|
||||
|
||||
val animatedOffsetY by animateDpAsState(targetValue = contentOffsetY, label = "offsetY")
|
||||
val animatedContentAlpha by animateFloatAsState(targetValue = contentAlpha, label = "alpha")
|
||||
|
||||
this
|
||||
.absoluteOffset(y = animatedOffsetY)
|
||||
.alpha(alpha = animatedContentAlpha)
|
||||
}
|
||||
|
||||
/**
|
||||
* y = ANIMATION_MAX_OFFSET_IN_DP.dp * sin(π / size * offset)
|
||||
*
|
||||
* @see <a href="https://www.wolframalpha.com/input?i=plot%2820+*+sin%28pi+%2F+100+*+x%29%2C0..100%29">Graphic</a>
|
||||
*/
|
||||
@Composable
|
||||
private fun rememberOffsetY(offset: Int, size: Int): State<Dp> {
|
||||
return remember(offset) {
|
||||
derivedStateOf {
|
||||
val y = maxOffset * sin(x = PI / size * offset)
|
||||
y.coerceIn(minimumValue = minOffset, maximumValue = maxOffset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* y = 0.5 * sin(2 * pi / size * offset + pi / 2) + 0.5
|
||||
*
|
||||
* @see <a href="https://www.wolframalpha.com/input?i=plot%280.5+*+sin%282pi+%2F+100+*+x+%2B+pi%2F2%29+%2B+0.5%2C0..100%29"
|
||||
* >Graphic</a>
|
||||
*/
|
||||
@Composable
|
||||
private fun rememberAlpha(offset: Int, size: Int): State<Float> {
|
||||
return remember(offset) {
|
||||
derivedStateOf {
|
||||
val y = AMPLITUDE_MULTIPLIER * sin(x = DOUBLE_PI / size * offset + HALF_PI) + Y_AXIS_OFFSET
|
||||
y.coerceIn(MIN_ALPHA, MAX_ALPHA)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.utils
|
||||
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.layout.absoluteOffset
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
private const val ANIMATION_MAX_OFFSET_IN_DP = 80
|
||||
private const val ANIMATION_MAX_ALPHA = 1f
|
||||
|
||||
/**
|
||||
* Modifier extension for setting [absoluteOffset] and [alpha] animations of wallet change
|
||||
*
|
||||
* @param lazyListState lazy list state
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal fun Modifier.changeWalletAnimator(lazyListState: LazyListState) = composed {
|
||||
val walletItemOffset by remember(lazyListState) { derivedStateOf { lazyListState.firstVisibleItemScrollOffset } }
|
||||
|
||||
val walletItemSize by remember(lazyListState) {
|
||||
derivedStateOf { lazyListState.layoutInfo.visibleItemsInfo.firstOrNull()?.size ?: 1 }
|
||||
}
|
||||
|
||||
val walletHalfItemSize by remember {
|
||||
derivedStateOf { walletItemSize / 2 }
|
||||
}
|
||||
|
||||
val contentOffsetY by remember {
|
||||
derivedStateOf {
|
||||
/*
|
||||
* Until [walletItemOffset] is less than [walletHalfItemSize],
|
||||
* then content offset animation has positive value (downward movement).
|
||||
* Otherwise, it has negative value (upward movement).
|
||||
*/
|
||||
val position = if (walletItemOffset < walletHalfItemSize) {
|
||||
walletItemOffset
|
||||
} else {
|
||||
walletItemSize - walletItemOffset
|
||||
}
|
||||
|
||||
ANIMATION_MAX_OFFSET_IN_DP.dp / walletItemSize * position
|
||||
}
|
||||
}
|
||||
|
||||
val contentAlpha by remember {
|
||||
derivedStateOf {
|
||||
/*
|
||||
* Until [walletItemOffset] is less than [walletHalfItemSize],
|
||||
* then content alpha animation has negative value (fade out).
|
||||
* Otherwise, it has positive value (fade in).
|
||||
*/
|
||||
val position = if (walletItemOffset < walletHalfItemSize) {
|
||||
walletHalfItemSize - walletItemOffset
|
||||
} else {
|
||||
walletItemOffset - walletHalfItemSize
|
||||
}
|
||||
|
||||
ANIMATION_MAX_ALPHA / walletHalfItemSize * position
|
||||
}
|
||||
}
|
||||
|
||||
val animatedOffsetY by animateDpAsState(targetValue = contentOffsetY)
|
||||
val animatedContentAlpha by animateFloatAsState(targetValue = contentAlpha)
|
||||
|
||||
this
|
||||
.absoluteOffset(y = animatedOffsetY)
|
||||
.alpha(alpha = animatedContentAlpha)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue