feat: add reaction dialog with grouped emojis and user list

pull/1406/head
andrekir 2024-12-03 16:36:28 -03:00
rodzic 797fc67982
commit dd3a77e2f7
5 zmienionych plików z 136 dodań i 15 usunięć

Wyświetl plik

@ -0,0 +1,52 @@
package com.geeksville.mesh.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
@Composable
fun BottomSheetDialog(
onDismiss: () -> Unit,
modifier: Modifier = Modifier,
content: @Composable ColumnScope.() -> Unit
) = Dialog(
onDismissRequest = onDismiss,
properties = DialogProperties(usePlatformDefaultWidth = false),
) {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Transparent)
.clickable(
onClick = onDismiss,
indication = null,
interactionSource = remember { MutableInteractionSource() }
)
) {
Column(
modifier = modifier
.align(Alignment.BottomCenter)
.background(
color = MaterialTheme.colors.surface.copy(alpha = 1f),
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
)
.padding(16.dp),
content = content
)
}
}

Wyświetl plik

@ -56,8 +56,21 @@ fun EmojiPicker(
},
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(fraction = 0.4f)
.background(MaterialTheme.colors.background)
)
}
}
@Composable
fun EmojiPickerDialog(
onDismiss: () -> Unit = {},
onConfirm: (String) -> Unit
) = BottomSheetDialog(
onDismiss = onDismiss,
modifier = Modifier.fillMaxHeight(fraction = .4f),
) {
EmojiPicker(
onConfirm = onConfirm,
onDismiss = onDismiss,
)
}

Wyświetl plik

@ -60,7 +60,7 @@ import com.geeksville.mesh.MeshProtos.Waypoint
import com.geeksville.mesh.R
import com.geeksville.mesh.copy
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.EmojiPicker
import com.geeksville.mesh.ui.components.EmojiPickerDialog
import com.geeksville.mesh.ui.theme.AppTheme
import com.geeksville.mesh.waypoint
@ -179,7 +179,7 @@ internal fun EditWaypointDialog(
}
},
) else {
EmojiPicker(onDismiss = { showEmojiPickerView = false }) {
EmojiPickerDialog(onDismiss = { showEmojiPickerView = false }) {
showEmojiPickerView = false
waypointInput = waypointInput.copy { icon = it.codePointAt(0) }
}

Wyświetl plik

@ -34,6 +34,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
import com.geeksville.mesh.DataPacket
import com.geeksville.mesh.database.entity.Reaction
import com.geeksville.mesh.model.Message
import com.geeksville.mesh.ui.components.SimpleAlertDialog
import kotlinx.coroutines.FlowPreview
@ -63,6 +64,12 @@ internal fun MessageList(
SimpleAlertDialog(title = title, text = text) { showStatusDialog = null }
}
var showReactionDialog by remember { mutableStateOf<List<Reaction>?>(null) }
if (showReactionDialog != null) {
val reactions = showReactionDialog ?: return
ReactionDialog(reactions) { showReactionDialog = null }
}
fun toggle(uuid: Long) = if (selectedIds.value.contains(uuid)) {
selectedIds.value -= uuid
} else {
@ -79,7 +86,7 @@ internal fun MessageList(
val fromLocal = msg.user.id == DataPacket.ID_LOCAL
val selected by remember { derivedStateOf { selectedIds.value.contains(msg.uuid) } }
ReactionRow(fromLocal, msg.emojis) {} // TODO
ReactionRow(fromLocal, msg.emojis) { showReactionDialog = msg.emojis }
MessageItem(
shortName = msg.user.shortName.takeIf { !fromLocal },
messageText = msg.text,

Wyświetl plik

@ -23,14 +23,20 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Badge
import androidx.compose.material.BadgedBox
import androidx.compose.material.ContentAlpha
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
@ -43,16 +49,18 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
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.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import com.geeksville.mesh.MeshProtos
import com.geeksville.mesh.database.entity.Reaction
import com.geeksville.mesh.ui.components.EmojiPicker
import com.geeksville.mesh.ui.components.BottomSheetDialog
import com.geeksville.mesh.ui.components.EmojiPickerDialog
import com.geeksville.mesh.ui.theme.AppTheme
@Composable
@ -159,17 +167,58 @@ fun ReactionRow(
fun reduceEmojis(emojis: List<String>): Map<String, Int> = emojis.groupingBy { it }.eachCount()
@Composable
fun EmojiPickerDialog(
onConfirm: (String) -> Unit,
onDismiss: () -> Unit = {},
fun ReactionDialog(
reactions: List<Reaction>,
onDismiss: () -> Unit = {}
) = BottomSheetDialog(
onDismiss = onDismiss,
modifier = Modifier.fillMaxHeight(fraction = .3f),
) {
Dialog(
onDismissRequest = onDismiss,
val groupedEmojis = reactions.groupBy { it.emoji }
var selectedEmoji by remember { mutableStateOf<String?>(null) }
val filteredReactions = selectedEmoji?.let { groupedEmojis[it] ?: emptyList() } ?: reactions
LazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.fillMaxWidth()
) {
EmojiPicker(
onConfirm = onConfirm,
onDismiss = onDismiss,
)
items(groupedEmojis.entries.toList()) { (emoji, reactions) ->
Text(
text = "$emoji${reactions.size}",
modifier = Modifier
.clip(CircleShape)
.background(if (selectedEmoji == emoji) Color.Gray else Color.Transparent)
.padding(8.dp)
.clickable {
selectedEmoji = if (selectedEmoji == emoji) null else emoji
},
style = MaterialTheme.typography.body2
)
}
}
Divider(Modifier.padding(vertical = 8.dp))
LazyColumn(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
items(filteredReactions) { reaction ->
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = reaction.user.longName,
style = MaterialTheme.typography.subtitle1
)
Text(
text = reaction.emoji,
style = MaterialTheme.typography.h6
)
}
}
}
}