feat: m3 list style

This commit is contained in:
infi 2026-06-17 20:16:29 +02:00
parent 8c197165b3
commit 3a07d78da3
7 changed files with 301 additions and 191 deletions

View File

@ -1,22 +1,28 @@
package chat.stoat.composables.chat
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.material3.ListItem
import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.text.style.TextOverflow
import chat.stoat.api.internals.BrushCompat
import chat.stoat.api.internals.Roles
import chat.stoat.api.internals.solidColor
import chat.stoat.core.model.schemas.Member
import chat.stoat.core.model.schemas.User
import chat.stoat.composables.generic.UserAvatar
import chat.stoat.composables.generic.presenceFromStatus
import chat.stoat.core.model.data.STOAT_FILES
import chat.stoat.internals.extensions.TransparentListItemColours
import chat.stoat.core.model.schemas.Member
import chat.stoat.core.model.schemas.User
private val NoneLambda = { }
@Composable
fun MemberListItem(
@ -25,6 +31,10 @@ fun MemberListItem(
serverId: String?,
userId: String,
modifier: Modifier = Modifier,
first: Boolean = false,
last: Boolean = false,
onClick: () -> Unit = {},
onLongClick: () -> Unit = NoneLambda,
trailingContent: @Composable (() -> Unit)? = null,
) {
val highestColourRole = serverId?.let {
@ -41,8 +51,43 @@ fun MemberListItem(
?: Brush.solidColor(LocalContentColor.current)
ListItem(
colors = TransparentListItemColours,
modifier = modifier,
colors = ListItemDefaults.colors(
containerColor = MaterialTheme.colorScheme.surfaceContainer
),
modifier = modifier
.then(
when {
first && last -> Modifier.clip(MaterialTheme.shapes.large)
first -> Modifier.clip(
MaterialTheme.shapes.extraSmall.copy(
topStart = MaterialTheme.shapes.large.topStart,
topEnd = MaterialTheme.shapes.large.topEnd
)
)
last -> Modifier.clip(
MaterialTheme.shapes.extraSmall.copy(
bottomStart = MaterialTheme.shapes.large.bottomStart,
bottomEnd = MaterialTheme.shapes.large.bottomEnd
)
)
else -> Modifier.clip(MaterialTheme.shapes.extraSmall)
}
)
.then(
if (onLongClick != NoneLambda) {
Modifier.combinedClickable(
onClick = onClick,
onLongClick = onLongClick
)
} else {
Modifier.clickable(
onClick = onClick
)
}
),
headlineContent = {
Text(
text = member?.nickname

View File

@ -7,6 +7,6 @@ import androidx.compose.ui.graphics.Color
val TransparentListItemColours: ListItemColors
@Composable
get() = ListItemDefaults.colors().copy(
get() = ListItemDefaults.colors(
containerColor = Color.Transparent
)

View File

@ -7,7 +7,6 @@ import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@ -179,8 +178,24 @@ fun FriendsScreen(topNav: NavController, useDrawer: Boolean, onDrawerClicked: ()
text = AnnotatedString.fromHtml(
stringResource(
R.string.friends_add_by_tag_sheet_description,
"<font color=\"${Color(HL_USERNAME).toArgb().let { "#%02x%02x%02x".format(it shr 16 and 0xff, it shr 8 and 0xff, it and 0xff) }}\">",
"<font color=\"${Color(HL_TAG).toArgb().let { "#%02x%02x%02x".format(it shr 16 and 0xff, it shr 8 and 0xff, it and 0xff) }}\">",
"<font color=\"${
Color(HL_USERNAME).toArgb().let {
"#%02x%02x%02x".format(
it shr 16 and 0xff,
it shr 8 and 0xff,
it and 0xff
)
}
}\">",
"<font color=\"${
Color(HL_TAG).toArgb().let {
"#%02x%02x%02x".format(
it shr 16 and 0xff,
it shr 8 and 0xff,
it and 0xff
)
}
}\">",
"</font>",
)
),
@ -601,19 +616,28 @@ fun FriendsScreen(topNav: NavController, useDrawer: Boolean, onDrawerClicked: ()
val item = FriendRequests.getIncoming().getOrNull(it)
if (item == null) return@items
val isLast = it == FriendRequests.getIncoming().size - 1
MemberListItem(
member = null,
user = item,
serverId = null,
userId = item.id ?: "",
modifier = Modifier.clickable {
first = it == 0,
last = isLast,
onClick = {
scope.launch {
item.id?.let { userId ->
ActionChannel.send(Action.OpenUserSheet(userId, null))
}
}
}
},
modifier = Modifier.padding(horizontal = 16.dp)
)
if (!isLast) {
Spacer(modifier = Modifier.height(2.dp))
}
}
stickyHeader(key = "outgoing") {
@ -627,19 +651,28 @@ fun FriendsScreen(topNav: NavController, useDrawer: Boolean, onDrawerClicked: ()
val item = FriendRequests.getOutgoing().getOrNull(it)
if (item == null) return@items
val isLast = it == FriendRequests.getOutgoing().size - 1
MemberListItem(
member = null,
user = item,
serverId = null,
userId = item.id ?: "",
modifier = Modifier.clickable {
first = it == 0,
last = isLast,
onClick = {
scope.launch {
item.id?.let { userId ->
ActionChannel.send(Action.OpenUserSheet(userId, null))
}
}
}
},
modifier = Modifier.padding(horizontal = 16.dp)
)
if (!isLast) {
Spacer(modifier = Modifier.height(2.dp))
}
}
stickyHeader(key = "online") {
@ -653,19 +686,28 @@ fun FriendsScreen(topNav: NavController, useDrawer: Boolean, onDrawerClicked: ()
val item = FriendRequests.getOnlineFriends().getOrNull(it)
if (item == null) return@items
val isLast = it == FriendRequests.getOnlineFriends().size - 1
MemberListItem(
member = null,
user = item,
serverId = null,
userId = item.id ?: "",
modifier = Modifier.clickable {
first = it == 0,
last = isLast,
onClick = {
scope.launch {
item.id?.let { userId ->
ActionChannel.send(Action.OpenUserSheet(userId, null))
}
}
}
},
modifier = Modifier.padding(horizontal = 16.dp)
)
if (!isLast) {
Spacer(modifier = Modifier.height(2.dp))
}
}
stickyHeader(key = "not_online") {
@ -679,19 +721,28 @@ fun FriendsScreen(topNav: NavController, useDrawer: Boolean, onDrawerClicked: ()
val item = FriendRequests.getFriends(true).getOrNull(it)
if (item == null) return@items
val isLast = it == FriendRequests.getFriends(true).size - 1
MemberListItem(
member = null,
user = item,
serverId = null,
userId = item.id ?: "",
modifier = Modifier.clickable {
first = it == 0,
last = isLast,
onClick = {
scope.launch {
item.id?.let { userId ->
ActionChannel.send(Action.OpenUserSheet(userId, null))
}
}
}
},
modifier = Modifier.padding(horizontal = 16.dp)
)
if (!isLast) {
Spacer(modifier = Modifier.height(2.dp))
}
}
stickyHeader(key = "blocked") {
@ -706,19 +757,28 @@ fun FriendsScreen(topNav: NavController, useDrawer: Boolean, onDrawerClicked: ()
val item = FriendRequests.getBlocked().getOrNull(it)
if (item == null) return@items
val isLast = it == FriendRequests.getBlocked().size - 1
MemberListItem(
member = null,
user = item,
serverId = null,
userId = item.id ?: "",
modifier = Modifier.clickable {
first = it == 0,
last = isLast,
onClick = {
scope.launch {
item.id?.let { userId ->
ActionChannel.send(Action.OpenUserSheet(userId, null))
}
}
}
},
modifier = Modifier.padding(horizontal = 16.dp)
)
if (!isLast) {
Spacer(modifier = Modifier.height(2.dp))
}
}
}

View File

@ -3,10 +3,11 @@ package chat.stoat.screens.create
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
@ -192,13 +193,16 @@ fun CreateGroupScreen(
val friend = StoatAPI.userCache[viewModel.friendsFilteredBySearch[index]]
?: return@items
val isMember = viewModel.groupMembers.contains(friend.id)
val isLast = index == viewModel.friendsFilteredBySearch.size - 1
MemberListItem(
member = null,
user = friend,
serverId = null,
userId = friend.id!!,
modifier = Modifier.clickable {
first = index == 0,
last = isLast,
onClick = {
if (isMember) {
viewModel.groupMembers.remove(friend.id)
} else {
@ -207,6 +211,7 @@ fun CreateGroupScreen(
}
}
},
modifier = Modifier.padding(horizontal = 16.dp),
trailingContent = {
Checkbox(
checked = isMember,
@ -215,6 +220,9 @@ fun CreateGroupScreen(
)
}
)
if (!isLast) {
Spacer(modifier = Modifier.height(2.dp))
}
}
}
}

View File

@ -4,7 +4,9 @@ import android.content.Intent
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
@ -13,6 +15,7 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LargeTopAppBar
import androidx.compose.material3.ListItem
import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
@ -21,6 +24,7 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
@ -100,18 +104,15 @@ fun SettingsScreen(
Column(
modifier = Modifier
.fillMaxSize()
.padding(vertical = 10.dp)
.padding(bottom = 10.dp)
) {
ListHeader {
Text(stringResource(R.string.settings_category_account))
}
ListItem(
headlineContent = {
Text(
text = stringResource(id = R.string.settings_account)
)
},
SettingsListItem(
first = true,
headlineContent = { Text(text = stringResource(id = R.string.settings_account)) },
leadingContent = {
SettingsIcon {
Icon(
@ -122,17 +123,11 @@ fun SettingsScreen(
},
modifier = Modifier
.testTag("settings_view_account")
.clickable {
navController.navigate("settings/account")
}
.clickable { navController.navigate("settings/account") }
)
ListItem(
headlineContent = {
Text(
text = stringResource(id = R.string.settings_profile)
)
},
Spacer(Modifier.height(2.dp))
SettingsListItem(
headlineContent = { Text(text = stringResource(id = R.string.settings_profile)) },
leadingContent = {
SettingsIcon {
Icon(
@ -143,17 +138,12 @@ fun SettingsScreen(
},
modifier = Modifier
.testTag("settings_view_profile")
.clickable {
navController.navigate("settings/profile")
}
.clickable { navController.navigate("settings/profile") }
)
ListItem(
headlineContent = {
Text(
text = stringResource(id = R.string.settings_sessions)
)
},
Spacer(Modifier.height(2.dp))
SettingsListItem(
last = true,
headlineContent = { Text(text = stringResource(id = R.string.settings_sessions)) },
leadingContent = {
SettingsIcon {
Icon(
@ -164,21 +154,16 @@ fun SettingsScreen(
},
modifier = Modifier
.testTag("settings_view_sessions")
.clickable {
navController.navigate("settings/sessions")
}
.clickable { navController.navigate("settings/sessions") }
)
ListHeader {
Text(stringResource(R.string.settings_category_general))
}
ListItem(
headlineContent = {
Text(
text = stringResource(id = R.string.settings_appearance)
)
},
SettingsListItem(
first = true,
headlineContent = { Text(text = stringResource(id = R.string.settings_appearance)) },
leadingContent = {
SettingsIcon {
Icon(
@ -189,17 +174,11 @@ fun SettingsScreen(
},
modifier = Modifier
.testTag("settings_view_appearance")
.clickable {
navController.navigate("settings/appearance")
}
.clickable { navController.navigate("settings/appearance") }
)
ListItem(
headlineContent = {
Text(
text = stringResource(id = R.string.settings_language)
)
},
Spacer(Modifier.height(2.dp))
SettingsListItem(
headlineContent = { Text(text = stringResource(id = R.string.settings_language)) },
leadingContent = {
SettingsIcon {
Icon(
@ -210,17 +189,11 @@ fun SettingsScreen(
},
modifier = Modifier
.testTag("settings_view_language")
.clickable {
navController.navigate("settings/language")
}
.clickable { navController.navigate("settings/language") }
)
ListItem(
headlineContent = {
Text(
text = stringResource(id = R.string.settings_chat)
)
},
Spacer(Modifier.height(2.dp))
SettingsListItem(
headlineContent = { Text(text = stringResource(id = R.string.settings_chat)) },
leadingContent = {
SettingsIcon {
Icon(
@ -231,16 +204,12 @@ fun SettingsScreen(
},
modifier = Modifier
.testTag("settings_view_chat")
.clickable {
navController.navigate("settings/chat")
}
.clickable { navController.navigate("settings/chat") }
)
ListItem(
headlineContent = {
Text(
text = stringResource(id = R.string.settings_notifications)
)
},
Spacer(Modifier.height(2.dp))
SettingsListItem(
last = true,
headlineContent = { Text(text = stringResource(id = R.string.settings_notifications)) },
leadingContent = {
SettingsIcon {
Icon(
@ -251,21 +220,25 @@ fun SettingsScreen(
},
modifier = Modifier
.testTag("settings_view_notifications")
.clickable {
navController.navigate("settings/notifications")
}
.clickable { navController.navigate("settings/notifications") }
)
ListHeader {
Text(stringResource(R.string.settings_category_miscellaneous))
}
ListItem(
headlineContent = {
Text(
text = stringResource(id = R.string.about)
)
},
val miscLastIsExperiments = LoadedSettings.experimentsEnabled
val miscLastIsLabs =
!miscLastIsExperiments && FeatureFlags.labsAccessControlGranted
val miscLastIsDebug =
!miscLastIsExperiments && !miscLastIsLabs && BuildConfig.DEBUG
val miscLastIsAbout =
!miscLastIsExperiments && !miscLastIsLabs && !miscLastIsDebug
SettingsListItem(
first = true,
last = miscLastIsAbout,
headlineContent = { Text(text = stringResource(id = R.string.about)) },
leadingContent = {
SettingsIcon {
Icon(
@ -276,18 +249,14 @@ fun SettingsScreen(
},
modifier = Modifier
.testTag("settings_view_about")
.clickable {
navController.navigate("about")
}
.clickable { navController.navigate("about") }
)
if (BuildConfig.DEBUG) {
ListItem(
headlineContent = {
Text(
text = "Debug"
)
},
Spacer(Modifier.height(2.dp))
SettingsListItem(
last = miscLastIsDebug,
headlineContent = { Text(text = "Debug") },
leadingContent = {
SettingsIcon {
Icon(
@ -298,19 +267,15 @@ fun SettingsScreen(
},
modifier = Modifier
.testTag("settings_view_debug")
.clickable {
navController.navigate("settings/debug")
}
.clickable { navController.navigate("settings/debug") }
)
}
if (FeatureFlags.labsAccessControlGranted) {
ListItem(
headlineContent = {
Text(
text = "Labs"
)
},
Spacer(Modifier.height(2.dp))
SettingsListItem(
last = miscLastIsLabs,
headlineContent = { Text(text = "Labs") },
leadingContent = {
SettingsIcon {
Icon(
@ -321,19 +286,15 @@ fun SettingsScreen(
},
modifier = Modifier
.testTag("settings_view_labs")
.clickable {
navController.navigate("labs")
}
.clickable { navController.navigate("labs") }
)
}
if (LoadedSettings.experimentsEnabled) {
ListItem(
headlineContent = {
Text(
text = "Experiments"
)
},
Spacer(Modifier.height(2.dp))
SettingsListItem(
last = true,
headlineContent = { Text(text = "Experiments") },
leadingContent = {
SettingsIcon {
Icon(
@ -344,9 +305,7 @@ fun SettingsScreen(
},
modifier = Modifier
.testTag("settings_view_experiments")
.clickable {
navController.navigate("settings/experiments")
}
.clickable { navController.navigate("settings/experiments") }
)
}
@ -359,12 +318,9 @@ fun SettingsScreen(
)
}
ListItem(
headlineContent = {
Text(
text = stringResource(id = R.string.settings_changelog)
)
},
SettingsListItem(
first = true,
headlineContent = { Text(text = stringResource(id = R.string.settings_changelog)) },
leadingContent = {
SettingsIcon {
Icon(
@ -375,22 +331,12 @@ fun SettingsScreen(
},
modifier = Modifier
.testTag("settings_view_changelog")
.clickable {
navController.navigate("changelog/latest")
}
.clickable { navController.navigate("changelog/latest") }
)
ListItem(
headlineContent = {
Text(
text = stringResource(id = R.string.settings_feedback)
)
},
supportingContent = {
Text(
text = stringResource(id = R.string.settings_feedback_description)
)
},
Spacer(Modifier.height(2.dp))
SettingsListItem(
headlineContent = { Text(text = stringResource(id = R.string.settings_feedback)) },
supportingContent = { Text(text = stringResource(id = R.string.settings_feedback_description)) },
leadingContent = {
SettingsIcon {
Icon(
@ -406,18 +352,16 @@ fun SettingsScreen(
context,
InviteActivity::class.java
).setAction(Intent.ACTION_VIEW)
intent.data = "https://stt.gg/Testers".toUri()
context.startActivity(intent)
}
)
ListItem(
Spacer(Modifier.height(2.dp))
SettingsListItem(
last = true,
headlineContent = {
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.error) {
Text(
text = stringResource(id = R.string.logout)
)
Text(text = stringResource(id = R.string.logout))
}
},
leadingContent = {
@ -445,6 +389,42 @@ fun SettingsScreen(
}
}
@Composable
private fun SettingsListItem(
first: Boolean = false,
last: Boolean = false,
headlineContent: @Composable () -> Unit,
supportingContent: (@Composable () -> Unit)? = null,
leadingContent: (@Composable () -> Unit)? = null,
modifier: Modifier = Modifier,
) {
ListItem(
colors = ListItemDefaults.colors(containerColor = MaterialTheme.colorScheme.surfaceContainer),
headlineContent = headlineContent,
supportingContent = supportingContent,
leadingContent = leadingContent,
modifier = Modifier
.padding(horizontal = 16.dp)
.clip(
when {
first && last -> MaterialTheme.shapes.large
first -> MaterialTheme.shapes.extraSmall.copy(
topStart = MaterialTheme.shapes.large.topStart,
topEnd = MaterialTheme.shapes.large.topEnd
)
last -> MaterialTheme.shapes.extraSmall.copy(
bottomStart = MaterialTheme.shapes.large.bottomStart,
bottomEnd = MaterialTheme.shapes.large.bottomEnd
)
else -> MaterialTheme.shapes.extraSmall
}
)
.then(modifier)
)
}
@Composable
fun SettingsIcon(danger: Boolean = false, content: @Composable () -> Unit) {
CompositionLocalProvider(
@ -454,4 +434,4 @@ fun SettingsIcon(danger: Boolean = false, content: @Composable () -> Unit) {
) {
content()
}
}
}

View File

@ -4,11 +4,12 @@ import android.app.Application
import android.util.Log
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
@ -311,47 +312,58 @@ fun MemberListSheet(
)
}
is MemberListSheetItem.MemberItem -> item(key = item.member.id!!.user) {
MemberListItem(
user = StoatAPI.userCache[item.member.id!!.user],
member = item.member,
serverId = serverId,
userId = item.member.id!!.user,
modifier = Modifier
.combinedClickable(
onClick = {
userInfoSheetTarget = item.member.id!!.user
showUserInfoSheet = true
},
onClickLabel = stringResource(R.string.user_info_sheet_open),
onLongClick = {
memberContextSheetTarget = item.member.id!!.user
showMemberContextSheet = true
},
onLongClickLabel = stringResource(R.string.member_context_sheet_open)
)
)
is MemberListSheetItem.MemberItem -> {
val isFirst = index == 0 || viewModel.fullItemList[index - 1] is MemberListSheetItem.CategoryItem
val isLast = index == viewModel.fullItemList.size - 1 || viewModel.fullItemList[index + 1] is MemberListSheetItem.CategoryItem
item(key = item.member.id!!.user) {
MemberListItem(
user = StoatAPI.userCache[item.member.id!!.user],
member = item.member,
serverId = serverId,
userId = item.member.id!!.user,
first = isFirst,
last = isLast,
onClick = {
userInfoSheetTarget = item.member.id!!.user
showUserInfoSheet = true
},
onLongClick = {
memberContextSheetTarget = item.member.id!!.user
showMemberContextSheet = true
},
modifier = Modifier.padding(horizontal = 16.dp)
)
if (!isLast) {
Spacer(modifier = Modifier.height(2.dp))
}
}
}
is MemberListSheetItem.UserItem -> item(key = item.user.id!!) {
MemberListItem(
user = item.user,
member = null,
serverId = serverId,
userId = item.user.id!!,
modifier = Modifier.combinedClickable(
is MemberListSheetItem.UserItem -> {
val isFirst = index == 0 || viewModel.fullItemList[index - 1] is MemberListSheetItem.CategoryItem
val isLast = index == viewModel.fullItemList.size - 1 || viewModel.fullItemList[index + 1] is MemberListSheetItem.CategoryItem
item(key = item.user.id!!) {
MemberListItem(
user = item.user,
member = null,
serverId = serverId,
userId = item.user.id!!,
first = isFirst,
last = isLast,
onClick = {
userInfoSheetTarget = item.user.id!!
showUserInfoSheet = true
},
onClickLabel = stringResource(R.string.user_info_sheet_open),
onLongClick = {
memberContextSheetTarget = item.user.id!!
showMemberContextSheet = true
},
onLongClickLabel = stringResource(R.string.member_context_sheet_open)
modifier = Modifier.padding(horizontal = 16.dp)
)
)
if (!isLast) {
Spacer(modifier = Modifier.height(2.dp))
}
}
}
}
}

View File

@ -14,7 +14,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.HorizontalDivider
@ -218,9 +218,9 @@ fun ReactionInfoSheet(messageId: String, emoji: String, onDismiss: () -> Unit) {
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.padding(
top = 16.dp,
start = 16.dp,
end = 16.dp,
bottom = 4.dp
start = 24.dp,
end = 24.dp,
bottom = 16.dp
),
) {
Row(
@ -308,15 +308,14 @@ fun ReactionInfoSheet(messageId: String, emoji: String, onDismiss: () -> Unit) {
)
}
}
HorizontalDivider()
}
}
}
val reactionsForEmoji =
reactions?.get(reactionEmoji[selectedReactionIndex]) ?: emptyList()
items(items = reactionsForEmoji) { reaction ->
itemsIndexed(items = reactionsForEmoji) { index, reaction ->
val isLast = index == reactionsForEmoji.size - 1
val userOrNull = StoatAPI.userCache[reaction]
val user = userOrNull ?: User.getPlaceholder(reaction)
val member = if (channel.server != null && user.id != null) {
@ -340,7 +339,13 @@ fun ReactionInfoSheet(messageId: String, emoji: String, onDismiss: () -> Unit) {
user = user,
serverId = channel.server,
userId = reaction,
first = index == 0,
last = isLast,
modifier = Modifier.padding(horizontal = 16.dp)
)
if (!isLast) {
Spacer(modifier = Modifier.height(2.dp))
}
}
item("bottom") {