From e51c82e6156f0f1a3fc9549108ade4b495af0adc Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 20 Aug 2024 15:13:03 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../common/ui/userwallet/UserWalletItem.kt | 19 +- .../components/buttons/common/TangemButton.kt | 57 +++++- .../impl/ui/AddToPortfolioBottomSheet.kt | 184 +++++++++++++++--- .../PreviewAddToPortfolioBSContentProvider.kt | 60 ++++-- .../ui/state/AddToPortfolioBSContentUM.kt | 2 + 5 files changed, 262 insertions(+), 60 deletions(-) diff --git a/common/ui/src/main/java/com/tangem/common/ui/userwallet/UserWalletItem.kt b/common/ui/src/main/java/com/tangem/common/ui/userwallet/UserWalletItem.kt index baf96fd3cf..91f837f146 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/userwallet/UserWalletItem.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/userwallet/UserWalletItem.kt @@ -1,8 +1,11 @@ package com.tangem.common.ui.userwallet +import android.content.res.Configuration import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.Image +import androidx.compose.foundation.background import androidx.compose.foundation.layout.* +import androidx.compose.material3.CardColors import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -26,6 +29,7 @@ import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.common.ui.R import com.tangem.core.ui.coil.RotationTransformation +import com.tangem.core.ui.components.block.TangemBlockCardColors import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.res.TangemThemePreview @@ -33,9 +37,14 @@ import com.tangem.domain.wallets.models.UserWalletId import kotlinx.collections.immutable.persistentListOf @Composable -fun UserWalletItem(state: UserWalletItemUM, modifier: Modifier = Modifier) { +fun UserWalletItem( + state: UserWalletItemUM, + modifier: Modifier = Modifier, + blockColors: CardColors = TangemBlockCardColors, +) { BlockCard( modifier = modifier, + colors = blockColors, onClick = state.onClick, enabled = state.isEnabled, ) { @@ -142,6 +151,7 @@ private fun CardImage(imageUrl: String, modifier: Modifier = Modifier) { } @Preview +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable private fun Preview() { TangemThemePreview { @@ -174,7 +184,12 @@ private fun Preview() { ), ) - Column { + Column( + Modifier + .background(TangemTheme.colors.background.tertiary) + .padding(TangemTheme.dimens.spacing12), + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), + ) { list.fastForEach { userWalletItemUM -> UserWalletItem( modifier = Modifier.fillMaxWidth(), diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButton.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButton.kt index 11fcaeb099..3033f2221a 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButton.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButton.kt @@ -1,5 +1,10 @@ package com.tangem.core.ui.components.buttons.common +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.togetherWith import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.runtime.Composable @@ -36,6 +41,7 @@ fun TangemButton( textStyle: TextStyle = TangemTheme.typography.button, shape: Shape = size.toShape(), iconPadding: Dp = size.toIconPadding(), + animateContentChange: Boolean = false, ) { val multipleClickPreventer = remember { MultipleClickPreventer.get() } @@ -56,6 +62,7 @@ fun TangemButton( buttonIcon = icon, iconPadding = iconPadding, showProgress = showProgress, + animateContentChange = animateContentChange, progressIndicator = { CircularProgressIndicator( modifier = Modifier.buttonContentSize(maxContentSize), @@ -108,24 +115,54 @@ private inline fun RowScope.ButtonContentContainer( buttonIcon: TangemButtonIconPosition, iconPadding: Dp, showProgress: Boolean, + animateContentChange: Boolean, progressIndicator: @Composable RowScope.() -> Unit, - text: @Composable RowScope.() -> Unit, - icon: @Composable RowScope.(Int) -> Unit, + crossinline text: @Composable RowScope.() -> Unit, + crossinline icon: @Composable RowScope.(Int) -> Unit, additionalText: @Composable () -> Unit, ) { if (showProgress) { progressIndicator() } else { Column(horizontalAlignment = Alignment.CenterHorizontally) { - Row(horizontalArrangement = Arrangement.Center) { - if (buttonIcon is TangemButtonIconPosition.Start) { - icon(buttonIcon.iconResId) - Spacer(modifier = Modifier.requiredWidth(iconPadding)) + if (animateContentChange) { + AnimatedContent( + targetState = buttonIcon, + transitionSpec = { + fadeIn(tween(durationMillis = 220)) togetherWith + fadeOut(tween(durationMillis = 220)) + }, + label = "button text with icon", + ) { iconState -> + Row(horizontalArrangement = Arrangement.Center) { + when (iconState) { + is TangemButtonIconPosition.Start -> { + icon(iconState.iconResId) + Spacer(modifier = Modifier.requiredWidth(iconPadding)) + text() + } + is TangemButtonIconPosition.End -> { + text() + Spacer(modifier = Modifier.requiredWidth(iconPadding)) + icon(iconState.iconResId) + } + is TangemButtonIconPosition.None -> { + text() + } + } + } } - text() - if (buttonIcon is TangemButtonIconPosition.End) { - Spacer(modifier = Modifier.requiredWidth(iconPadding)) - icon(buttonIcon.iconResId) + } else { + Row(horizontalArrangement = Arrangement.Center) { + if (buttonIcon is TangemButtonIconPosition.Start) { + icon(buttonIcon.iconResId) + Spacer(modifier = Modifier.requiredWidth(iconPadding)) + } + text() + if (buttonIcon is TangemButtonIconPosition.End) { + Spacer(modifier = Modifier.requiredWidth(iconPadding)) + icon(buttonIcon.iconResId) + } } } additionalText() diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/AddToPortfolioBottomSheet.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/AddToPortfolioBottomSheet.kt index 8a5a2ae45f..e9b760f529 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/AddToPortfolioBottomSheet.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/AddToPortfolioBottomSheet.kt @@ -2,29 +2,36 @@ package com.tangem.features.markets.portfolio.impl.ui import android.content.res.Configuration import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button import androidx.compose.material3.Icon import androidx.compose.material3.Text -import androidx.compose.runtime.Composable +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.util.fastForEachIndexed import com.tangem.common.ui.userwallet.UserWalletItem -import com.tangem.core.ui.components.PrimaryButtonIconEnd -import com.tangem.core.ui.components.SpacerW12 -import com.tangem.core.ui.components.SpacerW6 -import com.tangem.core.ui.components.TangemSwitch +import com.tangem.core.ui.components.* +import com.tangem.core.ui.components.block.TangemBlockCardColors import com.tangem.core.ui.components.block.information.InformationBlock import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.buttons.common.TangemButton +import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition +import com.tangem.core.ui.components.buttons.common.TangemButtonSize +import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults import com.tangem.core.ui.components.currency.icon.CoinIcon import com.tangem.core.ui.components.rows.ArrowRow import com.tangem.core.ui.components.rows.BlockchainRow @@ -36,18 +43,18 @@ import com.tangem.features.markets.impl.R import com.tangem.features.markets.portfolio.impl.ui.preview.PreviewAddToPortfolioBSContentProvider import com.tangem.features.markets.portfolio.impl.ui.state.AddToPortfolioBSContentUM import com.tangem.features.markets.portfolio.impl.ui.state.SelectNetworkUM +import kotlinx.coroutines.delay @Composable internal fun AddToPortfolioBottomSheet(config: TangemBottomSheetConfig) { TangemBottomSheet( config = config, containerColor = TangemTheme.colors.background.tertiary, + addBottomInsets = false, titleText = resourceReference(R.string.markets_add_to_portfolio_button), ) { Content( - modifier = Modifier - .fillMaxWidth() - .padding(bottom = TangemTheme.dimens.spacing16), + modifier = Modifier.fillMaxWidth(), state = config.content as AddToPortfolioBSContentUM, ) } @@ -55,33 +62,101 @@ internal fun AddToPortfolioBottomSheet(config: TangemBottomSheetConfig) { @Composable private fun Content(state: AddToPortfolioBSContentUM, modifier: Modifier = Modifier) { - Column( - modifier = modifier.padding(horizontal = TangemTheme.dimens.spacing16), - verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), - ) { - UserWalletItem(state.selectedWallet) + var continueButtonAreaHeight by remember { mutableIntStateOf(0) } + val density = LocalDensity.current + val scrollState = rememberScrollState() - NetworkSelection( - modifier = Modifier.fillMaxWidth(), - state = state.selectNetworkUM, - ) - - AnimatedVisibility( - visible = state.isScanCardNotificationVisible, - modifier = Modifier.fillMaxWidth(), + Box(modifier = modifier) { + Column( + modifier = Modifier + .verticalScroll(state = scrollState) + .padding(horizontal = TangemTheme.dimens.spacing16), ) { - ScanWalletWarning(modifier = Modifier.fillMaxWidth()) + UserWalletItem( + state = state.selectedWallet, + blockColors = TangemBlockCardColors.copy( + containerColor = TangemTheme.colors.background.action, + disabledContainerColor = TangemTheme.colors.background.action, + ), + ) + + SpacerH12() + + NetworkSelection( + modifier = Modifier.fillMaxWidth(), + state = state.selectNetworkUM, + ) + + SpacerH12() + + AnimatedVisibility( + visible = state.isScanCardNotificationVisible, + modifier = Modifier.fillMaxWidth(), + ) { + Column { + ScanWalletWarning(modifier = Modifier.fillMaxWidth()) + SpacerH12() + } + + // Scroll to the bottom when the notification appears and the scroll is at the bottom + LaunchedEffect(Unit) { + if (scrollState.canScrollForward.not()) { + delay(timeMillis = 500) + scrollState.animateScrollTo(scrollState.maxValue) + } + } + } + + SpacerH(with(density) { continueButtonAreaHeight.toDp() }) } - PrimaryButtonIconEnd( - modifier = Modifier.fillMaxWidth(), - text = stringResource(R.string.common_continue), - iconResId = R.drawable.ic_tangem_24, - onClick = {}, + AnimatedVisibility( + visible = scrollState.canScrollForward, + enter = fadeIn(), + exit = fadeOut(), + modifier = Modifier.align(Alignment.BottomCenter), + ) { + BottomFade(Modifier.align(Alignment.BottomCenter)) + } + + ContinueButton( + modifier = Modifier + .align(Alignment.BottomCenter) + .onGloballyPositioned { + continueButtonAreaHeight = it.size.height + }, + enabled = state.continueButtonEnabled, + onClick = state.onContinueButtonClick, ) } } +@Composable +private fun ContinueButton(enabled: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) { + TangemButton( + enabled = enabled, + modifier = modifier + .padding( + start = TangemTheme.dimens.spacing16, + end = TangemTheme.dimens.spacing16, + bottom = TangemTheme.dimens.spacing16, + ) + .navigationBarsPadding() + .fillMaxWidth(), + text = stringResource(R.string.common_continue), + icon = if (enabled) { + TangemButtonIconPosition.End(R.drawable.ic_tangem_24) + } else { + TangemButtonIconPosition.None + }, + showProgress = false, + size = TangemButtonSize.Default, + colors = TangemButtonsDefaults.primaryButtonColors, + onClick = onClick, + animateContentChange = true, + ) +} + @Suppress("LongMethod") @Composable private fun NetworkSelection(state: SelectNetworkUM, modifier: Modifier = Modifier) { @@ -95,10 +170,7 @@ private fun NetworkSelection(state: SelectNetworkUM, modifier: Modifier = Modifi ) }, ) { - Column( - modifier = Modifier - .verticalScroll(rememberScrollState()), - ) { + Column { Row( modifier = Modifier .fillMaxWidth() @@ -180,6 +252,7 @@ private fun ScanWalletWarning(modifier: Modifier = Modifier) { Icon( modifier = Modifier.requiredSize(TangemTheme.dimens.size20), imageVector = ImageVector.vectorResource(R.drawable.ic_tangem_24), + tint = TangemTheme.colors.icon.primary1, contentDescription = null, ) Text( @@ -237,4 +310,53 @@ private fun PreviewContentRtl( state = content, ) } +} + +// For on device testing +@Composable +@Preview +private fun PreviewContentTestOnDevice( + @PreviewParameter(PreviewAddToPortfolioBSContentProvider::class) content: AddToPortfolioBSContentUM, +) { + TangemThemePreview( + alwaysShowBottomSheets = false, + ) { + var isShow by remember { mutableStateOf(false) } + + var contentState by remember { + mutableStateOf(content) + } + + LaunchedEffect(Unit) { + contentState = content.copy( + onContinueButtonClick = { + contentState = contentState.copy( + isScanCardNotificationVisible = !contentState.isScanCardNotificationVisible, + ) + }, + continueButtonEnabled = true, + selectedWallet = content.selectedWallet.copy( + onClick = { + contentState = contentState.copy( + continueButtonEnabled = !contentState.continueButtonEnabled, + ) + }, + ), + ) + } + + AddToPortfolioBottomSheet( + config = TangemBottomSheetConfig( + isShow = isShow, + content = contentState, + onDismissRequest = { isShow = false }, + ), + ) + + Button( + onClick = { isShow = !isShow }, + ) { + Text(text = "Toggle") + } + } } \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/preview/PreviewAddToPortfolioBSContentProvider.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/preview/PreviewAddToPortfolioBSContentProvider.kt index 2b33545fd3..d184d3aac6 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/preview/PreviewAddToPortfolioBSContentProvider.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/preview/PreviewAddToPortfolioBSContentProvider.kt @@ -12,6 +12,14 @@ import kotlinx.collections.immutable.persistentListOf internal class PreviewAddToPortfolioBSContentProvider : PreviewParameterProvider { + private val blockchainRow = BlockchainRowUM( + name = "Etherium 3", + type = "TEST", + iconResId = R.drawable.ic_eth_16, + isMainNetwork = false, + isSelected = false, + ) + override val values: Sequence get() = sequenceOf( AddToPortfolioBSContentUM( @@ -29,32 +37,50 @@ internal class PreviewAddToPortfolioBSContentProvider : PreviewParameterProvider tokenName = "Etherium", tokenCurrencySymbol = "ETH", networks = persistentListOf( - BlockchainRowUM( - name = "Etherium", + blockchainRow.copy( type = "MAIN", - iconResId = R.drawable.ic_eth_16, isMainNetwork = true, isSelected = true, ), - BlockchainRowUM( - name = "Etherium 2", - type = "TEST", - iconResId = R.drawable.ic_eth_16, - isMainNetwork = false, - isSelected = false, - ), - BlockchainRowUM( - name = "Etherium 3", - type = "TEST", - iconResId = R.drawable.ic_eth_16, - isMainNetwork = false, - isSelected = false, - ), + blockchainRow, + blockchainRow, ), onNetworkSwitchClick = { _, _ -> }, iconUrl = null, ), isScanCardNotificationVisible = true, + continueButtonEnabled = true, + onContinueButtonClick = {}, + ), + AddToPortfolioBSContentUM( + selectedWallet = UserWalletItemUM( + id = UserWalletId("1"), + name = stringReference("Wallet 1"), + information = stringReference("3 cards, 10,123$"), + imageUrl = "", + isEnabled = true, + endIcon = UserWalletItemUM.EndIcon.Arrow, + onClick = {}, + ), + selectNetworkUM = SelectNetworkUM( + tokenId = "etherium", + tokenName = "Etherium", + tokenCurrencySymbol = "ETH", + networks = persistentListOf( + blockchainRow.copy( + type = "MAIN", + isMainNetwork = true, + isSelected = true, + ), + *Array(25) { blockchainRow }, + ), + + onNetworkSwitchClick = { _, _ -> }, + iconUrl = null, + ), + isScanCardNotificationVisible = true, + continueButtonEnabled = false, + onContinueButtonClick = {}, ), ) } \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/AddToPortfolioBSContentUM.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/AddToPortfolioBSContentUM.kt index ad733c39cf..6b708502ce 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/AddToPortfolioBSContentUM.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/AddToPortfolioBSContentUM.kt @@ -7,4 +7,6 @@ internal data class AddToPortfolioBSContentUM( val selectedWallet: UserWalletItemUM, val selectNetworkUM: SelectNetworkUM, val isScanCardNotificationVisible: Boolean, + val continueButtonEnabled: Boolean, + val onContinueButtonClick: () -> Unit, ) : TangemBottomSheetConfigContent \ No newline at end of file