Updated on 2026-08-14
This commit is contained in:
parent
cb50b7cf07
commit
0c2d933601
4 changed files with 394 additions and 0 deletions
|
|
@ -0,0 +1,134 @@
|
|||
package com.tangem.core.ui.components.bottomsheets.message
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
@Immutable
|
||||
data class MessageBottomSheetUMV2(
|
||||
var elements: ImmutableList<Element> = persistentListOf(),
|
||||
var onDismissRequest: () -> Unit = {},
|
||||
) : TangemBottomSheetConfigContent {
|
||||
|
||||
@Immutable
|
||||
inner class CloseScope {
|
||||
fun closeBs() {
|
||||
onDismissRequest()
|
||||
}
|
||||
}
|
||||
|
||||
val closeScope = CloseScope()
|
||||
|
||||
@Immutable
|
||||
sealed interface Element
|
||||
|
||||
@Immutable
|
||||
data class Icon(
|
||||
@DrawableRes internal var res: Int,
|
||||
var type: Type = Type.Unspecified,
|
||||
var backgroundType: BackgroundType = BackgroundType.Unspecified,
|
||||
) : Element {
|
||||
enum class Type {
|
||||
Unspecified, Accent, Informative, Attention, Warning,
|
||||
}
|
||||
|
||||
enum class BackgroundType {
|
||||
Unspecified, SameAsTint
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class Chip(
|
||||
internal var text: TextReference,
|
||||
var type: Type = Type.Unspecified,
|
||||
) : Element {
|
||||
enum class Type {
|
||||
Unspecified, Warning
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class InfoBlock(
|
||||
internal var icon: Icon? = null,
|
||||
internal var chip: Chip? = null,
|
||||
var title: TextReference? = null,
|
||||
var body: TextReference? = null,
|
||||
) : Element
|
||||
|
||||
@Immutable
|
||||
data class Button(
|
||||
internal var isPrimary: Boolean = false,
|
||||
var text: TextReference? = null,
|
||||
@DrawableRes internal var iconInternal: Int? = null,
|
||||
internal var iconOrder: IconOrder = IconOrder.Start,
|
||||
var onClick: (CloseScope.() -> Unit)? = null,
|
||||
) : Element {
|
||||
|
||||
var icon: Int? = iconInternal
|
||||
set(value) {
|
||||
iconOrder = if (text == null) {
|
||||
IconOrder.Start
|
||||
} else {
|
||||
IconOrder.End
|
||||
}
|
||||
field = value
|
||||
}
|
||||
|
||||
enum class IconOrder {
|
||||
Start, End
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Target(AnnotationTarget.TYPE)
|
||||
@DslMarker
|
||||
annotation class MessageBottomSheetV2Dsl
|
||||
|
||||
// region: DSL
|
||||
|
||||
internal fun messageBottomSheetUM(init: @MessageBottomSheetV2Dsl MessageBottomSheetUMV2.() -> Unit) =
|
||||
MessageBottomSheetUMV2().apply(init)
|
||||
|
||||
fun MessageBottomSheetUMV2.onDismiss(block: () -> Unit) = MessageBottomSheetUMV2().apply { onDismissRequest = block }
|
||||
|
||||
fun MessageBottomSheetUMV2.infoBlock(init: @MessageBottomSheetV2Dsl MessageBottomSheetUMV2.InfoBlock.() -> Unit) =
|
||||
apply {
|
||||
val element = MessageBottomSheetUMV2.InfoBlock().apply(init)
|
||||
elements = (elements + element).toPersistentList()
|
||||
}
|
||||
|
||||
fun MessageBottomSheetUMV2.InfoBlock.icon(@DrawableRes res: Int, init: MessageBottomSheetUMV2.Icon.() -> Unit = {}) =
|
||||
apply {
|
||||
icon = MessageBottomSheetUMV2.Icon(res).apply(init)
|
||||
}
|
||||
|
||||
fun MessageBottomSheetUMV2.InfoBlock.chip(text: TextReference, init: MessageBottomSheetUMV2.Chip.() -> Unit = {}) =
|
||||
apply {
|
||||
chip = MessageBottomSheetUMV2.Chip(text).apply(init)
|
||||
}
|
||||
|
||||
internal fun MessageBottomSheetUMV2.button(init: @MessageBottomSheetV2Dsl MessageBottomSheetUMV2.Button.() -> Unit) =
|
||||
apply {
|
||||
val element = MessageBottomSheetUMV2.Button().apply(init)
|
||||
elements = (elements + element).toPersistentList()
|
||||
}
|
||||
|
||||
fun MessageBottomSheetUMV2.primaryButton(init: @MessageBottomSheetV2Dsl MessageBottomSheetUMV2.Button.() -> Unit) =
|
||||
apply {
|
||||
button { isPrimary = true; apply(init) }
|
||||
}
|
||||
|
||||
fun MessageBottomSheetUMV2.secondaryButton(init: @MessageBottomSheetV2Dsl MessageBottomSheetUMV2.Button.() -> Unit) =
|
||||
apply {
|
||||
button { isPrimary = false; apply(init) }
|
||||
}
|
||||
|
||||
fun MessageBottomSheetUMV2.Button.onClick(block: MessageBottomSheetUMV2.CloseScope.() -> Unit) = apply {
|
||||
onClick = block
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
package com.tangem.core.ui.components.bottomsheets.message
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetUMV2.Button.IconOrder
|
||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButton
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
@Composable
|
||||
fun MessageBottomSheetV2(state: MessageBottomSheetUMV2, onDismissRequest: () -> Unit) {
|
||||
val config = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
content = state,
|
||||
onDismissRequest = onDismissRequest,
|
||||
)
|
||||
|
||||
TangemModalBottomSheet(
|
||||
config,
|
||||
title = {
|
||||
TangemModalBottomSheetTitle(
|
||||
endIconRes = R.drawable.ic_close_24,
|
||||
onEndClick = state.onDismissRequest,
|
||||
)
|
||||
},
|
||||
) { content: MessageBottomSheetUMV2 ->
|
||||
MessageBottomSheetV2Content(content.copy(onDismissRequest = onDismissRequest))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MessageBottomSheetV2Content(state: MessageBottomSheetUMV2, modifier: Modifier = Modifier) {
|
||||
Column(modifier = modifier) {
|
||||
state.elements.fastForEach {
|
||||
when (it) {
|
||||
is MessageBottomSheetUMV2.InfoBlock -> {
|
||||
ContentContainer(
|
||||
modifier = Modifier
|
||||
.heightIn(min = TangemTheme.dimens.size180)
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.padding(bottom = 32.dp),
|
||||
state = it,
|
||||
)
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
ButtonsContainer(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
closeScope = state.closeScope,
|
||||
buttons = state.elements.filterIsInstance<MessageBottomSheetUMV2.Button>().toPersistentList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ContentContainer(state: MessageBottomSheetUMV2.InfoBlock, modifier: Modifier = Modifier) {
|
||||
Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
state.icon?.let {
|
||||
BottomSheetIcon(it)
|
||||
}
|
||||
state.title?.let {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = TangemTheme.dimens.spacing24),
|
||||
text = it.resolveReference(),
|
||||
style = TangemTheme.typography.h3,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
state.body?.let {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = TangemTheme.dimens.spacing8),
|
||||
text = it.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
state.chip?.let {
|
||||
BottomSheetChip(
|
||||
modifier = Modifier.padding(top = TangemTheme.dimens.spacing16),
|
||||
chip = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BottomSheetIcon(icon: MessageBottomSheetUMV2.Icon, modifier: Modifier = Modifier) {
|
||||
val tint = when (icon.type) {
|
||||
MessageBottomSheetUMV2.Icon.Type.Unspecified -> Color.Unspecified
|
||||
MessageBottomSheetUMV2.Icon.Type.Accent -> TangemTheme.colors.icon.accent
|
||||
MessageBottomSheetUMV2.Icon.Type.Informative -> TangemTheme.colors.icon.informative
|
||||
MessageBottomSheetUMV2.Icon.Type.Attention -> TangemTheme.colors.icon.attention
|
||||
MessageBottomSheetUMV2.Icon.Type.Warning -> TangemTheme.colors.icon.warning
|
||||
}
|
||||
|
||||
val backgroundColor = when (icon.backgroundType) {
|
||||
MessageBottomSheetUMV2.Icon.BackgroundType.Unspecified -> TangemTheme.colors.icon.informative
|
||||
MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint -> tint
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(TangemTheme.dimens.size56)
|
||||
.clip(CircleShape)
|
||||
.background(backgroundColor.copy(alpha = 0.1F)),
|
||||
contentAlignment = Alignment.Center,
|
||||
content = {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size32),
|
||||
painter = painterResource(icon.res),
|
||||
contentDescription = null,
|
||||
tint = tint,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BottomSheetChip(chip: MessageBottomSheetUMV2.Chip, modifier: Modifier = Modifier) {
|
||||
val color = when (chip.type) {
|
||||
MessageBottomSheetUMV2.Chip.Type.Unspecified -> TangemTheme.colors.text.primary1
|
||||
MessageBottomSheetUMV2.Chip.Type.Warning -> TangemTheme.colors.text.warning
|
||||
}
|
||||
|
||||
Text(
|
||||
modifier = modifier
|
||||
.background(
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius16),
|
||||
color = color.copy(alpha = 0.1F),
|
||||
)
|
||||
.padding(vertical = TangemTheme.dimens.spacing4, horizontal = TangemTheme.dimens.spacing12),
|
||||
text = chip.text.resolveReference(),
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = color,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun ButtonsContainer(
|
||||
buttons: ImmutableList<MessageBottomSheetUMV2.Button>,
|
||||
closeScope: MessageBottomSheetUMV2.CloseScope,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier.padding(all = TangemTheme.dimens.spacing16),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||
) {
|
||||
buttons.fastForEach { button ->
|
||||
val icon = button.icon?.let {
|
||||
when (button.iconOrder) {
|
||||
IconOrder.Start -> TangemButtonIconPosition.Start(it)
|
||||
IconOrder.End -> TangemButtonIconPosition.End(it)
|
||||
}
|
||||
} ?: TangemButtonIconPosition.None
|
||||
|
||||
TangemButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = button.text?.resolveReference() ?: "",
|
||||
icon = icon,
|
||||
onClick = { button.onClick?.invoke(closeScope) },
|
||||
colors = if (button.isPrimary) {
|
||||
TangemButtonsDefaults.primaryButtonColors
|
||||
} else {
|
||||
TangemButtonsDefaults.secondaryButtonColors
|
||||
},
|
||||
enabled = true,
|
||||
showProgress = false,
|
||||
textStyle = TangemTheme.typography.subtitle1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
TangemThemePreview {
|
||||
MessageBottomSheetV2(
|
||||
messageBottomSheetUM {
|
||||
infoBlock {
|
||||
icon(R.drawable.img_knight_shield_32) {
|
||||
type = MessageBottomSheetUMV2.Icon.Type.Attention
|
||||
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint
|
||||
}
|
||||
title = TextReference.Str("Title Title Title")
|
||||
body = TextReference.Str("Body")
|
||||
chip(text = TextReference.Str("Some chip information"))
|
||||
}
|
||||
primaryButton {
|
||||
text = TextReference.Str("Test")
|
||||
icon = R.drawable.ic_tangem_24
|
||||
}
|
||||
secondaryButton {
|
||||
icon = R.drawable.ic_tangem_24
|
||||
text = TextReference.Str("asdasd")
|
||||
onClick {
|
||||
closeBs()
|
||||
}
|
||||
}
|
||||
},
|
||||
onDismissRequest = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,9 @@ import androidx.annotation.DrawableRes
|
|||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.decompose.ui.UiMessage
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetUMV2
|
||||
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetV2Dsl
|
||||
import com.tangem.core.ui.components.bottomsheets.message.messageBottomSheetUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.SnackbarMessage.Duration
|
||||
|
|
@ -177,6 +180,13 @@ data class BottomSheetMessage(
|
|||
}
|
||||
}
|
||||
|
||||
data class BottomSheetMessageV2(
|
||||
val messageBottomSheetUMV2: MessageBottomSheetUMV2,
|
||||
) : EventMessage
|
||||
|
||||
internal fun bottomSheetMessage(init: @MessageBottomSheetV2Dsl MessageBottomSheetUMV2.() -> Unit) =
|
||||
BottomSheetMessageV2(messageBottomSheetUM(init))
|
||||
|
||||
/**
|
||||
* Represents an action button in the dialog.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import com.tangem.core.ui.components.DialogButtonUM
|
|||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetUM
|
||||
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetV2
|
||||
import com.tangem.core.ui.event.EventEffect
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.LocalEventMessageHandler
|
||||
|
|
@ -29,6 +30,7 @@ fun EventMessageEffect(
|
|||
|
||||
var dialogMessage: DialogMessage? by remember { mutableStateOf(value = null) }
|
||||
var bottomSheetMessage: BottomSheetMessage? by remember { mutableStateOf(value = null) }
|
||||
var bottomSheetMessageV2: BottomSheetMessageV2? by remember { mutableStateOf(value = null) }
|
||||
|
||||
EventEffect(event = messageEvent) { message ->
|
||||
when (message) {
|
||||
|
|
@ -41,6 +43,9 @@ fun EventMessageEffect(
|
|||
is BottomSheetMessage -> {
|
||||
bottomSheetMessage = message
|
||||
}
|
||||
is BottomSheetMessageV2 -> {
|
||||
bottomSheetMessageV2 = message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -63,6 +68,13 @@ fun EventMessageEffect(
|
|||
},
|
||||
)
|
||||
}
|
||||
|
||||
bottomSheetMessageV2?.let { message ->
|
||||
MessageBottomSheetV2(
|
||||
state = message.messageBottomSheetUMV2,
|
||||
onDismissRequest = { bottomSheetMessageV2 = null },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue