From 6386cb1ea4e93a299886bc56de9727f1aa7064fa Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 24 Jun 2023 14:22:13 +0800 Subject: [PATCH] Updated on 2026-08-14 --- .../core/ui/components/Notifications.kt | 202 ------------------ .../components/notifications/Notification.kt | 171 +++++++++++++++ .../notifications/NotificationState.kt | 54 +++++ .../com/tangem/core/ui/res/TangemDimens.kt | 2 + .../presentation/common/WalletPreviewData.kt | 17 +- .../wallet/state/WalletNotification.kt | 65 ++++++ .../wallet/state/WalletStateHolder.kt | 21 +- 7 files changed, 319 insertions(+), 213 deletions(-) delete mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/Notifications.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/notifications/NotificationState.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletNotification.kt diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Notifications.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Notifications.kt deleted file mode 100644 index e70f571b18..0000000000 --- a/core/ui/src/main/java/com/tangem/core/ui/components/Notifications.kt +++ /dev/null @@ -1,202 +0,0 @@ -package com.tangem.core.ui.components - -import androidx.annotation.DrawableRes -import androidx.compose.foundation.Image -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.* -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.tooling.preview.Preview -import com.tangem.core.ui.R -import com.tangem.core.ui.res.TangemTheme - -/** - * Closable notification with custom icon - * Child of parent component - * @see Figma component - * - * Use to show banner with custom icon and possibility to close - * i.e. Feedback notification - * - * @param title notification title - * @param icon drawable res on icon - * @param iconColor icon color - * @param onClick callback on click - * @param onCloseClick callback on close icon click - */ -@Composable -fun ClosableNotification( - title: String, - @DrawableRes icon: Int, - iconColor: Color, - onClick: (() -> Unit), - onCloseClick: (() -> Unit), -) { - NotificationCardTemplate(onClick) { - Icon( - modifier = Modifier - .size(TangemTheme.dimens.size20) - .align(Alignment.CenterStart), - painter = painterResource(id = icon), - tint = iconColor, - contentDescription = null, - ) - Text( - modifier = Modifier - .padding(horizontal = TangemTheme.dimens.spacing28) - .align(Alignment.CenterStart), - text = title, - color = TangemTheme.colors.text.primary1, - style = TangemTheme.typography.subtitle2, - ) - Icon( - modifier = Modifier - .size(TangemTheme.dimens.size20) - .align(Alignment.CenterEnd) - .clickable(onClick = onCloseClick), - painter = painterResource(id = R.drawable.ic_close_24), - contentDescription = null, - tint = TangemTheme.colors.icon.informative, - ) - } -} - -/** - * Notification component from Design system - * There are few states for this component, but only one parent, see link below - * - * Use this for Notification with title, subtitle, clickable or not - * - * @param title notification title - * @param subtitle notification subtitle - * @param onClick click on notification, if its null then no chevron icon - * - * @see Figma component - */ -@Composable -fun WarningNotification(title: String, subtitle: String?, onClick: (() -> Unit)?) { - NotificationCardTemplate(onClick) { - Image( - modifier = Modifier - .size(TangemTheme.dimens.size20) - .align(Alignment.CenterStart), - painter = painterResource(id = R.drawable.img_attention_20), - contentDescription = null, - ) - Column( - modifier = Modifier - .padding(horizontal = TangemTheme.dimens.spacing28) - .align(Alignment.CenterStart), - ) { - Text( - text = title, - color = TangemTheme.colors.text.primary1, - style = TangemTheme.typography.subtitle2, - ) - if (!subtitle.isNullOrEmpty()) { - SpacerH2() - Text( - text = subtitle, - color = TangemTheme.colors.text.tertiary, - style = TangemTheme.typography.caption, - ) - } - } - if (onClick != null) { - Icon( - modifier = Modifier - .size(TangemTheme.dimens.size20) - .align(Alignment.CenterEnd), - painter = painterResource(id = R.drawable.ic_chevron_right_24), - contentDescription = null, - tint = TangemTheme.colors.icon.informative, - ) - } - } -} - -@OptIn(ExperimentalMaterialApi::class) -@Composable -private fun NotificationCardTemplate(onClick: (() -> Unit)? = null, content: @Composable BoxScope.() -> Unit) { - Surface( - color = TangemTheme.colors.button.secondary, - shape = RoundedCornerShape(TangemTheme.dimens.radius18), - onClick = onClick ?: {}, - enabled = onClick != null, - ) { - Box( - Modifier - .padding( - horizontal = TangemTheme.dimens.spacing12, - vertical = TangemTheme.dimens.spacing8, - ) - .wrapContentSize(), - ) { - content() - } - } -} - -// region Preview - -@Composable -private fun WarningNotificationPreview() { - Column(modifier = Modifier.fillMaxWidth()) { - WarningNotification( - title = "Your wallet hasn’t been backed up", - subtitle = "Lorem ipsum dolor sit amet, consectetur " + - "adipiscing elit, sed do eiusmod tempor incididunt ut labore et...", - onClick = {}, - ) - SpacerH32() - WarningNotification( - title = "Your wallet hasn’t been backed up", - subtitle = null, - onClick = {}, - ) - SpacerH32() - WarningNotification( - title = "Your wallet hasn’t been backed up", - subtitle = "Lorem ipsum dolor sit amet, consectetur " + - "adipiscing elit, sed do eiusmod tempor incididunt ut labore et...", - onClick = null, - ) - SpacerH32() - WarningNotification( - title = "Your wallet hasn’t been backed up", - subtitle = null, - onClick = null, - ) - SpacerH32() - ClosableNotification( - title = "Like tangem app?", - icon = R.drawable.ic_star_24, - iconColor = TangemTheme.colors.icon.attention, - onClick = {}, - onCloseClick = {}, - ) - } -} - -@Preview(showBackground = true) -@Composable -private fun Preview_WarningNotification_InLightTheme() { - TangemTheme(isDark = false) { - WarningNotificationPreview() - } -} - -@Preview(showBackground = true) -@Composable -private fun Preview_WarningNotification_InDarkTheme() { - TangemTheme(isDark = true) { - WarningNotificationPreview() - } -} - -// endregion Preview \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt new file mode 100644 index 0000000000..db338c9721 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt @@ -0,0 +1,171 @@ +package com.tangem.core.ui.components.notifications + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider +import com.tangem.core.ui.R +import com.tangem.core.ui.components.SpacerH2 +import com.tangem.core.ui.res.TangemColorPalette +import com.tangem.core.ui.res.TangemTheme + +/** + * Notification component from Design system. + * Use this for Notification with title, subtitle, clickable or not. + * + * @param state component state + * @param modifier modifier + * + * @see Figma component + */ +@Composable +fun Notification(state: NotificationState, modifier: Modifier = Modifier) { + Surface( + onClick = if (state is NotificationState.Action) { + state.onClick + } else { + {} + }, + modifier = modifier, + enabled = when (state) { + is NotificationState.Simple -> false + is NotificationState.Action -> true + }, + shape = RoundedCornerShape(TangemTheme.dimens.radius18), + color = TangemTheme.colors.button.secondary, + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = TangemTheme.dimens.spacing12, vertical = TangemTheme.dimens.spacing8), + ) { + NotificationIcon( + iconResId = state.iconResId, + iconTint = state.tint, + modifier = Modifier + .size(size = TangemTheme.dimens.size20) + .align(alignment = Alignment.CenterStart), + ) + + NotificationInfoBlock( + title = state.title, + subtitle = state.subtitle, + modifier = Modifier.align(alignment = Alignment.CenterStart), + ) + + if (state is NotificationState.Action) { + Icon( + modifier = Modifier + .size(size = TangemTheme.dimens.size20) + .align(alignment = Alignment.CenterEnd), + painter = painterResource(id = R.drawable.ic_chevron_right_24), + contentDescription = null, + tint = TangemTheme.colors.icon.informative, + ) + } + } + } +} + +@Composable +private fun NotificationIcon(@DrawableRes iconResId: Int, iconTint: Color?, modifier: Modifier = Modifier) { + if (iconTint != null) { + Icon( + painter = painterResource(id = iconResId), + contentDescription = null, + modifier = modifier, + tint = iconTint, + ) + } else { + Image( + painter = painterResource(id = iconResId), + contentDescription = null, + modifier = modifier, + ) + } +} + +@Composable +private fun NotificationInfoBlock(title: String, subtitle: String?, modifier: Modifier = Modifier) { + Column(modifier = modifier.padding(horizontal = TangemTheme.dimens.spacing30)) { + Text( + text = title, + color = TangemTheme.colors.text.primary1, + style = TangemTheme.typography.body2, + ) + + if (!subtitle.isNullOrEmpty()) { + SpacerH2() + Text( + text = subtitle, + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.caption, + ) + } + } +} + +@Preview +@Composable +private fun Preview_WarningNotification_Light( + @PreviewParameter(NotificationStateProvider::class) + state: NotificationState, +) { + TangemTheme(isDark = false) { + Notification(state) + } +} + +@Preview +@Composable +private fun Preview_WarningNotification_Dark( + @PreviewParameter(NotificationStateProvider::class) + state: NotificationState, +) { + TangemTheme(isDark = true) { + Notification(state) + } +} + +private class NotificationStateProvider : CollectionPreviewParameterProvider( + collection = listOf( + NotificationState.Simple( + title = "Your wallet hasn’t been backed up", + subtitle = "Lorem ipsum dolor sit amet, consectetur " + + "adipiscing elit, sed do eiusmod tempor incididunt ut labore et...", + iconResId = R.drawable.img_attention_20, + ), + NotificationState.Simple( + title = "Your wallet hasn’t been backed up", + subtitle = null, + iconResId = R.drawable.ic_alert_circle_24, + tint = TangemColorPalette.Amaranth, + ), + NotificationState.Action( + title = "Your wallet hasn’t been backed up", + subtitle = "Lorem ipsum dolor sit amet, consectetur " + + "adipiscing elit, sed do eiusmod tempor incididunt ut labore et...", + iconResId = R.drawable.img_attention_20, + onClick = {}, + ), + NotificationState.Action( + title = "Your wallet hasn’t been backed up", + subtitle = null, + iconResId = R.drawable.ic_alert_circle_24, + tint = TangemColorPalette.Amaranth, + onClick = {}, + ), + ), +) \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/NotificationState.kt b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/NotificationState.kt new file mode 100644 index 0000000000..065fa1e125 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/NotificationState.kt @@ -0,0 +1,54 @@ +package com.tangem.core.ui.components.notifications + +import androidx.annotation.DrawableRes +import androidx.compose.ui.graphics.Color + +/** + * Notification component state + * + * @property title title + * @property subtitle subtitle + * @property iconResId icon resource id + * @property tint icon tint + * +[REDACTED_AUTHOR] + */ +sealed class NotificationState( + open val title: String, + open val subtitle: String? = null, + @DrawableRes open val iconResId: Int, + open val tint: Color? = null, +) { + + /** + * Simple notification state. Non clickable. + * + * @property title title + * @property subtitle subtitle + * @property iconResId icon resource id + * @property tint icon tint + */ + data class Simple( + override val title: String, + override val subtitle: String? = null, + @DrawableRes override val iconResId: Int, + override val tint: Color? = null, + ) : NotificationState(title, subtitle, iconResId, tint) + + /** + * Clickable notification state + * + * @property title title + * @property subtitle subtitle + * @property iconResId icon resource id + * @property tint icon tint + * @param onClick lambda be invoked when notification component is clicked + */ + data class Action( + override val title: String, + override val subtitle: String? = null, + @DrawableRes override val iconResId: Int, + override val tint: Color? = null, + val onClick: () -> Unit, + ) : NotificationState(title, subtitle, iconResId, tint) +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt index 421c82c9e1..8c85ac6339 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt @@ -76,6 +76,7 @@ data class TangemDimens internal constructor( val size200: Dp = 200.dp, // endregion Size // region Spacing + val spacing0: Dp = 0.dp, val spacing0_5: Dp = 0.5.dp, val spacing2: Dp = 2.dp, val spacing4: Dp = 4.dp, @@ -91,6 +92,7 @@ data class TangemDimens internal constructor( val spacing24: Dp = 24.dp, val spacing26: Dp = 26.dp, val spacing28: Dp = 28.dp, + val spacing30: Dp = 30.dp, val spacing32: Dp = 32.dp, val spacing34: Dp = 34.dp, val spacing36: Dp = 34.dp, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index 410256fd11..2bfde4eaaa 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -7,10 +7,7 @@ import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TokenO import com.tangem.feature.wallet.presentation.organizetokens.DraggableItem import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensListState import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensStateHolder -import com.tangem.feature.wallet.presentation.wallet.state.WalletCardState -import com.tangem.feature.wallet.presentation.wallet.state.WalletContentItemState -import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder -import com.tangem.feature.wallet.presentation.wallet.state.WalletTopBarConfig +import com.tangem.feature.wallet.presentation.wallet.state.* import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toPersistentList import java.util.UUID @@ -220,6 +217,12 @@ internal object WalletPreviewData { ), ), ), + notifications = persistentListOf( + WalletNotification.UnreachableNetworks, + WalletNotification.LikeTangemApp(onClick = {}), + WalletNotification.NeedToBackup(onClick = {}), + WalletNotification.ScanCard(onClick = {}), + ), onOrganizeTokensClick = {}, ) @@ -252,5 +255,11 @@ internal object WalletPreviewData { ), ), ), + notifications = persistentListOf( + WalletNotification.UnreachableNetworks, + WalletNotification.LikeTangemApp(onClick = {}), + WalletNotification.NeedToBackup(onClick = {}), + WalletNotification.ScanCard(onClick = {}), + ), ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletNotification.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletNotification.kt new file mode 100644 index 0000000000..39e760dd4f --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletNotification.kt @@ -0,0 +1,65 @@ +package com.tangem.feature.wallet.presentation.wallet.state + +import com.tangem.core.ui.components.notifications.NotificationState +import com.tangem.core.ui.res.TangemColorPalette +import com.tangem.feature.wallet.impl.R + +/** + * Wallet notification component state + * + * @property state state + * +[REDACTED_AUTHOR] + */ +sealed class WalletNotification(open val state: NotificationState) { + + /** + * "Backup the card" notification + * + * @property onClick lambda be invoked when notification is clicked + */ + data class NeedToBackup(val onClick: () -> Unit) : WalletNotification( + state = NotificationState.Action( + title = "Backup your card", + iconResId = R.drawable.ic_alert_circle_24, + onClick = onClick, + tint = TangemColorPalette.Amaranth, + ), + ) + + /** "Unreachable networks" notification */ + object UnreachableNetworks : WalletNotification( + state = NotificationState.Simple( + title = "Some networks are unreachable", + iconResId = R.drawable.img_attention_20, + tint = null, + ), + ) + + /** + * "Like Tangem App" notification + * + * @property onClick lambda be invoked when notification is clicked + */ + data class LikeTangemApp(val onClick: () -> Unit) : WalletNotification( + state = NotificationState.Action( + title = "Like Tangem App?", + iconResId = R.drawable.ic_star_24, + onClick = onClick, + tint = TangemColorPalette.Tangerine, + ), + ) + + /** + * "Scan the card" notification + * + * @property onClick lambda be invoked when notification is clicked + */ + data class ScanCard(val onClick: () -> Unit) : WalletNotification( + state = NotificationState.Action( + title = "Scan your card to continue", + iconResId = R.drawable.ic_tangem_24, + onClick = onClick, + ), + ) +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt index bfc07128cf..a10b91ab37 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt @@ -10,6 +10,7 @@ import kotlinx.collections.immutable.ImmutableList * @property selectedWallet selected wallet * @property wallets list of wallets states * @property contentItems content items + * @property notifications notifications * [REDACTED_AUTHOR] */ @@ -19,16 +20,19 @@ internal sealed class WalletStateHolder( open val selectedWallet: WalletCardState, open val wallets: ImmutableList, open val contentItems: ImmutableList, + open val notifications: ImmutableList, ) { /** * Multi currency wallet content state * - * @property onBackClick lambda be invoked when back button is clicked - * @property topBarConfig top bar config - * @property selectedWallet selected wallet - * @property wallets list of wallets states - * @property contentItems content items + * @property onBackClick lambda be invoked when back button is clicked + * @property topBarConfig top bar config + * @property selectedWallet selected wallet + * @property wallets list of wallets states + * @property contentItems content items + * @property notifications notifications + * @property onOrganizeTokensClick lambda be invoked when organize tokens button is clicked */ data class MultiCurrencyContent( override val onBackClick: () -> Unit, @@ -36,8 +40,9 @@ internal sealed class WalletStateHolder( override val selectedWallet: WalletCardState, override val wallets: ImmutableList, override val contentItems: ImmutableList, + override val notifications: ImmutableList, val onOrganizeTokensClick: () -> Unit, - ) : WalletStateHolder(onBackClick, topBarConfig, selectedWallet, wallets, contentItems) + ) : WalletStateHolder(onBackClick, topBarConfig, selectedWallet, wallets, contentItems, notifications) /** * Single currency wallet content state @@ -47,6 +52,7 @@ internal sealed class WalletStateHolder( * @property selectedWallet selected wallet * @property wallets list of wallets states * @property contentItems content items + * @property notifications notifications */ data class SingleCurrencyContent( override val onBackClick: () -> Unit, @@ -54,5 +60,6 @@ internal sealed class WalletStateHolder( override val selectedWallet: WalletCardState, override val wallets: ImmutableList, override val contentItems: ImmutableList, - ) : WalletStateHolder(onBackClick, topBarConfig, selectedWallet, wallets, contentItems) + override val notifications: ImmutableList, + ) : WalletStateHolder(onBackClick, topBarConfig, selectedWallet, wallets, contentItems, notifications) } \ No newline at end of file