From aecd523c70a0b1f030554e710686e809cbe005bd Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 1 Jul 2026 16:52:19 +0200 Subject: [PATCH] Updated on 2026-08-14 --- .../ui/ds2/button/TangemButtonInternal.kt | 32 +- .../src/main/res/drawable/ic_card_pin_24.xml | 15 - core/ui/src/main/res/drawable/ic_close_20.xml | 15 - .../src/main/res/drawable/ic_edit_card_20.xml | 15 - .../ui/src/main/res/drawable/ic_freeze_24.xml | 15 - .../src/main/res/drawable/ic_limit_new_20.xml | 15 - core/ui/src/main/res/drawable/ic_mail_20.xml | 15 - .../main/res/drawable/ic_rating_star_24.xml | 15 - .../res/drawable/ic_visa_card_details_24.xml | 15 - .../main/res/drawable/ic_visa_in_banner.xml | 15 - .../ui/src/main/res/drawable/ic_visa_logo.xml | 15 - .../src/main/res/drawable/ic_warning_20.xml | 15 - .../component/DefaultTokenReceiveComponent.kt | 24 +- .../component/TokenReceiveAssetsComponent.kt | 8 +- .../component/TokenReceiveQrCodeComponent.kt | 8 +- .../component/TokenReceiveWarningComponent.kt | 8 +- .../tokenreceive/entity/ReceiveAddress.kt | 1 + .../ui/TokenReceiveAssetsContent.kt | 916 +++++++----------- .../ui/TokenReceiveAssetsContentLegacy.kt | 672 +++++++++++++ .../tokenreceive/ui/TokenReceiveContent.kt | 118 ++- .../ui/TokenReceiveContentLegacy.kt | 94 ++ .../ui/TokenReceiveQrCodeContent.kt | 152 ++- .../ui/TokenReceiveQrCodeContentLegacy.kt | 231 +++++ .../ui/TokenReceiveWarningContent.kt | 87 +- .../ui/TokenReceiveWarningContentLegacy.kt | 139 +++ 25 files changed, 1720 insertions(+), 935 deletions(-) create mode 100644 features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveAssetsContentLegacy.kt create mode 100644 features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveContentLegacy.kt create mode 100644 features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveQrCodeContentLegacy.kt create mode 100644 features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveWarningContentLegacy.kt diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds2/button/TangemButtonInternal.kt b/core/ui/src/main/java/com/tangem/core/ui/ds2/button/TangemButtonInternal.kt index 6ab5a5c86f..8e67699426 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds2/button/TangemButtonInternal.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds2/button/TangemButtonInternal.kt @@ -1,27 +1,13 @@ package com.tangem.core.ui.ds2.button -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.EnterTransition -import androidx.compose.animation.ExitTransition +import androidx.compose.animation.* import androidx.compose.animation.core.Spring import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.spring -import androidx.compose.animation.expandHorizontally -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.animation.shrinkHorizontally -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.text.TextAutoSize import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.ReadOnlyComposable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha @@ -38,11 +24,7 @@ import androidx.compose.ui.unit.dp import com.tangem.core.ui.ds.image.TangemIcon import com.tangem.core.ui.ds.image.TangemIconUM import com.tangem.core.ui.ds2.loader.TangemLoader -import com.tangem.core.ui.extensions.ColorReference2 -import com.tangem.core.ui.extensions.TextReference -import com.tangem.core.ui.extensions.conditionalCompose -import com.tangem.core.ui.extensions.rememberLastNonNull -import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.extensions.* import com.tangem.core.ui.res.TangemTheme /** @@ -168,6 +150,10 @@ private fun ContentRow( maxLines = 1, softWrap = false, overflow = TextOverflow.Ellipsis, + autoSize = TextAutoSize.StepBased( + minFontSize = TangemTheme.typography3.caption.medium.fontSize, + maxFontSize = TangemTheme.typography3.body.medium.fontSize, + ), ) } } diff --git a/core/ui/src/main/res/drawable/ic_card_pin_24.xml b/core/ui/src/main/res/drawable/ic_card_pin_24.xml index be0bd72728..0678b0492c 100644 --- a/core/ui/src/main/res/drawable/ic_card_pin_24.xml +++ b/core/ui/src/main/res/drawable/ic_card_pin_24.xml @@ -1,18 +1,3 @@ - diff --git a/core/ui/src/main/res/drawable/ic_visa_card_details_24.xml b/core/ui/src/main/res/drawable/ic_visa_card_details_24.xml index 26a48d4fd1..579644aaa1 100644 --- a/core/ui/src/main/res/drawable/ic_visa_card_details_24.xml +++ b/core/ui/src/main/res/drawable/ic_visa_card_details_24.xml @@ -1,18 +1,3 @@ - + key(address.value) { + EnsItem( + modifier = Modifier.padding(horizontal = 16.dp), + onCopyClick = { + assetsUM.onCopyClick(address) + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + coroutineScope.launch { + topSnackbarHostState.showSnackbar( + SnackbarMessage( + startIconId = R.drawable.ic_check_24, + message = resourceReference(R.string.wallet_notification_address_copied), + ), + ) + } + }, + address = address.value, + ) + SpacerH8() + } + } + + PrimaryAddressesItems( + addresses = assetsUM.addresses.fastFilter { it.type is ReceiveAddress.Type.Primary }.toImmutableList(), + currencyIconState = assetsUM.currencyIconState, + onShareClick = { + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + assetsUM.onShareClick(it) + }, + onCopyClick = { address -> + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + coroutineScope.launch { + topSnackbarHostState.showSnackbar( + SnackbarMessage(message = resourceReference(R.string.wallet_notification_address_copied)), + ) + } + assetsUM.onCopyClick(address) + }, + onOpenQrCodeClick = { address -> + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + assetsUM.onOpenQrCodeClick(address) + }, + ) +} + +@Suppress("LongParameterList") +@Composable +private fun PrimaryAddressesItems( + addresses: ImmutableList, + currencyIconState: CurrencyIconState, + onShareClick: (String) -> Unit, + onCopyClick: (ReceiveAddress) -> Unit, + onOpenQrCodeClick: (String) -> Unit, +) { + if (addresses.isEmpty()) return + val pagerState = rememberPagerState( + initialPage = 0, + initialPageOffsetFraction = 0f, + pageCount = addresses::count, + ) + + HorizontalPager( + state = pagerState, + contentPadding = PaddingValues(horizontal = 16.dp), + pageSpacing = 16.dp, + ) { page -> + val address = addresses[page] + AddressItem( + currencyIconState = currencyIconState, + onOpenQrCodeClick = { onOpenQrCodeClick(address.value) }, + onCopyClick = { onCopyClick(address) }, + onShareClick = { onShareClick(address.value) }, + primaryType = address.type as ReceiveAddress.Type.Primary, + address = address.value, + isDynamicAddress = address.type is ReceiveAddress.Type.Primary.Dynamic, + ) + } + SpacerH(8.dp) + if (addresses.size > 1) { + TangemPagerIndicator(pagerState = pagerState) + } +} + +@Suppress("LongParameterList") +@Composable +private fun AddressItem( + currencyIconState: CurrencyIconState, + onOpenQrCodeClick: () -> Unit, + onCopyClick: () -> Unit, + onShareClick: () -> Unit, + primaryType: ReceiveAddress.Type.Primary, + address: String, + isDynamicAddress: Boolean, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxWidth() + .clickableSingle(onClick = onOpenQrCodeClick) + .padding(horizontal = 12.dp, vertical = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + CurrencyIcon( + modifier = Modifier.size(60.dp), + state = currencyIconState, + shouldDisplayNetwork = true, + iconSize = 56.dp, + networkBadgeSize = 20.dp, + ) + + SpacerH(12.dp) + + if (isDynamicAddress) { + DynamicAddressBadge(modifier = Modifier.padding(vertical = 4.dp)) + SpacerH(12.dp) + } + + Text( + text = primaryType.displayName.resolveReference(), + color = TangemTheme.colors3.text.primary, + style = TangemTheme.typography3.body.medium, + textAlign = TextAlign.Center, + ) + + SpacerH(4.dp) + + Text( + modifier = Modifier + .heightIn(min = 40.dp) + .padding(horizontal = 16.dp), + text = address, + color = TangemTheme.colors3.text.secondary, + style = TangemTheme.typography3.caption.medium, + textAlign = TextAlign.Center, + ) + + SpacerH(8.dp) + + Row( + modifier = Modifier + .clickableSingle(onClick = onOpenQrCodeClick) + .padding(10.dp), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + modifier = Modifier.size(16.dp), + imageVector = ImageVector.vectorResource(R.drawable.ic_qrcode_new_24), + tint = TangemTheme.colors3.icon.primary, + contentDescription = null, + ) + + SpacerW(6.dp) + + Text( + text = stringResourceSafe(R.string.token_receive_show_qr_code_title), + style = TangemTheme.typography3.body.medium, + color = TangemTheme.colors3.text.primary, + ) + } + + SpacerH(20.dp) + + ButtonsBlock( + onCopyClick = onCopyClick, + onShareClick = onShareClick, + ) + } +} + +@Composable +private fun ButtonsBlock(onCopyClick: () -> Unit, onShareClick: () -> Unit) { + val hapticFeedback = LocalHapticFeedback.current + val coroutineScope = rememberCoroutineScope() + val topSnackbarHostState = LocalTopSnackbarHostState.current + + Row( + modifier = Modifier.width(IntrinsicSize.Min), + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + TangemButton( + modifier = Modifier.weight(1f), + onClick = { + onCopyClick() + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + coroutineScope.launch { + topSnackbarHostState.showSnackbar( + SnackbarMessage( + startIconId = R.drawable.ic_check_24, + message = resourceReference(R.string.wallet_notification_address_copied), + ), + ) + } + }, + iconStart = TangemIconUM.Icon(imageVector = Icons.ic_copy_24), + variant = TangemButton.Variant.Secondary, + size = TangemButton.Size.X12, + text = TextReference.Res(id = R.string.common_copy), + ) + + TangemButton( + modifier = Modifier.weight(1f), + onClick = { + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + onShareClick() + }, + iconStart = TangemIconUM.Icon(imageVector = Icons.ic_share_android_24), + variant = TangemButton.Variant.Secondary, + size = TangemButton.Size.X12, + text = TextReference.Res(id = R.string.common_share), + ) + } +} + +@Composable +private fun EnsItem(onCopyClick: () -> Unit, address: String, modifier: Modifier = Modifier) { + Card( + modifier = modifier.fillMaxWidth(), + shape = RoundedCornerShape(24.dp), + colors = CardDefaults.cardColors(containerColor = TangemTheme.colors3.bg.opaque.primary), + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Image( + modifier = Modifier.size(36.dp), + imageVector = ImageVector.vectorResource(R.drawable.ic_ens_36), + contentDescription = null, + ) + SpacerW(12.dp) + + Text( + modifier = Modifier.weight(1f), + text = address, + color = TangemTheme.colors3.text.primary, + style = TangemTheme.typography3.body.medium, + overflow = TextOverflow.MiddleEllipsis, + maxLines = 1, + ) + + TangemButton( + modifier = Modifier.padding(start = 12.dp), + onClick = onCopyClick, + iconStart = TangemIconUM.Icon(imageVector = Icons.ic_share_android_24), + size = TangemButton.Size.X9, + variant = TangemButton.Variant.Secondary, + ) + } + } +} + +@Composable +private fun DynamicAddressBadge(modifier: Modifier = Modifier) { + TangemBadge( + modifier = modifier, + text = resourceReference(R.string.dynamic_addresses_receive_badge), + iconStart = TangemIconUM.Icon(iconRes = R.drawable.ic_dynamic_addresses_badge_16), + status = TangemBadge.Status.Info, + ) +} + @Composable private fun Info( currencyIconState: CurrencyIconState, @@ -110,227 +392,52 @@ private fun Info( .padding(horizontal = 18.dp) .fillMaxWidth(), text = stringResourceSafe(R.string.receive_bottom_sheet_no_memo_required_message), - style = TangemTheme.typography.caption1, - color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography3.caption.medium, + color = TangemTheme.colors3.text.tertiary, textAlign = TextAlign.Center, ) } notificationConfigs.fastForEach { notificationConfig -> key(notificationConfig.hashCode()) { - // TODO remove after new design system if (notificationConfig is NotificationUM.Warning.YieldSupplyIsActive) { - YieldSupplyDepositedWarning( - currencyIconState = currencyIconState, - title = stringResourceSafe( - R.string.yield_module_balance_info_sheet_title, - notificationConfig.tokenName, + TangemMessage( + title = resourceReference( + id = R.string.yield_module_balance_info_sheet_title, + formatArgs = wrappedList(notificationConfig.tokenName), ), - subtitle = stringResourceSafe(R.string.yield_module_balance_info_sheet_subtitle), - ) - } else { - Notification(config = notificationConfig.config) - } - } - } - } -} - -@Composable -private fun YieldSupplyDepositedWarning( - currencyIconState: CurrencyIconState, - title: String, - subtitle: String, - modifier: Modifier = Modifier, -) { - Surface( - modifier = modifier - .defaultMinSize(minHeight = TangemTheme.dimens.size44) - .fillMaxWidth(), - shape = TangemTheme.shapes.roundedCornersXMedium, - color = TangemTheme.colors.button.disabled, - ) { - Row( - modifier = Modifier - .padding(all = TangemTheme.dimens.spacing12), - ) { - Box( - modifier = Modifier - .height(22.dp) - .width(22.dp), - contentAlignment = Alignment.TopStart, - ) { - CurrencyIcon( - modifier = Modifier - .align(Alignment.TopStart) - .size(13.dp), - state = currencyIconState, - shouldDisplayNetwork = false, - iconSize = 13.dp, - ) - - Image( - modifier = Modifier - .background(TangemTheme.colors.background.tertiary, RoundedCornerShape(15.dp)) - .padding(1.dp) - .size(15.dp) - .align(Alignment.BottomEnd), - imageVector = ImageVector.vectorResource(R.drawable.img_aave_22), - contentDescription = null, - ) - } - - SpacerW(width = TangemTheme.dimens.spacing8) - - Column(modifier = Modifier.weight(1f)) { - Text( - text = title, - color = TangemTheme.colors.text.primary1, - style = TangemTheme.typography.button, - ) - - SpacerH(height = TangemTheme.dimens.spacing2) - - Text( - text = subtitle, - color = TangemTheme.colors.text.tertiary, - style = TangemTheme.typography.caption2, - ) - } - } - } -} - -@Composable -private fun AddressBlock(assetsUM: ReceiveAssetsUM, snackbarHostState: SnackbarHostState) { - val hapticFeedback = LocalHapticFeedback.current - val coroutineScope = rememberCoroutineScope() - val context = LocalContext.current - val resources = context.resources - val isRedesignEnabled = LocalRedesignEnabled.current - val topSnackbarHostState = LocalTopSnackbarHostState.current - - assetsUM.addresses - .fastFilter { it.type is ReceiveAddress.Type.Ens } - .fastForEach { address -> - key(address.value) { - EnsItem( - modifier = Modifier.padding(horizontal = 16.dp), - onCopyClick = { - assetsUM.onCopyClick(address) - hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) - coroutineScope.launch { - if (isRedesignEnabled) { - topSnackbarHostState.showSnackbar( - SnackbarMessage( - startIconId = R.drawable.ic_check_24, - message = resourceReference(R.string.wallet_notification_address_copied), - ), + subtitle = resourceReference(R.string.yield_module_balance_info_sheet_subtitle), + leadingContent = { + Box( + modifier = Modifier + .height(22.dp) + .width(22.dp), + contentAlignment = Alignment.TopStart, + ) { + CurrencyIcon( + modifier = Modifier + .align(Alignment.TopStart) + .size(13.dp), + state = currencyIconState, + shouldDisplayNetwork = false, + iconSize = 13.dp, ) - } else { - snackbarHostState.showSnackbar( - message = resources.getStringSafe( - R.string.wallet_notification_address_copied, - ), + Image( + modifier = Modifier + .background(TangemTheme.colors.background.tertiary, RoundedCornerShape(15.dp)) + .padding(1.dp) + .size(15.dp) + .align(Alignment.BottomEnd), + imageVector = ImageVector.vectorResource(R.drawable.img_aave_22), + contentDescription = null, ) } - } - }, - address = address.value, - ) - SpacerH8() - } - } - - PrimaryAddressesItems( - addresses = assetsUM.addresses.fastFilter { it.type is ReceiveAddress.Type.Primary }.toImmutableList(), - currencyIconState = assetsUM.currencyIconState, - snackbarHostState = snackbarHostState, - onShareClick = { - hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) - assetsUM.onShareClick(it) - }, - onCopyClick = { - hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) - coroutineScope.launch { - snackbarHostState.showSnackbar( - message = resources.getStringSafe( - R.string.wallet_notification_address_copied, - ), - ) - } - assetsUM.onCopyClick(it) - }, - onOpenQrCodeClick = { - hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) - assetsUM.onOpenQrCodeClick(it) - }, - ) -} - -@Suppress("LongParameterList") -@Composable -private fun PrimaryAddressesItems( - addresses: ImmutableList, - currencyIconState: CurrencyIconState, - snackbarHostState: SnackbarHostState, - onShareClick: (String) -> Unit, - onCopyClick: (ReceiveAddress) -> Unit, - onOpenQrCodeClick: (String) -> Unit, -) { - if (addresses.isEmpty()) return - var selectedAddress by remember { mutableStateOf(addresses.first()) } - val pagerState = rememberPagerState( - initialPage = 0, - initialPageOffsetFraction = 0f, - pageCount = addresses::count, - ) - LaunchedEffect(key1 = pagerState.currentPage) { - selectedAddress = addresses[pagerState.currentPage] - } - - HorizontalPager( - state = pagerState, - contentPadding = PaddingValues(horizontal = 16.dp), - pageSpacing = 16.dp, - ) { - AddressItem( - currencyIconState = currencyIconState, - onOpenQrCodeClick = { onOpenQrCodeClick(selectedAddress.value) }, - onCopyClick = { onCopyClick(selectedAddress) }, - onShareClick = { onShareClick(selectedAddress.value) }, - primaryType = selectedAddress.type as ReceiveAddress.Type.Primary, - address = selectedAddress.value, - isDynamicAddress = selectedAddress.type is ReceiveAddress.Type.Primary.Dynamic, - snackbarHostState = snackbarHostState, - ) - } - - SpacerH(4.dp) - - if (pagerState.pageCount > 1) { - val indicatorState = rememberLazyListState() - val selectedColor = TangemTheme.colors.icon.primary1 - val unselectedColor = TangemTheme.colors.icon.informative - - LazyRow( - modifier = Modifier.height(20.dp), - state = indicatorState, - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically, - ) { - repeat(pagerState.pageCount) { iteration -> - item(key = iteration) { - val color by animateColorAsState( - targetValue = if (pagerState.currentPage == iteration) selectedColor else unselectedColor, - label = "", + }, ) - - Box( - modifier = Modifier - .padding(horizontal = 4.dp, vertical = 6.dp) - .background(color = color, shape = CircleShape) - .size(7.dp), + } else { + TangemMessage( + config = notificationConfig.config, + contentColor = Color.Transparent, ) } } @@ -338,308 +445,13 @@ private fun PrimaryAddressesItems( } } -@Suppress("LongParameterList") -@Composable -private fun AddressItem( - currencyIconState: CurrencyIconState, - onOpenQrCodeClick: () -> Unit, - onCopyClick: () -> Unit, - onShareClick: () -> Unit, - primaryType: ReceiveAddress.Type.Primary, - address: String, - isDynamicAddress: Boolean, - snackbarHostState: SnackbarHostState, - modifier: Modifier = Modifier, -) { - Card( - modifier = modifier.fillMaxWidth(), - shape = RoundedCornerShape(16.dp), - colors = CardDefaults.cardColors(containerColor = TangemTheme.colors.background.action), - onClick = onOpenQrCodeClick, - ) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 12.dp, vertical = 32.dp), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - CurrencyIcon( - modifier = Modifier.size(56.dp), - state = currencyIconState, - shouldDisplayNetwork = true, - iconSize = 56.dp, - ) - - SpacerH(12.dp) - - if (isDynamicAddress) { - DynamicAddressBadge() - SpacerH(8.dp) - } - - Text( - text = primaryType.displayName.resolveReference(), - color = TangemTheme.colors.text.primary1, - style = TangemTheme.typography.subtitle1, - textAlign = TextAlign.Center, - ) - - Text( - modifier = Modifier - .heightIn(min = 40.dp) - .padding(horizontal = 16.dp), - text = address, - color = TangemTheme.colors.text.tertiary, - style = TangemTheme.typography.body2, - textAlign = TextAlign.Center, - ) - - SpacerH8() - - Row( - modifier = Modifier - .clip(RoundedCornerShape(16.dp)) - .clickable(onClick = onOpenQrCodeClick) - .padding(vertical = 6.dp, horizontal = 12.dp), - horizontalArrangement = Arrangement.Center, - ) { - Icon( - modifier = Modifier.size(16.dp), - imageVector = ImageVector.vectorResource(R.drawable.ic_qrcode_new_24), - contentDescription = null, - ) - - SpacerW(4.dp) - - Text( - text = stringResourceSafe(R.string.token_receive_show_qr_code_title), - style = TangemTheme.typography.caption1, - color = TangemTheme.colors.text.secondary, - ) - } - - SpacerH(20.dp) - - ButtonsBlock( - snackbarHostState = snackbarHostState, - onCopyClick = onCopyClick, - onShareClick = onShareClick, - ) - } - } -} - -@Composable -private fun ButtonsBlock(snackbarHostState: SnackbarHostState, onCopyClick: () -> Unit, onShareClick: () -> Unit) { - val hapticFeedback = LocalHapticFeedback.current - val coroutineScope = rememberCoroutineScope() - val context = LocalContext.current - val resources = context.resources - val isRedesignEnabled = LocalRedesignEnabled.current - val topSnackbarHostState = LocalTopSnackbarHostState.current - - Row( - modifier = Modifier.width(IntrinsicSize.Min), - horizontalArrangement = Arrangement.spacedBy(8.dp), - ) { - ActionButtonWithResizableText( - modifier = Modifier.weight(1f), - config = ActionButtonConfig( - text = TextReference.Res(id = R.string.common_copy), - iconResId = R.drawable.ic_copy_new_24, - onClick = { - onCopyClick() - hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) - coroutineScope.launch { - if (isRedesignEnabled) { - topSnackbarHostState.showSnackbar( - SnackbarMessage( - startIconId = R.drawable.ic_check_24, - message = resourceReference(R.string.wallet_notification_address_copied), - ), - ) - } else { - snackbarHostState.showSnackbar( - message = resources.getStringSafe(R.string.wallet_notification_address_copied), - ) - } - } - }, - ), - ) - - ActionButtonWithResizableText( - modifier = Modifier.weight(1f), - config = ActionButtonConfig( - text = TextReference.Res(id = R.string.common_share), - iconResId = R.drawable.ic_share_24, - onClick = { - hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) - onShareClick() - }, - ), - ) - } -} - -@Composable -private fun EnsItem(onCopyClick: () -> Unit, address: String, modifier: Modifier = Modifier) { - Card( - modifier = modifier.fillMaxWidth(), - shape = RoundedCornerShape(16.dp), - colors = CardDefaults.cardColors(containerColor = TangemTheme.colors.background.action), - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 14.dp, horizontal = 12.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Image( - modifier = Modifier.size(36.dp), - imageVector = ImageVector.vectorResource(R.drawable.ic_ens_36), - contentDescription = null, - ) - - SpacerW12() - - EllipsisText( - modifier = Modifier.weight(1f), - text = address, - ellipsis = TextEllipsis.Middle, - color = TangemTheme.colors.text.primary1, - style = TangemTheme.typography.subtitle1, - ) - - TangemIconButton( - modifier = Modifier.size(TangemTheme.dimens.size28), - iconRes = R.drawable.ic_share_24, - innerPadding = 6.dp, - onClick = onCopyClick, - ) - } - } -} - -@Composable -private fun LoadingBlock(modifier: Modifier = Modifier) { - Card( - modifier = modifier.fillMaxWidth(), - shape = RoundedCornerShape(16.dp), - colors = CardDefaults.cardColors(containerColor = TangemTheme.colors.background.action), - ) { - Box( - modifier = modifier - .fillMaxWidth() - .heightIn(min = 64.dp), - contentAlignment = Alignment.Center, - ) { - CircularProgressIndicator( - color = TangemTheme.colors.icon.informative, - modifier = Modifier.padding(TangemTheme.dimens.spacing8), - ) - } - } -} - -@Composable -private fun ActionButtonWithResizableText(config: ActionButtonConfig, modifier: Modifier = Modifier) { - ActionBaseButton( - config = config, - shape = RoundedCornerShape(size = TangemTheme.dimens.radius24), - content = { contentModifier -> - ActionButtonContent( - config = config, - text = { color -> - Text( - text = config.text.resolveReference(), - autoSize = TextAutoSize.StepBased( - minFontSize = 10.sp, - maxFontSize = TangemTheme.typography.button.fontSize, - ), - color = color, - overflow = TextOverflow.Ellipsis, - maxLines = 1, - style = TangemTheme.typography.button, - ) - }, - modifier = contentModifier.padding(horizontal = 16.dp), - paddingBetweenIconAndText = 4.dp, - ) - }, - modifier = modifier, - color = TangemTheme.colors.button.secondary, - ) -} - -@Composable -private fun DynamicAddressBadge(modifier: Modifier = Modifier) { - Row( - modifier = modifier - .background( - color = TangemTheme.colors.text.accent.copy(alpha = 0.1f), - shape = RoundedCornerShape(percent = 50), - ) - .padding(horizontal = 12.dp, vertical = 4.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(4.dp), - ) { - Icon( - painter = painterResource( - id = R.drawable.ic_dynamic_addresses_badge_16, - ), - contentDescription = null, - modifier = Modifier.size(16.dp), - tint = TangemTheme.colors.icon.accent, - ) - Text( - text = stringResourceSafe(R.string.dynamic_addresses_receive_badge), - style = TangemTheme.typography.caption1, - color = TangemTheme.colors.text.accent, - ) - } -} - @Composable @Preview(showBackground = true, widthDp = 360) @Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) private fun Preview_TokenReceiveAssetsContent( @PreviewParameter(TokenReceiveAssetsContentProvider::class) params: ReceiveAssetsUM, ) { - TangemThemePreview { + TangemThemePreviewRedesign { TokenReceiveAssetsContent(assetsUM = params) } -} - -private class TokenReceiveAssetsContentProvider : PreviewParameterProvider { - val address = ReceiveAddress( - value = "0xe5178c7d4d0e861ed2e9414e045b501226b0de8d", - type = ReceiveAddress.Type.Primary.Default( - displayName = stringReference("Etherium address"), - ), - ) - private val config = ReceiveAssetsUM( - notificationConfigs = - persistentListOf( - NotificationUM.Warning( - title = stringReference("Send only XLM on the Ethereum network"), - subtitle = resourceReference(R.string.receive_bottom_sheet_warning_message_description), - ), - ), - addresses = persistentListOf( - address, - address, - address.copy(type = ReceiveAddress.Type.Ens, value = "papasha.eth"), - ), - showMemoDisclaimer = false, - onCopyClick = {}, - onOpenQrCodeClick = {}, - isEnsResultLoading = true, - network = "USDT", - currencyIconState = CurrencyIconState.Locked, - onShareClick = {}, - ) - - override val values: Sequence - get() = sequenceOf(config) } \ No newline at end of file diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveAssetsContentLegacy.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveAssetsContentLegacy.kt new file mode 100644 index 0000000000..c052bb3c43 --- /dev/null +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveAssetsContentLegacy.kt @@ -0,0 +1,672 @@ +package com.tangem.features.tokenreceive.ui + +import android.content.res.Configuration +import androidx.compose.animation.animateColorAsState +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.TextAutoSize +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalHapticFeedback +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.util.fastFilter +import androidx.compose.ui.util.fastForEach +import com.tangem.common.ui.notifications.NotificationUM +import com.tangem.core.res.getStringSafe +import com.tangem.core.ui.R +import com.tangem.core.ui.components.* +import com.tangem.core.ui.components.atoms.text.EllipsisText +import com.tangem.core.ui.components.atoms.text.TextEllipsis +import com.tangem.core.ui.components.buttons.actions.ActionBaseButton +import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig +import com.tangem.core.ui.components.buttons.actions.ActionButtonContent +import com.tangem.core.ui.components.buttons.small.TangemIconButton +import com.tangem.core.ui.components.currency.icon.CurrencyIcon +import com.tangem.core.ui.components.currency.icon.CurrencyIconState +import com.tangem.core.ui.components.notifications.Notification +import com.tangem.core.ui.extensions.* +import com.tangem.core.ui.message.SnackbarMessage +import com.tangem.core.ui.res.* +import com.tangem.features.tokenreceive.entity.ReceiveAddress +import com.tangem.features.tokenreceive.ui.state.ReceiveAssetsUM +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.launch + +@Composable +internal fun TokenReceiveAssetsContentLegacy(assetsUM: ReceiveAssetsUM) { + val snackbarHostState = remember(::SnackbarHostState) + + ContainerWithSnackbarHost(snackbarHostState = snackbarHostState) { + Column( + modifier = Modifier + .fillMaxWidth() + .background(color = TangemTheme.colors.background.tertiary) + .padding(bottom = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + AddressBlock( + assetsUM = assetsUM, + snackbarHostState = snackbarHostState, + ) + + if (assetsUM.isEnsResultLoading) { + SpacerH8() + LoadingBlock(modifier = Modifier.padding(horizontal = 16.dp)) + } + + SpacerH12() + + Info( + showMemoDisclaimer = assetsUM.showMemoDisclaimer, + notificationConfigs = assetsUM.notificationConfigs, + currencyIconState = assetsUM.currencyIconState, + ) + } + } +} + +@Composable +private fun Info( + currencyIconState: CurrencyIconState, + notificationConfigs: ImmutableList, + showMemoDisclaimer: Boolean, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier.padding(horizontal = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(space = 16.dp), + ) { + if (showMemoDisclaimer) { + Text( + modifier = Modifier + .padding(horizontal = 18.dp) + .fillMaxWidth(), + text = stringResourceSafe(R.string.receive_bottom_sheet_no_memo_required_message), + style = TangemTheme.typography.caption1, + color = TangemTheme.colors.text.tertiary, + textAlign = TextAlign.Center, + ) + } + + notificationConfigs.fastForEach { notificationConfig -> + key(notificationConfig.hashCode()) { + if (notificationConfig is NotificationUM.Warning.YieldSupplyIsActive) { + YieldSupplyDepositedWarning( + currencyIconState = currencyIconState, + title = stringResourceSafe( + R.string.yield_module_balance_info_sheet_title, + notificationConfig.tokenName, + ), + subtitle = stringResourceSafe(R.string.yield_module_balance_info_sheet_subtitle), + ) + } else { + Notification(config = notificationConfig.config) + } + } + } + } +} + +@Composable +private fun YieldSupplyDepositedWarning( + currencyIconState: CurrencyIconState, + title: String, + subtitle: String, + modifier: Modifier = Modifier, +) { + Surface( + modifier = modifier + .defaultMinSize(minHeight = TangemTheme.dimens.size44) + .fillMaxWidth(), + shape = TangemTheme.shapes.roundedCornersXMedium, + color = TangemTheme.colors.button.disabled, + ) { + Row( + modifier = Modifier + .padding(all = TangemTheme.dimens.spacing12), + ) { + Box( + modifier = Modifier + .height(22.dp) + .width(22.dp), + contentAlignment = Alignment.TopStart, + ) { + CurrencyIcon( + modifier = Modifier + .align(Alignment.TopStart) + .size(13.dp), + state = currencyIconState, + shouldDisplayNetwork = false, + iconSize = 13.dp, + ) + + Image( + modifier = Modifier + .background(TangemTheme.colors.background.tertiary, RoundedCornerShape(15.dp)) + .padding(1.dp) + .size(15.dp) + .align(Alignment.BottomEnd), + imageVector = ImageVector.vectorResource(R.drawable.img_aave_22), + contentDescription = null, + ) + } + + SpacerW(width = TangemTheme.dimens.spacing8) + + Column(modifier = Modifier.weight(1f)) { + Text( + text = title, + color = TangemTheme.colors.text.primary1, + style = TangemTheme.typography.button, + ) + + SpacerH(height = TangemTheme.dimens.spacing2) + + Text( + text = subtitle, + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.caption2, + ) + } + } + } +} + +@Composable +private fun AddressBlock(assetsUM: ReceiveAssetsUM, snackbarHostState: SnackbarHostState) { + val hapticFeedback = LocalHapticFeedback.current + val coroutineScope = rememberCoroutineScope() + val context = LocalContext.current + val resources = context.resources + val isRedesignEnabled = LocalRedesignEnabled.current + val topSnackbarHostState = LocalTopSnackbarHostState.current + + assetsUM.addresses + .fastFilter { it.type is ReceiveAddress.Type.Ens } + .fastForEach { address -> + key(address.value) { + EnsItem( + modifier = Modifier.padding(horizontal = 16.dp), + onCopyClick = { + assetsUM.onCopyClick(address) + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + coroutineScope.launch { + if (isRedesignEnabled) { + topSnackbarHostState.showSnackbar( + SnackbarMessage( + startIconId = R.drawable.ic_check_24, + message = resourceReference(R.string.wallet_notification_address_copied), + ), + ) + } else { + snackbarHostState.showSnackbar( + message = resources.getStringSafe( + R.string.wallet_notification_address_copied, + ), + ) + } + } + }, + address = address.value, + ) + SpacerH8() + } + } + + PrimaryAddressesItems( + addresses = assetsUM.addresses.fastFilter { it.type is ReceiveAddress.Type.Primary }.toImmutableList(), + currencyIconState = assetsUM.currencyIconState, + snackbarHostState = snackbarHostState, + onShareClick = { address -> + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + assetsUM.onShareClick(address) + }, + onCopyClick = { address -> + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + coroutineScope.launch { + snackbarHostState.showSnackbar( + message = resources.getStringSafe( + R.string.wallet_notification_address_copied, + ), + ) + } + assetsUM.onCopyClick(address) + }, + onOpenQrCodeClick = { address -> + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + assetsUM.onOpenQrCodeClick(address) + }, + ) +} + +@Suppress("LongParameterList") +@Composable +private fun PrimaryAddressesItems( + addresses: ImmutableList, + currencyIconState: CurrencyIconState, + snackbarHostState: SnackbarHostState, + onShareClick: (String) -> Unit, + onCopyClick: (ReceiveAddress) -> Unit, + onOpenQrCodeClick: (String) -> Unit, +) { + if (addresses.isEmpty()) return + var selectedAddress by remember { mutableStateOf(addresses.first()) } + val pagerState = rememberPagerState( + initialPage = 0, + initialPageOffsetFraction = 0f, + pageCount = addresses::count, + ) + LaunchedEffect(key1 = pagerState.currentPage) { + selectedAddress = addresses[pagerState.currentPage] + } + + HorizontalPager( + state = pagerState, + contentPadding = PaddingValues(horizontal = 16.dp), + pageSpacing = 16.dp, + ) { + AddressItem( + currencyIconState = currencyIconState, + onOpenQrCodeClick = { onOpenQrCodeClick(selectedAddress.value) }, + onCopyClick = { onCopyClick(selectedAddress) }, + onShareClick = { onShareClick(selectedAddress.value) }, + primaryType = selectedAddress.type as ReceiveAddress.Type.Primary, + address = selectedAddress.value, + isDynamicAddress = selectedAddress.type is ReceiveAddress.Type.Primary.Dynamic, + snackbarHostState = snackbarHostState, + ) + } + + SpacerH(4.dp) + + if (pagerState.pageCount > 1) { + val indicatorState = rememberLazyListState() + val selectedColor = TangemTheme.colors.icon.primary1 + val unselectedColor = TangemTheme.colors.icon.informative + + LazyRow( + modifier = Modifier.height(20.dp), + state = indicatorState, + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, + ) { + repeat(pagerState.pageCount) { iteration -> + item(key = iteration) { + val color by animateColorAsState( + targetValue = if (pagerState.currentPage == iteration) selectedColor else unselectedColor, + label = "", + ) + + Box( + modifier = Modifier + .padding(horizontal = 4.dp, vertical = 6.dp) + .background(color = color, shape = CircleShape) + .size(7.dp), + ) + } + } + } + } +} + +@Suppress("LongParameterList") +@Composable +private fun AddressItem( + currencyIconState: CurrencyIconState, + onOpenQrCodeClick: () -> Unit, + onCopyClick: () -> Unit, + onShareClick: () -> Unit, + primaryType: ReceiveAddress.Type.Primary, + address: String, + isDynamicAddress: Boolean, + snackbarHostState: SnackbarHostState, + modifier: Modifier = Modifier, +) { + Card( + modifier = modifier.fillMaxWidth(), + shape = RoundedCornerShape(16.dp), + colors = CardDefaults.cardColors(containerColor = TangemTheme.colors.background.action), + onClick = onOpenQrCodeClick, + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 12.dp, vertical = 32.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + CurrencyIcon( + modifier = Modifier.size(56.dp), + state = currencyIconState, + shouldDisplayNetwork = true, + iconSize = 56.dp, + ) + + SpacerH(12.dp) + + if (isDynamicAddress) { + DynamicAddressBadge() + SpacerH(8.dp) + } + + Text( + text = primaryType.displayName.resolveReference(), + color = TangemTheme.colors.text.primary1, + style = TangemTheme.typography.subtitle1, + textAlign = TextAlign.Center, + ) + + Text( + modifier = Modifier + .heightIn(min = 40.dp) + .padding(horizontal = 16.dp), + text = address, + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.body2, + textAlign = TextAlign.Center, + ) + + SpacerH8() + + Row( + modifier = Modifier + .clip(RoundedCornerShape(16.dp)) + .clickable(onClick = onOpenQrCodeClick) + .padding(vertical = 6.dp, horizontal = 12.dp), + horizontalArrangement = Arrangement.Center, + ) { + Icon( + modifier = Modifier.size(16.dp), + imageVector = ImageVector.vectorResource(R.drawable.ic_qrcode_new_24), + contentDescription = null, + ) + + SpacerW(4.dp) + + Text( + text = stringResourceSafe(R.string.token_receive_show_qr_code_title), + style = TangemTheme.typography.caption1, + color = TangemTheme.colors.text.secondary, + ) + } + + SpacerH(20.dp) + + ButtonsBlock( + snackbarHostState = snackbarHostState, + onCopyClick = onCopyClick, + onShareClick = onShareClick, + ) + } + } +} + +@Composable +private fun ButtonsBlock(snackbarHostState: SnackbarHostState, onCopyClick: () -> Unit, onShareClick: () -> Unit) { + val hapticFeedback = LocalHapticFeedback.current + val coroutineScope = rememberCoroutineScope() + val context = LocalContext.current + val resources = context.resources + val isRedesignEnabled = LocalRedesignEnabled.current + val topSnackbarHostState = LocalTopSnackbarHostState.current + + Row( + modifier = Modifier.width(IntrinsicSize.Min), + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + ActionButtonWithResizableText( + modifier = Modifier.weight(1f), + config = ActionButtonConfig( + text = TextReference.Res(id = R.string.common_copy), + iconResId = R.drawable.ic_copy_new_24, + onClick = { + onCopyClick() + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + coroutineScope.launch { + if (isRedesignEnabled) { + topSnackbarHostState.showSnackbar( + SnackbarMessage( + startIconId = R.drawable.ic_check_24, + message = resourceReference(R.string.wallet_notification_address_copied), + ), + ) + } else { + snackbarHostState.showSnackbar( + message = resources.getStringSafe(R.string.wallet_notification_address_copied), + ) + } + } + }, + ), + ) + + ActionButtonWithResizableText( + modifier = Modifier.weight(1f), + config = ActionButtonConfig( + text = TextReference.Res(id = R.string.common_share), + iconResId = R.drawable.ic_share_24, + onClick = { + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + onShareClick() + }, + ), + ) + } +} + +@Composable +private fun EnsItem(onCopyClick: () -> Unit, address: String, modifier: Modifier = Modifier) { + Card( + modifier = modifier.fillMaxWidth(), + shape = RoundedCornerShape(16.dp), + colors = CardDefaults.cardColors(containerColor = TangemTheme.colors.background.action), + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 14.dp, horizontal = 12.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Image( + modifier = Modifier.size(36.dp), + imageVector = ImageVector.vectorResource(R.drawable.ic_ens_36), + contentDescription = null, + ) + + SpacerW12() + + EllipsisText( + modifier = Modifier.weight(1f), + text = address, + ellipsis = TextEllipsis.Middle, + color = TangemTheme.colors.text.primary1, + style = TangemTheme.typography.subtitle1, + ) + + TangemIconButton( + modifier = Modifier.size(TangemTheme.dimens.size28), + iconRes = R.drawable.ic_share_24, + innerPadding = 6.dp, + onClick = onCopyClick, + ) + } + } +} + +@Composable +private fun LoadingBlock(modifier: Modifier = Modifier) { + Card( + modifier = modifier.fillMaxWidth(), + shape = RoundedCornerShape(16.dp), + colors = CardDefaults.cardColors(containerColor = TangemTheme.colors.background.action), + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .heightIn(min = 64.dp), + contentAlignment = Alignment.Center, + ) { + CircularProgressIndicator( + color = TangemTheme.colors.icon.informative, + modifier = Modifier.padding(TangemTheme.dimens.spacing8), + ) + } + } +} + +@Composable +private fun ActionButtonWithResizableText(config: ActionButtonConfig, modifier: Modifier = Modifier) { + ActionBaseButton( + config = config, + shape = RoundedCornerShape(size = TangemTheme.dimens.radius24), + content = { contentModifier -> + ActionButtonContent( + config = config, + text = { color -> + Text( + text = config.text.resolveReference(), + autoSize = TextAutoSize.StepBased( + minFontSize = 10.sp, + maxFontSize = TangemTheme.typography.button.fontSize, + ), + color = color, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + style = TangemTheme.typography.button, + ) + }, + modifier = contentModifier.padding(horizontal = 16.dp), + paddingBetweenIconAndText = 4.dp, + ) + }, + modifier = modifier, + color = TangemTheme.colors.button.secondary, + ) +} + +@Composable +private fun DynamicAddressBadge(modifier: Modifier = Modifier) { + Row( + modifier = modifier + .background( + color = TangemTheme.colors.text.accent.copy(alpha = 0.1f), + shape = RoundedCornerShape(percent = 50), + ) + .padding(horizontal = 12.dp, vertical = 4.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + Icon( + painter = painterResource( + id = R.drawable.ic_dynamic_addresses_badge_16, + ), + contentDescription = null, + modifier = Modifier.size(16.dp), + tint = TangemTheme.colors.icon.accent, + ) + Text( + text = stringResourceSafe(R.string.dynamic_addresses_receive_badge), + style = TangemTheme.typography.caption1, + color = TangemTheme.colors.text.accent, + ) + } +} + +@Composable +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +private fun Preview_TokenReceiveAssetsContent( + @PreviewParameter(TokenReceiveAssetsContentProvider::class) params: ReceiveAssetsUM, +) { + TangemThemePreview { + TokenReceiveAssetsContentLegacy(assetsUM = params) + } +} + +internal class TokenReceiveAssetsContentProvider : PreviewParameterProvider { + val address = ReceiveAddress( + value = "0xe5178c7d4d0e861ed2e9414e045b501226b0de8d", + type = ReceiveAddress.Type.Primary.Default( + displayName = stringReference("Etherium address"), + ), + ) + private val dynamicAddress = ReceiveAddress( + value = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", + type = ReceiveAddress.Type.Primary.Dynamic( + displayName = stringReference("Ethereum address"), + ), + ) + private val tokenIconState = CurrencyIconState.TokenIcon( + url = null, + topBadgeIconResId = null, + fallbackTint = TangemColorPalette.Black, + fallbackBackground = TangemColorPalette.Meadow, + isGrayscale = false, + shouldShowCustomBadge = false, + ) + private val config = ReceiveAssetsUM( + notificationConfigs = + persistentListOf( + NotificationUM.Warning( + title = stringReference("Send only XLM on the Ethereum network"), + subtitle = resourceReference(R.string.receive_bottom_sheet_warning_message_description), + ), + ), + addresses = persistentListOf( + address, + address, + address.copy(type = ReceiveAddress.Type.Ens, value = "papasha.eth"), + ), + showMemoDisclaimer = false, + onCopyClick = {}, + onOpenQrCodeClick = {}, + isEnsResultLoading = true, + network = "USDT", + currencyIconState = CurrencyIconState.Locked, + onShareClick = {}, + ) + private val yieldSupplyIsActiveConfig = config.copy( + notificationConfigs = persistentListOf( + NotificationUM.Warning.YieldSupplyIsActive(tokenName = "USDT"), + ), + isEnsResultLoading = false, + currencyIconState = tokenIconState, + ) + private val dynamicAddressConfig = config.copy( + addresses = persistentListOf( + dynamicAddress, + address, + address.copy(type = ReceiveAddress.Type.Ens, value = "papasha.eth"), + ), + isEnsResultLoading = false, + notificationConfigs = persistentListOf(), + currencyIconState = tokenIconState, + ) + + override val values: Sequence + get() = sequenceOf(config, yieldSupplyIsActiveConfig, dynamicAddressConfig) +} \ No newline at end of file diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveContent.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveContent.kt index f4e495de55..5d9dc29fa8 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveContent.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveContent.kt @@ -2,22 +2,32 @@ package com.tangem.features.tokenreceive.ui import androidx.compose.animation.animateContentSize import androidx.compose.animation.core.tween -import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier import com.arkivanov.decompose.extensions.compose.stack.Children import com.arkivanov.decompose.extensions.compose.stack.animation.fade import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation import com.arkivanov.decompose.router.stack.ChildStack import com.tangem.core.ui.R +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent -import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet -import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetType import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.core.ui.ds.image.TangemIconUM +import com.tangem.core.ui.ds.topbar.TangemTopBar +import com.tangem.core.ui.ds.topbar.TangemTopBarType +import com.tangem.core.ui.ds2.button.TangemButton import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.res.LocalHazeState import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.generated.icons.Icons +import com.tangem.core.ui.res.generated.icons.ic_chevron_left_24 +import com.tangem.core.ui.res.generated.icons.ic_cross_24 import com.tangem.features.tokenreceive.route.TokenReceiveRoutes +import dev.chrisbanes.haze.rememberHazeState @Composable internal fun TokenReceiveContentSheet( @@ -26,59 +36,89 @@ internal fun TokenReceiveContentSheet( onBackClick: () -> Unit, contentStack: ChildStack, ) { - TangemModalBottomSheet( - config = TangemBottomSheetConfig( - isShown = true, - onDismissRequest = onCloseClick, - content = TangemBottomSheetConfigContent.Empty, - ), - onBack = onBackClick, - containerColor = TangemTheme.colors.background.tertiary, - title = { - Title( - route = route, - onBackClick = onBackClick, - onCloseClick = onCloseClick, - ) - }, - content = { - TokenReceiveContent( - stackState = contentStack, - modifier = Modifier, - ) - }, - ) + CompositionLocalProvider(LocalHazeState provides rememberHazeState()) { + TangemBottomSheet( + type = TangemBottomSheetType.Modal, + onBack = onBackClick, + config = TangemBottomSheetConfig( + isShown = true, + onDismissRequest = onCloseClick, + content = TangemBottomSheetConfigContent.Empty, + ), + containerColor = TangemTheme.colors3.bg.secondary, + title = { + Title( + route = route, + onBackClick = onBackClick, + onCloseClick = onCloseClick, + ) + }, + content = { + TokenReceiveContent( + stackState = contentStack, + modifier = Modifier, + ) + }, + ) + } } @Composable private fun Title(route: TokenReceiveRoutes, onBackClick: () -> Unit, onCloseClick: () -> Unit) { when (route) { is TokenReceiveRoutes.QrCode -> { - TangemModalBottomSheetTitle( - startIconRes = R.drawable.ic_back_24, - onStartClick = onBackClick, - endIconRes = R.drawable.ic_close_24, - onEndClick = onCloseClick, + TangemTopBar( + type = TangemTopBarType.BottomSheet, + endContent = { + TangemButton( + iconStart = TangemIconUM.Icon(Icons.ic_cross_24), + onClick = onCloseClick, + size = TangemButton.Size.X11, + variant = TangemButton.Variant.Material, + ) + }, + startContent = { + TangemButton( + iconStart = TangemIconUM.Icon(imageVector = Icons.ic_chevron_left_24), + onClick = onBackClick, + size = TangemButton.Size.X11, + variant = TangemButton.Variant.Material, + ) + }, ) } TokenReceiveRoutes.ReceiveAssets -> { - TangemModalBottomSheetTitle( + TangemTopBar( title = resourceReference(R.string.domain_receive_assets_navigation_title), - endIconRes = R.drawable.ic_close_24, - onEndClick = onCloseClick, + type = TangemTopBarType.BottomSheet, + endContent = { + TangemButton( + iconStart = TangemIconUM.Icon(Icons.ic_cross_24), + onClick = onCloseClick, + size = TangemButton.Size.X11, + variant = TangemButton.Variant.Material, + ) + }, ) } TokenReceiveRoutes.Warning -> { - TangemModalBottomSheetTitle( - endIconRes = R.drawable.ic_close_24, - onEndClick = onCloseClick, + TangemTopBar( + type = TangemTopBarType.BottomSheet, + endContent = { + TangemButton( + iconStart = TangemIconUM.Icon(Icons.ic_cross_24), + onClick = onCloseClick, + size = TangemButton.Size.X11, + variant = TangemButton.Variant.Material, + ) + }, ) } } } @Composable -internal fun TokenReceiveContent( +private fun TokenReceiveContent( stackState: ChildStack, modifier: Modifier = Modifier, ) { @@ -86,9 +126,9 @@ internal fun TokenReceiveContent( stack = stackState, animation = stackAnimation(fade(animationSpec = tween(durationMillis = 100))), modifier = modifier - .fillMaxSize() + .fillMaxWidth() .animateContentSize(), ) { - it.instance.Content(Modifier.fillMaxSize()) + it.instance.Content(Modifier.fillMaxWidth()) } } \ No newline at end of file diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveContentLegacy.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveContentLegacy.kt new file mode 100644 index 0000000000..8d35837ae5 --- /dev/null +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveContentLegacy.kt @@ -0,0 +1,94 @@ +package com.tangem.features.tokenreceive.ui + +import androidx.compose.animation.animateContentSize +import androidx.compose.animation.core.tween +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.arkivanov.decompose.extensions.compose.stack.Children +import com.arkivanov.decompose.extensions.compose.stack.animation.fade +import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation +import com.arkivanov.decompose.router.stack.ChildStack +import com.tangem.core.ui.R +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent +import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet +import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle +import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.features.tokenreceive.route.TokenReceiveRoutes + +@Composable +internal fun TokenReceiveContentSheetLegacy( + route: TokenReceiveRoutes, + onCloseClick: () -> Unit, + onBackClick: () -> Unit, + contentStack: ChildStack, +) { + TangemModalBottomSheet( + config = TangemBottomSheetConfig( + isShown = true, + onDismissRequest = onCloseClick, + content = TangemBottomSheetConfigContent.Empty, + ), + onBack = onBackClick, + containerColor = TangemTheme.colors.background.tertiary, + title = { + Title( + route = route, + onBackClick = onBackClick, + onCloseClick = onCloseClick, + ) + }, + content = { + TokenReceiveContent( + stackState = contentStack, + modifier = Modifier, + ) + }, + ) +} + +@Composable +private fun Title(route: TokenReceiveRoutes, onBackClick: () -> Unit, onCloseClick: () -> Unit) { + when (route) { + is TokenReceiveRoutes.QrCode -> { + TangemModalBottomSheetTitle( + startIconRes = R.drawable.ic_back_24, + onStartClick = onBackClick, + endIconRes = R.drawable.ic_close_24, + onEndClick = onCloseClick, + ) + } + TokenReceiveRoutes.ReceiveAssets -> { + TangemModalBottomSheetTitle( + title = resourceReference(R.string.domain_receive_assets_navigation_title), + endIconRes = R.drawable.ic_close_24, + onEndClick = onCloseClick, + ) + } + TokenReceiveRoutes.Warning -> { + TangemModalBottomSheetTitle( + endIconRes = R.drawable.ic_close_24, + onEndClick = onCloseClick, + ) + } + } +} + +@Composable +private fun TokenReceiveContent( + stackState: ChildStack, + modifier: Modifier = Modifier, +) { + Children( + stack = stackState, + animation = stackAnimation(fade(animationSpec = tween(durationMillis = 100))), + modifier = modifier + .fillMaxSize() + .animateContentSize(), + ) { + it.instance.Content(Modifier.fillMaxSize()) + } +} \ No newline at end of file diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveQrCodeContent.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveQrCodeContent.kt index 0c4a083cec..ae42b08d41 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveQrCodeContent.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveQrCodeContent.kt @@ -2,11 +2,9 @@ package com.tangem.features.tokenreceive.ui import android.content.res.Configuration import androidx.compose.foundation.Image -import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember @@ -18,24 +16,25 @@ import androidx.compose.ui.graphics.painter.BitmapPainter import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.tangem.core.res.getStringSafe -import com.tangem.core.ui.components.* +import com.tangem.core.ui.components.SpacerH +import com.tangem.core.ui.ds.image.TangemIconUM +import com.tangem.core.ui.ds2.button.TangemButton import com.tangem.core.ui.extensions.* import com.tangem.core.ui.message.SnackbarMessage -import com.tangem.core.ui.res.LocalRedesignEnabled import com.tangem.core.ui.res.LocalTopSnackbarHostState import com.tangem.core.ui.res.TangemTheme -import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.res.TangemThemePreviewRedesign +import com.tangem.core.ui.res.generated.icons.Icons +import com.tangem.core.ui.res.generated.icons.ic_copy_24 +import com.tangem.core.ui.res.generated.icons.ic_share_android_24 import com.tangem.core.ui.test.TokenReceiveQrCodeBottomSheetTestTags import com.tangem.features.tokenreceive.impl.R import com.tangem.features.tokenreceive.ui.state.QrCodeUM @@ -43,34 +42,28 @@ import kotlinx.coroutines.launch @Composable internal fun TokenReceiveQrCodeContent(qrCodeUM: QrCodeUM) { - val snackbarHostState = remember(::SnackbarHostState) val qrCodePainter = rememberQrPainter(content = qrCodeUM.addressValue) - ContainerWithSnackbarHost(snackbarHostState = snackbarHostState) { - Column( - modifier = Modifier - .fillMaxWidth() - .background(color = TangemTheme.colors.background.tertiary) - .padding(bottom = 16.dp) - .padding(horizontal = 16.dp), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - SpacerH8() + Column( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 16.dp, start = 16.dp, end = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + QrCodePage( + addressFullName = qrCodeUM.addressName, + addressValue = qrCodeUM.addressValue, + network = qrCodeUM.network, + qrCodePainter = qrCodePainter, + ) - QrCodePage( - addressFullName = qrCodeUM.addressName, - addressValue = qrCodeUM.addressValue, - network = qrCodeUM.network, - qrCodePainter = qrCodePainter, - ) - SpacerH24() + SpacerH(24.dp) - Buttons( - onShareClick = { qrCodeUM.onShareClick(qrCodeUM.addressValue) }, - onCopyClick = qrCodeUM.onCopyClick, - snackbarHostState = snackbarHostState, - ) - } + Buttons( + modifier = Modifier.padding(top = 8.dp), + onShareClick = { qrCodeUM.onShareClick(qrCodeUM.addressValue) }, + onCopyClick = qrCodeUM.onCopyClick, + ) } } @@ -81,7 +74,12 @@ private fun QrCodePage(addressFullName: TextReference, addressValue: String, net horizontalAlignment = Alignment.CenterHorizontally, ) { Column( - modifier = Modifier.padding(horizontal = TangemTheme.dimens.size36), + modifier = Modifier.padding( + start = 16.dp, + end = 16.dp, + top = 24.dp, + bottom = 16.dp, + ), horizontalAlignment = Alignment.CenterHorizontally, ) { Text( @@ -90,22 +88,22 @@ private fun QrCodePage(addressFullName: TextReference, addressValue: String, net addressFullName.resolveReference(), network, ), - color = TangemTheme.colors.text.primary1, + color = TangemTheme.colors3.text.primary, textAlign = TextAlign.Center, - style = TangemTheme.typography.h3, + style = TangemTheme.typography3.heading.small, modifier = Modifier.testTag(TokenReceiveQrCodeBottomSheetTestTags.TITLE), ) - SpacerH(20.dp) + SpacerH(32.dp) Box( modifier = Modifier .border( - width = 8.dp, - color = TangemTheme.colors.icon.constant, - shape = RoundedCornerShape(8.dp), + width = 16.dp, + color = TangemTheme.colors3.bg.secondary, + shape = RoundedCornerShape(12.dp), ) - .padding(8.dp) + .padding(16.dp) .testTag(TokenReceiveQrCodeBottomSheetTestTags.QR_CODE), ) { @@ -116,79 +114,66 @@ private fun QrCodePage(addressFullName: TextReference, addressValue: String, net modifier = Modifier.sizeIn(minWidth = 148.dp), ) } - - SpacerH24() } Text( text = stringResourceSafe(R.string.wc_common_address), - color = TangemTheme.colors.text.tertiary, + color = TangemTheme.colors3.text.secondary, textAlign = TextAlign.Center, - style = TangemTheme.typography.subtitle2, + style = TangemTheme.typography3.caption.medium, ) - SpacerH2() + SpacerH(4.dp) Text( text = addressValue, - color = TangemTheme.colors.text.primary1, + color = TangemTheme.colors3.text.primary, textAlign = TextAlign.Center, - style = TangemTheme.typography.subtitle1, + style = TangemTheme.typography3.body.medium, modifier = Modifier.testTag(TokenReceiveQrCodeBottomSheetTestTags.ADDRESS), ) } } @Composable -private fun Buttons( - snackbarHostState: SnackbarHostState, - onShareClick: () -> Unit, - onCopyClick: () -> Unit, - modifier: Modifier = Modifier, -) { +private fun Buttons(onShareClick: () -> Unit, onCopyClick: () -> Unit, modifier: Modifier = Modifier) { val hapticFeedback = LocalHapticFeedback.current val coroutineScope = rememberCoroutineScope() - val context = LocalContext.current - val resources = context.resources - - val isRedesignEnabled = LocalRedesignEnabled.current val tangemTopSnackbarHostState = LocalTopSnackbarHostState.current Row( modifier = modifier, - horizontalArrangement = Arrangement.spacedBy(16.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp), ) { - SecondaryButtonIconStart( + TangemButton( modifier = Modifier.weight(1f), - text = stringResourceSafe(id = R.string.common_copy), - iconResId = R.drawable.ic_copy_24, onClick = { hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) onCopyClick() coroutineScope.launch { - if (isRedesignEnabled) { - tangemTopSnackbarHostState.showSnackbar( - SnackbarMessage( - startIconId = R.drawable.ic_check_24, - message = resourceReference(R.string.wallet_notification_address_copied), - ), - ) - } else { - snackbarHostState.showSnackbar( - message = resources.getStringSafe(R.string.wallet_notification_address_copied), - ) - } + tangemTopSnackbarHostState.showSnackbar( + SnackbarMessage( + startIconId = R.drawable.ic_check_24, + message = resourceReference(R.string.wallet_notification_address_copied), + ), + ) } }, + text = resourceReference(R.string.common_copy), + iconStart = TangemIconUM.Icon(Icons.ic_copy_24), + size = TangemButton.Size.X12, + variant = TangemButton.Variant.Secondary, ) - SecondaryButtonIconStart( + TangemButton( modifier = Modifier.weight(1f), - text = stringResourceSafe(id = R.string.common_share), - iconResId = R.drawable.ic_share_24, onClick = { hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) onShareClick() }, + text = resourceReference(R.string.common_share), + iconStart = TangemIconUM.Icon(Icons.ic_share_android_24), + size = TangemButton.Size.X12, + variant = TangemButton.Variant.Secondary, ) } } @@ -212,20 +197,7 @@ private fun rememberQrPainter(content: String, size: Dp = 248.dp, padding: Dp = private fun Preview_TokenReceiveQrCodeContent( @PreviewParameter(TokenReceiveQrCodeContentPreviewProvider::class) qrCodeUM: QrCodeUM, ) { - TangemThemePreview { + TangemThemePreviewRedesign { TokenReceiveQrCodeContent(qrCodeUM = qrCodeUM) } -} - -private class TokenReceiveQrCodeContentPreviewProvider : PreviewParameterProvider { - private val config = QrCodeUM( - network = "Ethereum", - addressName = stringReference("Etherium"), - addressValue = "0xe5178c7d4d0e861ed2e9414e045b501226b0de8d", - onCopyClick = {}, - onShareClick = {}, - ) - - override val values: Sequence - get() = sequenceOf(config) } \ No newline at end of file diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveQrCodeContentLegacy.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveQrCodeContentLegacy.kt new file mode 100644 index 0000000000..2e167ea81e --- /dev/null +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveQrCodeContentLegacy.kt @@ -0,0 +1,231 @@ +package com.tangem.features.tokenreceive.ui + +import android.content.res.Configuration +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.graphics.painter.BitmapPainter +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalHapticFeedback +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.tangem.core.res.getStringSafe +import com.tangem.core.ui.components.* +import com.tangem.core.ui.extensions.* +import com.tangem.core.ui.message.SnackbarMessage +import com.tangem.core.ui.res.LocalRedesignEnabled +import com.tangem.core.ui.res.LocalTopSnackbarHostState +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.test.TokenReceiveQrCodeBottomSheetTestTags +import com.tangem.features.tokenreceive.impl.R +import com.tangem.features.tokenreceive.ui.state.QrCodeUM +import kotlinx.coroutines.launch + +@Composable +internal fun TokenReceiveQrCodeContentLegacy(qrCodeUM: QrCodeUM) { + val snackbarHostState = remember(::SnackbarHostState) + val qrCodePainter = rememberQrPainter(content = qrCodeUM.addressValue) + + ContainerWithSnackbarHost(snackbarHostState = snackbarHostState) { + Column( + modifier = Modifier + .fillMaxWidth() + .background(color = TangemTheme.colors.background.tertiary) + .padding(bottom = 16.dp) + .padding(horizontal = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + SpacerH8() + + QrCodePage( + addressFullName = qrCodeUM.addressName, + addressValue = qrCodeUM.addressValue, + network = qrCodeUM.network, + qrCodePainter = qrCodePainter, + ) + SpacerH24() + + Buttons( + onShareClick = { qrCodeUM.onShareClick(qrCodeUM.addressValue) }, + onCopyClick = qrCodeUM.onCopyClick, + snackbarHostState = snackbarHostState, + ) + } + } +} + +@Composable +private fun QrCodePage(addressFullName: TextReference, addressValue: String, network: String, qrCodePainter: Painter) { + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Column( + modifier = Modifier.padding(horizontal = TangemTheme.dimens.size36), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text( + text = stringResourceSafe( + R.string.receive_bottom_sheet_warning_message_compact, + addressFullName.resolveReference(), + network, + ), + color = TangemTheme.colors.text.primary1, + textAlign = TextAlign.Center, + style = TangemTheme.typography.h3, + modifier = Modifier.testTag(TokenReceiveQrCodeBottomSheetTestTags.TITLE), + ) + + SpacerH(20.dp) + + Box( + modifier = Modifier + .border( + width = 8.dp, + color = TangemTheme.colors.icon.constant, + shape = RoundedCornerShape(8.dp), + ) + .padding(8.dp) + .testTag(TokenReceiveQrCodeBottomSheetTestTags.QR_CODE), + + ) { + Image( + painter = qrCodePainter, + contentDescription = null, + contentScale = ContentScale.Fit, + modifier = Modifier.sizeIn(minWidth = 148.dp), + ) + } + + SpacerH24() + } + Text( + text = stringResourceSafe(R.string.wc_common_address), + color = TangemTheme.colors.text.tertiary, + textAlign = TextAlign.Center, + style = TangemTheme.typography.subtitle2, + ) + + SpacerH2() + + Text( + text = addressValue, + color = TangemTheme.colors.text.primary1, + textAlign = TextAlign.Center, + style = TangemTheme.typography.subtitle1, + modifier = Modifier.testTag(TokenReceiveQrCodeBottomSheetTestTags.ADDRESS), + ) + } +} + +@Composable +private fun Buttons( + snackbarHostState: SnackbarHostState, + onShareClick: () -> Unit, + onCopyClick: () -> Unit, + modifier: Modifier = Modifier, +) { + val hapticFeedback = LocalHapticFeedback.current + val coroutineScope = rememberCoroutineScope() + val context = LocalContext.current + val resources = context.resources + + val isRedesignEnabled = LocalRedesignEnabled.current + val tangemTopSnackbarHostState = LocalTopSnackbarHostState.current + + Row( + modifier = modifier, + horizontalArrangement = Arrangement.spacedBy(16.dp), + ) { + SecondaryButtonIconStart( + modifier = Modifier.weight(1f), + text = stringResourceSafe(id = R.string.common_copy), + iconResId = R.drawable.ic_copy_24, + onClick = { + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + onCopyClick() + coroutineScope.launch { + if (isRedesignEnabled) { + tangemTopSnackbarHostState.showSnackbar( + SnackbarMessage( + startIconId = R.drawable.ic_check_24, + message = resourceReference(R.string.wallet_notification_address_copied), + ), + ) + } else { + snackbarHostState.showSnackbar( + message = resources.getStringSafe(R.string.wallet_notification_address_copied), + ) + } + } + }, + ) + + SecondaryButtonIconStart( + modifier = Modifier.weight(1f), + text = stringResourceSafe(id = R.string.common_share), + iconResId = R.drawable.ic_share_24, + onClick = { + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + onShareClick() + }, + ) + } +} + +@Composable +private fun rememberQrPainter(content: String, size: Dp = 248.dp, padding: Dp = 0.dp): BitmapPainter { + val density = LocalDensity.current + return remember(content) { + BitmapPainter( + content.toQrCode( + sizePx = with(density) { size.roundToPx() }, + paddingPx = with(density) { padding.roundToPx() }, + ).asImageBitmap(), + ) + } +} + +@Composable +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +private fun Preview_TokenReceiveQrCodeContent( + @PreviewParameter(TokenReceiveQrCodeContentPreviewProvider::class) qrCodeUM: QrCodeUM, +) { + TangemThemePreview { + TokenReceiveQrCodeContentLegacy(qrCodeUM = qrCodeUM) + } +} + +internal class TokenReceiveQrCodeContentPreviewProvider : PreviewParameterProvider { + private val config = QrCodeUM( + network = "Ethereum", + addressName = stringReference("Etherium"), + addressValue = "0xe5178c7d4d0e861ed2e9414e045b501226b0de8d", + onCopyClick = {}, + onShareClick = {}, + ) + + override val values: Sequence + get() = sequenceOf(config) +} \ No newline at end of file diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveWarningContent.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveWarningContent.kt index 6f4c57d692..b9223214da 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveWarningContent.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveWarningContent.kt @@ -1,8 +1,10 @@ package com.tangem.features.tokenreceive.ui import android.content.res.Configuration -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +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.Alignment @@ -13,19 +15,14 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp -import com.tangem.core.ui.components.SecondaryButton import com.tangem.core.ui.components.SpacerH -import com.tangem.core.ui.components.SpacerH12 -import com.tangem.core.ui.components.SpacerH24 -import com.tangem.core.ui.components.SpacerW6 import com.tangem.core.ui.components.currency.icon.CurrencyIcon -import com.tangem.core.ui.components.currency.icon.CurrencyIconState +import com.tangem.core.ui.ds2.button.TangemButton +import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringResourceSafe -import com.tangem.core.ui.res.TangemColorPalette import com.tangem.core.ui.res.TangemTheme -import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.res.TangemThemePreviewRedesign import com.tangem.core.ui.test.TokenReceiveWarningBottomSheetTestTags import com.tangem.features.tokenreceive.impl.R import com.tangem.features.tokenreceive.ui.state.WarningUM @@ -37,51 +34,51 @@ internal fun TokenReceiveWarningContent(warningUM: WarningUM) { Column( modifier = Modifier .fillMaxWidth() - .background(color = TangemTheme.colors.background.tertiary) .padding( + top = 28.dp, start = 16.dp, end = 16.dp, bottom = 16.dp, - ).testTag(TokenReceiveWarningBottomSheetTestTags.BOTTOM_SHEET), + ) + .testTag(TokenReceiveWarningBottomSheetTestTags.BOTTOM_SHEET), horizontalAlignment = Alignment.CenterHorizontally, ) { CurrencyIcon( modifier = Modifier - .padding(8.dp) - .size(size = 64.dp), + .size(size = 76.dp), state = warningUM.iconState, shouldDisplayNetwork = true, - iconSize = 56.dp, + networkBadgeSize = 24.dp, + iconSize = 72.dp, ) - - SpacerH24() - + SpacerH(32.dp) WarningBlock(networkName = warningUM.network) - - SpacerH12() - + SpacerH(8.dp) Text( + modifier = Modifier.padding(horizontal = 16.dp), textAlign = TextAlign.Center, text = stringResourceSafe(R.string.domain_receive_assets_onboarding_description), - style = TangemTheme.typography.body2, - color = TangemTheme.colors.text.secondary, + style = TangemTheme.typography3.subheading.medium, + color = TangemTheme.colors3.text.secondary, ) - SpacerH(48.dp) - - SecondaryButton( - modifier = Modifier.fillMaxWidth(), - text = stringResourceSafe(R.string.common_got_it), + TangemButton( + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp), + variant = TangemButton.Variant.Secondary, + text = resourceReference(R.string.common_got_it), onClick = { hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) warningUM.onWarningAcknowledged() }, + size = TangemButton.Size.X12, ) } } @Composable -fun WarningBlock(networkName: String, modifier: Modifier = Modifier) { +private fun WarningBlock(networkName: String, modifier: Modifier = Modifier) { Column( modifier = modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally, @@ -89,17 +86,15 @@ fun WarningBlock(networkName: String, modifier: Modifier = Modifier) { Text( textAlign = TextAlign.Center, text = stringResourceSafe(R.string.domain_receive_assets_onboarding_title), - style = TangemTheme.typography.h3, - color = TangemTheme.colors.text.primary1, + style = TangemTheme.typography3.heading.small, + color = TangemTheme.colors3.text.primary, ) - SpacerW6() - Text( textAlign = TextAlign.Center, text = stringResourceSafe(R.string.domain_receive_assets_onboarding_network_name, networkName), - style = TangemTheme.typography.h3, - color = TangemTheme.colors.text.primary1, + style = TangemTheme.typography3.heading.small, + color = TangemTheme.colors3.text.primary, ) } } @@ -110,27 +105,7 @@ fun WarningBlock(networkName: String, modifier: Modifier = Modifier) { private fun Preview_TokenReceiveWarningContent( @PreviewParameter(TokenReceiveWarningContentProvider::class) warningUM: WarningUM, ) { - TangemThemePreview { + TangemThemePreviewRedesign { TokenReceiveWarningContent(warningUM = warningUM) } -} - -private class TokenReceiveWarningContentProvider : PreviewParameterProvider { - val iconState = CurrencyIconState.TokenIcon( - url = null, - topBadgeIconResId = null, - fallbackTint = TangemColorPalette.Black, - fallbackBackground = TangemColorPalette.Meadow, - isGrayscale = false, - shouldShowCustomBadge = false, - ) - - override val values: Sequence - get() = sequenceOf( - WarningUM( - iconState = iconState, - onWarningAcknowledged = {}, - network = "Etherium", - ), - ) } \ No newline at end of file diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveWarningContentLegacy.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveWarningContentLegacy.kt new file mode 100644 index 0000000000..cbf960b3c5 --- /dev/null +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveWarningContentLegacy.kt @@ -0,0 +1,139 @@ +package com.tangem.features.tokenreceive.ui + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +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.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.platform.LocalHapticFeedback +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.compose.ui.unit.dp +import com.tangem.core.ui.components.SecondaryButton +import com.tangem.core.ui.components.SpacerH +import com.tangem.core.ui.components.SpacerH12 +import com.tangem.core.ui.components.SpacerH24 +import com.tangem.core.ui.components.currency.icon.CurrencyIcon +import com.tangem.core.ui.components.currency.icon.CurrencyIconState +import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.res.TangemColorPalette +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.test.TokenReceiveWarningBottomSheetTestTags +import com.tangem.features.tokenreceive.impl.R +import com.tangem.features.tokenreceive.ui.state.WarningUM + +@Composable +internal fun TokenReceiveWarningContentLegacy(warningUM: WarningUM) { + val hapticFeedback = LocalHapticFeedback.current + + Column( + modifier = Modifier + .fillMaxWidth() + .background(color = TangemTheme.colors.background.tertiary) + .padding( + start = 16.dp, + end = 16.dp, + bottom = 16.dp, + ) + .testTag(TokenReceiveWarningBottomSheetTestTags.BOTTOM_SHEET), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + CurrencyIcon( + modifier = Modifier + .padding(8.dp) + .size(size = 64.dp), + state = warningUM.iconState, + shouldDisplayNetwork = true, + iconSize = 56.dp, + ) + + SpacerH24() + + WarningBlock(networkName = warningUM.network) + + SpacerH12() + + Text( + textAlign = TextAlign.Center, + text = stringResourceSafe(R.string.domain_receive_assets_onboarding_description), + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.secondary, + ) + + SpacerH(48.dp) + + SecondaryButton( + modifier = Modifier.fillMaxWidth(), + text = stringResourceSafe(R.string.common_got_it), + onClick = { + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + warningUM.onWarningAcknowledged() + }, + ) + } +} + +@Composable +private fun WarningBlock(networkName: String, modifier: Modifier = Modifier) { + Column( + modifier = modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text( + textAlign = TextAlign.Center, + text = stringResourceSafe(R.string.domain_receive_assets_onboarding_title), + style = TangemTheme.typography.h3, + color = TangemTheme.colors.text.primary1, + ) + + SpacerH(6.dp) + + Text( + textAlign = TextAlign.Center, + text = stringResourceSafe(R.string.domain_receive_assets_onboarding_network_name, networkName), + style = TangemTheme.typography.h3, + color = TangemTheme.colors.text.primary1, + ) + } +} + +@Composable +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +private fun Preview_TokenReceiveWarningContent( + @PreviewParameter(TokenReceiveWarningContentProvider::class) warningUM: WarningUM, +) { + TangemThemePreview { + TokenReceiveWarningContentLegacy(warningUM = warningUM) + } +} + +internal class TokenReceiveWarningContentProvider : PreviewParameterProvider { + val iconState = CurrencyIconState.TokenIcon( + url = null, + topBadgeIconResId = null, + fallbackTint = TangemColorPalette.Black, + fallbackBackground = TangemColorPalette.Meadow, + isGrayscale = false, + shouldShowCustomBadge = false, + ) + + override val values: Sequence + get() = sequenceOf( + WarningUM( + iconState = iconState, + onWarningAcknowledged = {}, + network = "Etherium", + ), + ) +} \ No newline at end of file