Updated on 2026-08-14

This commit is contained in:
Tangem 2023-09-29 18:44:50 +08:00
parent 94a3d2bb2d
commit a285866cab
8 changed files with 98 additions and 111 deletions

View file

@ -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),
),
)

View file

@ -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,
),
)

View file

@ -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,

View file

@ -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"
}
}

View file

@ -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(),

View file

@ -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,
),
)
},
)

View file

@ -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)
}
}
}

View file

@ -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)
}