diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index 1c6883653b..566eb3e04d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -153,7 +153,7 @@ internal object WalletPreviewData { icon = tokenIconState, name = "Polygon", amount = "5,412 MATIC", - hasPending = true, + hasPending = false, tokenOptions = TokenOptionsState( config = PriceChangeConfig( valueInPercent = "2%", @@ -161,7 +161,6 @@ internal object WalletPreviewData { ), fiatAmount = "321 $", isBalanceHidden = false, - ), onItemClick = {}, onItemLongClick = {}, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt index a306e8bad2..0125f47284 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt @@ -1,40 +1,40 @@ package com.tangem.feature.wallet.presentation.common.component -import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.background -import androidx.compose.foundation.combinedClickable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.defaultMinSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.runtime.Composable -import androidx.compose.runtime.Stable +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.composed +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider -import androidx.constraintlayout.compose.ConstrainedLayoutReference -import androidx.constraintlayout.compose.ConstraintLayout -import androidx.constraintlayout.compose.ConstraintLayoutScope -import androidx.constraintlayout.compose.Dimension +import androidx.constraintlayout.compose.* import com.tangem.core.ui.extensions.rememberHapticFeedback import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.presentation.common.WalletPreviewData -import com.tangem.feature.wallet.presentation.common.component.token.TokenCryptoInfoBlock -import com.tangem.feature.wallet.presentation.common.component.token.TokenFiatInfoBlock +import com.tangem.feature.wallet.presentation.common.component.token.* import com.tangem.feature.wallet.presentation.common.component.token.icon.TokenIcon import com.tangem.feature.wallet.presentation.common.state.TokenItemState import org.burnoutcrew.reorderable.ReorderableLazyListState +@Suppress("LongMethod") @Composable internal fun TokenItem( state: TokenItemState, modifier: Modifier = Modifier, reorderableTokenListState: ReorderableLazyListState? = null, ) { - BaseContainer(modifier = modifier.tokenClickable(state)) { - val (iconRef, cryptoInfoRef, fiatInfoRef) = createRefs() + var rootWidth by remember { mutableStateOf(Int.MIN_VALUE) } + + @Suppress("DestructuringDeclarationWithTooManyEntries") + BaseContainer( + modifier = modifier + .tokenClickable(state) + .onSizeChanged { rootWidth = it.width }, + ) { + val (iconRef, titleRef, cryptoAmountRef, fiatAmountRef, priceChangeRef, nonFiatContentRef) = createRefs() TokenIcon( state = state, @@ -44,22 +44,98 @@ internal fun TokenItem( }, ) - TokenCryptoInfoBlock( + val density = LocalDensity.current + val titleRequiredMinWidth by remember(rootWidth) { + derivedStateOf { with(density) { rootWidth.toDp().times(other = 0.22f) } } + } + + TokenTitle( state = state, modifier = Modifier .padding(horizontal = TangemTheme.dimens.spacing8) - .constrainAs(cryptoInfoRef) { - centerVerticallyTo(parent) + .constrainAs(titleRef) { start.linkTo(iconRef.end) - end.linkTo(fiatInfoRef.start) - width = Dimension.fillToConstraints + top.linkTo(parent.top) + + width = Dimension.fillToConstraints.atLeast(dp = titleRequiredMinWidth) + + when (state) { + is TokenItemState.Content -> end.linkTo(fiatAmountRef.start) + is TokenItemState.Draggable -> end.linkTo(nonFiatContentRef.start) + is TokenItemState.Unreachable, + is TokenItemState.NoAddress, + -> { + end.linkTo(nonFiatContentRef.start) + bottom.linkTo(parent.bottom) + } + else -> Unit + } }, ) - TokenFiatInfoBlock( + TokenFiatAmount( + state = state, + modifier = Modifier.constrainAs(fiatAmountRef) { + top.linkTo(parent.top) + end.linkTo(parent.end) + + width = Dimension.fillToConstraints.atMostWrapContent + + if (state is TokenItemState.Content) { + start.linkTo(titleRef.end) + } + }, + ) + + val marginBetweenRows = TangemTheme.dimens.spacing2 + TokenCryptoAmount( + state = state, + modifier = Modifier + .padding(horizontal = TangemTheme.dimens.spacing8) + .constrainAs(cryptoAmountRef) { + start.linkTo(iconRef.end) + top.linkTo(titleRef.bottom, marginBetweenRows) + bottom.linkTo(parent.bottom) + + when (state) { + is TokenItemState.Content -> { + end.linkTo(priceChangeRef.start) + width = Dimension.fillToConstraints.atMostWrapContent + } + is TokenItemState.Draggable -> { + end.linkTo(nonFiatContentRef.start) + width = Dimension.fillToConstraints + } + else -> Unit + } + }, + ) + + val priceChangeRequiredMinWidth by remember(rootWidth) { + derivedStateOf { with(density) { rootWidth.toDp().times(other = 0.16f) } } + } + TokenPriceChange( + state = state, + modifier = Modifier.constrainAs(priceChangeRef) { + top.linkTo(fiatAmountRef.bottom, marginBetweenRows) + end.linkTo(anchor = parent.end) + bottom.linkTo(parent.bottom) + + if (state is TokenItemState.ContentState) { + start.linkTo(cryptoAmountRef.end) + width = Dimension.fillToConstraints + .atLeast(priceChangeRequiredMinWidth) + } + }, + ) + + NonFiatContentBlock( state = state, - modifier = Modifier.constrainAsOptionsItem(scope = this, ref = fiatInfoRef), reorderableTokenListState = reorderableTokenListState, + modifier = Modifier.constrainAs(nonFiatContentRef) { + centerVerticallyTo(parent) + end.linkTo(parent.end) + }, ) } } @@ -77,57 +153,26 @@ private inline fun BaseContainer( ConstraintLayout( modifier = Modifier .fillMaxWidth() - .padding( - horizontal = TangemTheme.dimens.spacing14, - vertical = TangemTheme.dimens.spacing14, - ), + .padding(all = TangemTheme.dimens.spacing14), content = content, ) } } -@Stable -private fun Modifier.constrainAsOptionsItem(scope: ConstraintLayoutScope, ref: ConstrainedLayoutReference): Modifier { - return with(scope) { - this@constrainAsOptionsItem.constrainAs(ref) { - centerVerticallyTo(parent) - end.linkTo(parent.end) - } - } -} - @OptIn(ExperimentalFoundationApi::class) private fun Modifier.tokenClickable(state: TokenItemState): Modifier = composed { when (state) { is TokenItemState.Content -> { - val onLongClick = rememberHapticFeedback( - state = state, - onAction = state.onItemLongClick, - ) - this.combinedClickable( - onClick = state.onItemClick, - onLongClick = onLongClick, - ) + val onLongClick = rememberHapticFeedback(state = state, onAction = state.onItemLongClick) + combinedClickable(onClick = state.onItemClick, onLongClick = onLongClick) } is TokenItemState.Unreachable -> { - val onLongClick = rememberHapticFeedback( - state = state, - onAction = state.onItemLongClick, - ) - this.combinedClickable( - onClick = state.onItemClick, - onLongClick = onLongClick, - ) + val onLongClick = rememberHapticFeedback(state = state, onAction = state.onItemLongClick) + combinedClickable(onClick = state.onItemClick, onLongClick = onLongClick) } is TokenItemState.NoAddress -> { - val onLongClick = rememberHapticFeedback( - state = state, - onAction = state.onItemLongClick, - ) - this.combinedClickable( - onClick = {}, - onLongClick = onLongClick, - ) + val onLongClick = rememberHapticFeedback(state = state, onAction = state.onItemLongClick) + combinedClickable(onClick = {}, onLongClick = onLongClick) } is TokenItemState.Draggable, is TokenItemState.Loading, @@ -155,6 +200,22 @@ private fun Preview_Tokens_DarkTheme(@PreviewParameter(TokenConfigProvider::clas private class TokenConfigProvider : CollectionPreviewParameterProvider( collection = listOf( + WalletPreviewData.tokenItemVisibleState.copy(amount = "5,41221467146712416241274127841274174213421 MATIC"), + WalletPreviewData.tokenItemVisibleState.copy( + tokenOptions = WalletPreviewData.tokenItemVisibleState.tokenOptions.copy( + config = WalletPreviewData.tokenItemVisibleState.tokenOptions.config.copy( + valueInPercent = "31231231231231231231223123123123212312312312.00%", + ), + ), + ), + WalletPreviewData.tokenItemVisibleState.copy( + amount = "5,41221467146712416241274127841274174213421 MATIC", + tokenOptions = WalletPreviewData.tokenItemVisibleState.tokenOptions.copy( + config = WalletPreviewData.tokenItemVisibleState.tokenOptions.config.copy( + valueInPercent = "31231231231231231231223123123123212312312312.00%", + ), + ), + ), WalletPreviewData.tokenItemVisibleState, WalletPreviewData.tokenItemUnreachableState, WalletPreviewData.tokenItemNoAddressState, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/NonFiatContentBlock.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/NonFiatContentBlock.kt new file mode 100644 index 0000000000..cb5c920555 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/NonFiatContentBlock.kt @@ -0,0 +1,78 @@ +package com.tangem.feature.wallet.presentation.common.component.token + +import androidx.annotation.StringRes +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemTypography +import com.tangem.feature.wallet.impl.R +import com.tangem.feature.wallet.presentation.common.state.TokenItemState +import org.burnoutcrew.reorderable.ReorderableLazyListState +import org.burnoutcrew.reorderable.detectReorder + +@OptIn(ExperimentalAnimationApi::class) +@Composable +internal fun NonFiatContentBlock( + state: TokenItemState, + reorderableTokenListState: ReorderableLazyListState?, + modifier: Modifier = Modifier, +) { + AnimatedContent( + targetState = state, + label = "Update non content fiat block", + modifier = modifier, + ) { animatedState -> + when (animatedState) { + is TokenItemState.Draggable -> DraggableImage(reorderableTokenListState = reorderableTokenListState) + is TokenItemState.Unreachable -> NonFiatContentText(text = R.string.common_unreachable) + is TokenItemState.NoAddress -> NonFiatContentText(text = R.string.common_no_address) + is TokenItemState.Content, + is TokenItemState.Loading, + is TokenItemState.Locked, + -> Unit + } + } +} + +@Composable +private fun DraggableImage(reorderableTokenListState: ReorderableLazyListState?) { + Box( + modifier = Modifier + .size(size = TangemTheme.dimens.size32) + .then( + other = if (reorderableTokenListState != null) { + Modifier.detectReorder(reorderableTokenListState) + } else { + Modifier + }, + ), + contentAlignment = Alignment.Center, + ) { + Icon( + painter = painterResource(id = R.drawable.ic_drag_24), + tint = TangemTheme.colors.icon.informative, + contentDescription = null, + ) + } +} + +@Composable +private fun NonFiatContentText(@StringRes text: Int) { + Text( + text = stringResource(id = text), + color = TangemTheme.colors.text.tertiary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = TangemTypography.body2, + ) +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenCryptoAmount.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenCryptoAmount.kt new file mode 100644 index 0000000000..700dfdc58c --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenCryptoAmount.kt @@ -0,0 +1,60 @@ +package com.tangem.feature.wallet.presentation.common.component.token + +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.composed +import androidx.compose.ui.text.style.TextOverflow +import com.tangem.common.Strings +import com.tangem.core.ui.components.RectangleShimmer +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemTypography +import com.tangem.feature.wallet.presentation.common.state.TokenItemState + +@OptIn(ExperimentalAnimationApi::class) +@Composable +internal fun TokenCryptoAmount(state: TokenItemState, modifier: Modifier = Modifier) { + AnimatedContent(targetState = state, label = "Update crypto amount", modifier = modifier) { animatedState -> + when (animatedState) { + is TokenItemState.Content -> { + CryptoAmountText( + amount = if (animatedState.tokenOptions.isBalanceHidden) Strings.STARS else animatedState.amount, + ) + } + is TokenItemState.Draggable -> { + CryptoAmountText(amount = animatedState.info.resolveReference()) + } + is TokenItemState.Loading -> { + RectangleShimmer(modifier = Modifier.placeholderSize(), radius = TangemTheme.dimens.radius4) + } + is TokenItemState.Locked -> { + LockedRectangle(modifier = Modifier.placeholderSize()) + } + is TokenItemState.Unreachable, + is TokenItemState.NoAddress, + -> Unit + } + } +} + +@Composable +private fun CryptoAmountText(amount: String) { + Text( + text = amount, + color = TangemTheme.colors.text.tertiary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = TangemTypography.body2, + ) +} + +private fun Modifier.placeholderSize(): Modifier = composed { + return@composed this + .padding(vertical = TangemTheme.dimens.spacing4) + .size(width = TangemTheme.dimens.size52, height = TangemTheme.dimens.size12) +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenCryptoInfoBlock.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenCryptoInfoBlock.kt deleted file mode 100644 index 4521b33872..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenCryptoInfoBlock.kt +++ /dev/null @@ -1,118 +0,0 @@ -package com.tangem.feature.wallet.presentation.common.component.token - -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.* -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.composed -import androidx.compose.ui.res.painterResource -import com.tangem.common.Strings.STARS -import com.tangem.core.ui.components.RectangleShimmer -import com.tangem.core.ui.extensions.resolveReference -import com.tangem.core.ui.res.TangemTheme -import com.tangem.core.ui.res.TangemTypography -import com.tangem.feature.wallet.impl.R -import com.tangem.feature.wallet.presentation.common.state.TokenItemState - -@Composable -internal fun TokenCryptoInfoBlock(state: TokenItemState, modifier: Modifier = Modifier) { - when (state) { - is TokenItemState.ContentState -> ContentBlock(state = state, modifier = modifier) - is TokenItemState.Loading -> LoadingBlock(modifier = modifier) - is TokenItemState.Locked -> LockedBlock(modifier = modifier) - } -} - -@Composable -private fun ContentBlock(state: TokenItemState.ContentState, modifier: Modifier = Modifier) { - Column( - modifier = modifier, - verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing2), - ) { - CurrencyNameText( - name = state.name, - hasPending = (state as? TokenItemState.Content)?.hasPending == true, - ) - - AmountText( - amount = when (state) { - is TokenItemState.Content -> if (state.tokenOptions.isBalanceHidden) STARS else state.amount - is TokenItemState.Draggable -> state.info.resolveReference() - is TokenItemState.Unreachable, - is TokenItemState.NoAddress, - -> null - }, - ) - } -} - -@Composable -private fun CurrencyNameText(name: String, hasPending: Boolean) { - Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) { - Text( - text = name, - style = TangemTypography.subtitle2, - color = TangemTheme.colors.text.primary1, - ) - - PendingTransactionImage(hasPending = hasPending, modifier = Modifier.align(Alignment.CenterVertically)) - } -} - -@Composable -private fun PendingTransactionImage(hasPending: Boolean, modifier: Modifier = Modifier) { - AnimatedVisibility(visible = hasPending, modifier = modifier) { - Image( - painter = painterResource(id = R.drawable.img_loader_15), - contentDescription = null, - ) - } -} - -@Composable -private fun AmountText(amount: String?) { - AnimatedVisibility(visible = !amount.isNullOrBlank()) { - if (amount == null) return@AnimatedVisibility - Text( - text = amount, - style = TangemTypography.body2, - color = TangemTheme.colors.text.tertiary, - ) - } -} - -@Composable -private fun LoadingBlock(modifier: Modifier = Modifier) { - NonContentContainer(modifier = modifier) { - RectangleShimmer(modifier = Modifier.nameSize(), radius = TangemTheme.dimens.radius4) - RectangleShimmer(modifier = Modifier.amountSize(), radius = TangemTheme.dimens.radius4) - } -} - -@Composable -private fun LockedBlock(modifier: Modifier = Modifier) { - NonContentContainer(modifier = modifier) { - LockedRectangle(modifier = Modifier.nameSize()) - LockedRectangle(modifier = Modifier.amountSize()) - } -} - -@Composable -private fun NonContentContainer(modifier: Modifier = Modifier, content: @Composable ColumnScope.() -> Unit) { - Column( - modifier = modifier, - verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing10), - content = content, - ) -} - -private fun Modifier.nameSize(): Modifier = composed { - return@composed size(width = TangemTheme.dimens.size72, height = TangemTheme.dimens.size12) -} - -private fun Modifier.amountSize(): Modifier = composed { - return@composed size(width = TangemTheme.dimens.size50, height = TangemTheme.dimens.size12) -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenFiatAmount.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenFiatAmount.kt new file mode 100644 index 0000000000..f4ed36c1c8 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenFiatAmount.kt @@ -0,0 +1,54 @@ +package com.tangem.feature.wallet.presentation.common.component.token + +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.composed +import androidx.compose.ui.text.style.TextOverflow +import com.tangem.common.Strings +import com.tangem.core.ui.components.RectangleShimmer +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemTypography +import com.tangem.feature.wallet.presentation.common.state.TokenItemState + +@OptIn(ExperimentalAnimationApi::class) +@Composable +internal fun TokenFiatAmount(state: TokenItemState, modifier: Modifier = Modifier) { + AnimatedContent(targetState = state, label = "Update fiat amount", modifier = modifier) { animatedState -> + when (animatedState) { + is TokenItemState.Content -> { + Text( + text = if (animatedState.tokenOptions.isBalanceHidden) { + Strings.STARS + } else { + animatedState.tokenOptions.fiatAmount + }, + color = TangemTheme.colors.text.primary1, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = TangemTypography.body2, + ) + } + is TokenItemState.Loading -> { + RectangleShimmer(modifier = Modifier.placeholderSize(), radius = TangemTheme.dimens.radius4) + } + is TokenItemState.Locked -> { + LockedRectangle(modifier = Modifier.placeholderSize()) + } + is TokenItemState.Unreachable, + is TokenItemState.Draggable, + is TokenItemState.NoAddress, + -> Unit + } + } +} + +private fun Modifier.placeholderSize(): Modifier = composed { + return@composed this + .padding(vertical = TangemTheme.dimens.spacing4) + .size(width = TangemTheme.dimens.size40, height = TangemTheme.dimens.size12) +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenFiatInfoBlock.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenFiatInfoBlock.kt deleted file mode 100644 index 74a07725c8..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenFiatInfoBlock.kt +++ /dev/null @@ -1,195 +0,0 @@ -package com.tangem.feature.wallet.presentation.common.component.token - -import androidx.compose.animation.AnimatedContent -import androidx.compose.animation.ExperimentalAnimationApi -import androidx.compose.foundation.layout.* -import androidx.compose.material3.Icon -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.composed -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import com.tangem.common.Strings.STARS -import com.tangem.core.ui.components.RectangleShimmer -import com.tangem.core.ui.components.SpacerW4 -import com.tangem.core.ui.components.marketprice.PriceChangeConfig -import com.tangem.core.ui.res.TangemTheme -import com.tangem.core.ui.res.TangemTypography -import com.tangem.feature.wallet.impl.R -import com.tangem.feature.wallet.presentation.common.state.TokenItemState -import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TokenOptionsState -import org.burnoutcrew.reorderable.ReorderableLazyListState -import org.burnoutcrew.reorderable.detectReorder - -@Composable -internal fun TokenFiatInfoBlock( - state: TokenItemState, - modifier: Modifier = Modifier, - reorderableTokenListState: ReorderableLazyListState? = null, -) { - when (state) { - is TokenItemState.Content -> ContentBlock(state = state.tokenOptions, modifier = modifier) - is TokenItemState.Draggable -> { - DraggableBlock( - modifier = modifier, - reorderableTokenListState = reorderableTokenListState, - ) - } - is TokenItemState.Unreachable -> UnreachableBlock(modifier = modifier) - is TokenItemState.NoAddress -> NoAddressBlock(modifier = modifier) - is TokenItemState.Loading -> LoadingBlock(modifier = modifier) - is TokenItemState.Locked -> LockedBlock(modifier = modifier) - } -} - -@OptIn(ExperimentalAnimationApi::class) -@Composable -private fun ContentBlock(state: TokenOptionsState, modifier: Modifier = Modifier) { - Column( - modifier = modifier.requiredWidth(IntrinsicSize.Max), - verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing2), - ) { - AnimatedContent( - targetState = state, - label = "Update the fiat percentage block", - modifier = Modifier.align(Alignment.End), - ) { - Text( - text = if (it.isBalanceHidden) { - STARS - } else { - it.fiatAmount - }, - style = TangemTypography.body2, - color = TangemTheme.colors.text.primary1, - ) - } - - PriceChangeBlock(config = state.config) - } -} - -@Composable -private fun PriceChangeBlock(config: PriceChangeConfig) { - Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) { - PriceChangeIcon(type = config.type, modifier = Modifier.align(Alignment.CenterVertically)) - SpacerW4() - PriceChangeText(config = config, modifier = Modifier.align(Alignment.CenterVertically)) - } -} - -@OptIn(ExperimentalAnimationApi::class) -@Composable -private fun PriceChangeIcon(type: PriceChangeConfig.Type, modifier: Modifier = Modifier) { - AnimatedContent( - targetState = type, - label = "Update the price change's arrow", - modifier = modifier, - ) { - Icon( - painter = painterResource( - id = when (it) { - PriceChangeConfig.Type.UP -> R.drawable.ic_arrow_up_8 - PriceChangeConfig.Type.DOWN -> R.drawable.ic_arrow_down_8 - }, - ), - tint = when (it) { - PriceChangeConfig.Type.UP -> TangemTheme.colors.icon.accent - PriceChangeConfig.Type.DOWN -> TangemTheme.colors.icon.warning - }, - contentDescription = null, - ) - } -} - -@OptIn(ExperimentalAnimationApi::class) -@Composable -private fun PriceChangeText(config: PriceChangeConfig, modifier: Modifier = Modifier) { - AnimatedContent( - targetState = config.type, - label = "Update the price change's arrow", - modifier = modifier, - ) { - Text( - text = config.valueInPercent, - style = TangemTypography.body2, - color = when (it) { - PriceChangeConfig.Type.UP -> TangemTheme.colors.text.accent - PriceChangeConfig.Type.DOWN -> TangemTheme.colors.text.warning - }, - ) - } -} - -@Composable -private fun DraggableBlock(reorderableTokenListState: ReorderableLazyListState?, modifier: Modifier = Modifier) { - Box( - modifier = modifier - .size(TangemTheme.dimens.size32) - .then( - other = if (reorderableTokenListState != null) { - Modifier.detectReorder(reorderableTokenListState) - } else { - Modifier - }, - ), - contentAlignment = Alignment.Center, - ) { - Icon( - painter = painterResource(id = R.drawable.ic_drag_24), - tint = TangemTheme.colors.icon.informative, - contentDescription = null, - ) - } -} - -@Composable -private fun UnreachableBlock(modifier: Modifier = Modifier) { - Text( - modifier = modifier, - text = stringResource(id = R.string.common_unreachable), - style = TangemTypography.body2, - color = TangemTheme.colors.text.tertiary, - ) -} - -@Composable -private fun NoAddressBlock(modifier: Modifier = Modifier) { - Text( - modifier = modifier, - text = stringResource(id = R.string.common_no_address), - style = TangemTypography.body2, - color = TangemTheme.colors.text.tertiary, - ) -} - -@Composable -private fun LoadingBlock(modifier: Modifier = Modifier) { - NonContentContainer(modifier = modifier) { - RectangleShimmer(modifier = Modifier.viewSize(), radius = TangemTheme.dimens.radius4) - RectangleShimmer(modifier = Modifier.viewSize(), radius = TangemTheme.dimens.radius4) - } -} - -@Composable -private fun LockedBlock(modifier: Modifier = Modifier) { - NonContentContainer(modifier = modifier) { - LockedRectangle(modifier = Modifier.viewSize()) - LockedRectangle(modifier = Modifier.viewSize()) - } -} - -@Composable -private fun NonContentContainer(modifier: Modifier = Modifier, content: @Composable ColumnScope.() -> Unit) { - Column( - modifier = modifier, - verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing10), - content = content, - ) -} - -private fun Modifier.viewSize(): Modifier = composed { - return@composed size(width = TangemTheme.dimens.size40, height = TangemTheme.dimens.size12) -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenPriceChange.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenPriceChange.kt new file mode 100644 index 0000000000..45d8194128 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenPriceChange.kt @@ -0,0 +1,102 @@ +package com.tangem.feature.wallet.presentation.common.component.token + +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.composed +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextOverflow +import com.tangem.core.ui.components.RectangleShimmer +import com.tangem.core.ui.components.SpacerW4 +import com.tangem.core.ui.components.marketprice.PriceChangeConfig +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemTypography +import com.tangem.feature.wallet.impl.R +import com.tangem.feature.wallet.presentation.common.state.TokenItemState + +@OptIn(ExperimentalAnimationApi::class) +@Composable +internal fun TokenPriceChange(state: TokenItemState, modifier: Modifier = Modifier) { + AnimatedContent(targetState = state, label = "Update the price change", modifier = modifier) { animatedState -> + when (animatedState) { + is TokenItemState.Content -> { + PriceChangeBlock(config = animatedState.tokenOptions.config) + } + is TokenItemState.Loading -> { + RectangleShimmer(modifier = Modifier.placeholderSize(), radius = TangemTheme.dimens.radius4) + } + is TokenItemState.Locked -> { + LockedRectangle(modifier = Modifier.placeholderSize()) + } + is TokenItemState.Unreachable, + is TokenItemState.Draggable, + is TokenItemState.NoAddress, + -> Unit + } + } +} + +@Composable +private fun PriceChangeBlock(config: PriceChangeConfig) { + Row(horizontalArrangement = Arrangement.End) { + PriceChangeIcon( + type = config.type, + modifier = Modifier.align(Alignment.CenterVertically), + ) + SpacerW4() + PriceChangeText(config = config, modifier = Modifier.align(Alignment.CenterVertically)) + } +} + +@OptIn(ExperimentalAnimationApi::class) +@Composable +private fun PriceChangeIcon(type: PriceChangeConfig.Type, modifier: Modifier = Modifier) { + AnimatedContent( + targetState = type, + label = "Update the price change's arrow", + modifier = modifier, + ) { animatedType -> + Icon( + painter = painterResource( + id = when (animatedType) { + PriceChangeConfig.Type.UP -> R.drawable.ic_arrow_up_8 + PriceChangeConfig.Type.DOWN -> R.drawable.ic_arrow_down_8 + }, + ), + tint = when (animatedType) { + PriceChangeConfig.Type.UP -> TangemTheme.colors.icon.accent + PriceChangeConfig.Type.DOWN -> TangemTheme.colors.icon.warning + }, + contentDescription = null, + ) + } +} + +@Composable +private fun PriceChangeText(config: PriceChangeConfig, modifier: Modifier = Modifier) { + Text( + text = config.valueInPercent, + modifier = modifier, + color = when (config.type) { + PriceChangeConfig.Type.UP -> TangemTheme.colors.text.accent + PriceChangeConfig.Type.DOWN -> TangemTheme.colors.text.warning + }, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + style = TangemTypography.body2, + ) +} + +private fun Modifier.placeholderSize(): Modifier = composed { + return@composed this + .padding(vertical = TangemTheme.dimens.spacing4) + .size(width = TangemTheme.dimens.size40, height = TangemTheme.dimens.size12) +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenTitle.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenTitle.kt new file mode 100644 index 0000000000..37106e707c --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenTitle.kt @@ -0,0 +1,80 @@ +package com.tangem.feature.wallet.presentation.common.component.token + +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.composed +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextOverflow +import com.tangem.core.ui.components.RectangleShimmer +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemTypography +import com.tangem.feature.wallet.impl.R +import com.tangem.feature.wallet.presentation.common.state.TokenItemState + +@OptIn(ExperimentalAnimationApi::class) +@Composable +internal fun TokenTitle(state: TokenItemState, modifier: Modifier = Modifier) { + AnimatedContent(targetState = state, label = "Update title", modifier = modifier) { animatedState -> + when (animatedState) { + is TokenItemState.ContentState -> { + ContentTitle( + name = animatedState.name, + hasPending = (animatedState as? TokenItemState.Content)?.hasPending == true, + ) + } + is TokenItemState.Loading -> { + RectangleShimmer(modifier = Modifier.placeholderSize(), radius = TangemTheme.dimens.radius4) + } + is TokenItemState.Locked -> { + LockedRectangle(modifier = Modifier.placeholderSize()) + } + } + } +} + +@Composable +private fun ContentTitle(name: String, hasPending: Boolean) { + Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing6)) { + CurrencyNameText(name = name, modifier = Modifier.weight(weight = 1f, fill = false)) + + PendingTransactionImage( + hasPending = hasPending, + modifier = Modifier.align(alignment = Alignment.CenterVertically), + ) + } +} + +@Composable +private fun CurrencyNameText(name: String, modifier: Modifier = Modifier) { + Text( + text = name, + modifier = modifier, + color = TangemTheme.colors.text.primary1, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + style = TangemTypography.subtitle2, + ) +} + +@Composable +private fun PendingTransactionImage(hasPending: Boolean, modifier: Modifier = Modifier) { + AnimatedVisibility(visible = hasPending, modifier = modifier) { + Image( + painter = painterResource(id = R.drawable.img_loader_15), + contentDescription = null, + ) + } +} + +private fun Modifier.placeholderSize(): Modifier = composed { + return@composed this + .padding(vertical = TangemTheme.dimens.spacing4) + .size(width = TangemTheme.dimens.size70, height = TangemTheme.dimens.size12) +} \ No newline at end of file