feat(marmot): show member pictures and names when group has no name
Marmot groups without a configured display name now fall back to the NIP-17 strategy: a stacked avatar of the members and a comma-separated list of their first names. Applied to both the group list row and the chat top app bar. NonClickableUserPictures and DisplayUserSetAsSubject get List<HexKey> overloads so the renderers can be reused without dragging Marmot through ChatroomKey (which is also a NIP-17 dedup key — different Marmot groups can share the same member set).
This commit is contained in:
@@ -231,19 +231,28 @@ fun NonClickableUserPictures(
|
|||||||
room: ChatroomKey,
|
room: ChatroomKey,
|
||||||
size: Dp,
|
size: Dp,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
|
) {
|
||||||
|
NonClickableUserPictures(room.users.toList(), size, accountViewModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun NonClickableUserPictures(
|
||||||
|
userHexList: List<HexKey>,
|
||||||
|
size: Dp,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
) {
|
) {
|
||||||
Box(Modifier.size(size), contentAlignment = Alignment.TopEnd) {
|
Box(Modifier.size(size), contentAlignment = Alignment.TopEnd) {
|
||||||
when (room.users.size) {
|
when (userHexList.size) {
|
||||||
0 -> {}
|
0 -> {}
|
||||||
|
|
||||||
1 -> {
|
1 -> {
|
||||||
LoadUser(baseUserHex = room.users.first(), accountViewModel) {
|
LoadUser(baseUserHex = userHexList.first(), accountViewModel) {
|
||||||
it?.let { BaseUserPicture(it, size, accountViewModel, outerModifier = Modifier) }
|
it?.let { BaseUserPicture(it, size, accountViewModel, outerModifier = Modifier) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
2 -> {
|
2 -> {
|
||||||
val userList = room.users.toList()
|
val userList = userHexList
|
||||||
|
|
||||||
LoadUser(baseUserHex = userList[0], accountViewModel) {
|
LoadUser(baseUserHex = userList[0], accountViewModel) {
|
||||||
it?.let {
|
it?.let {
|
||||||
@@ -268,7 +277,7 @@ fun NonClickableUserPictures(
|
|||||||
}
|
}
|
||||||
|
|
||||||
3 -> {
|
3 -> {
|
||||||
val userList = room.users.toList()
|
val userList = userHexList
|
||||||
|
|
||||||
LoadUser(baseUserHex = userList[0], accountViewModel) {
|
LoadUser(baseUserHex = userList[0], accountViewModel) {
|
||||||
it?.let {
|
it?.let {
|
||||||
@@ -303,7 +312,7 @@ fun NonClickableUserPictures(
|
|||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
val userList = room.users.toList()
|
val userList = userHexList
|
||||||
|
|
||||||
LoadUser(baseUserHex = userList[0], accountViewModel) {
|
LoadUser(baseUserHex = userList[0], accountViewModel) {
|
||||||
it?.let {
|
it?.let {
|
||||||
|
|||||||
+34
-6
@@ -21,7 +21,9 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.marmotGroup
|
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.marmotGroup
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
@@ -35,12 +37,16 @@ import androidx.compose.material3.TopAppBar
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||||
|
import com.vitorpamplona.amethyst.ui.note.NonClickableUserPictures
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.header.DisplayUserSetAsSubject
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@@ -56,6 +62,8 @@ fun MarmotGroupChatScreen(
|
|||||||
}
|
}
|
||||||
val displayName by chatroom.displayName.collectAsStateWithLifecycle()
|
val displayName by chatroom.displayName.collectAsStateWithLifecycle()
|
||||||
val memberCount by chatroom.memberCount.collectAsStateWithLifecycle()
|
val memberCount by chatroom.memberCount.collectAsStateWithLifecycle()
|
||||||
|
val members by chatroom.members.collectAsStateWithLifecycle()
|
||||||
|
val memberPubkeys = remember(members) { members.map { it.pubkey } }
|
||||||
|
|
||||||
DisappearingScaffold(
|
DisappearingScaffold(
|
||||||
isInvertedLayout = true,
|
isInvertedLayout = true,
|
||||||
@@ -70,19 +78,39 @@ fun MarmotGroupChatScreen(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
title = {
|
title = {
|
||||||
Column(
|
Row(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier.clickable {
|
Modifier.clickable {
|
||||||
nav.nav(Route.MarmotGroupInfo(nostrGroupId))
|
nav.nav(Route.MarmotGroupInfo(nostrGroupId))
|
||||||
},
|
},
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
) {
|
) {
|
||||||
Text(displayName ?: "Marmot Group")
|
if (memberPubkeys.isNotEmpty()) {
|
||||||
if (memberCount > 0) {
|
NonClickableUserPictures(
|
||||||
Text(
|
userHexList = memberPubkeys,
|
||||||
text = "$memberCount members",
|
size = 36.dp,
|
||||||
style = MaterialTheme.typography.bodySmall,
|
accountViewModel = accountViewModel,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Column {
|
||||||
|
if (!displayName.isNullOrBlank()) {
|
||||||
|
Text(displayName!!)
|
||||||
|
} else if (memberPubkeys.isNotEmpty()) {
|
||||||
|
DisplayUserSetAsSubject(
|
||||||
|
userList = memberPubkeys,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Text("Marmot Group")
|
||||||
|
}
|
||||||
|
if (memberCount > 0) {
|
||||||
|
Text(
|
||||||
|
text = "$memberCount members",
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
|
|||||||
+37
-8
@@ -65,7 +65,9 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||||||
import com.vitorpamplona.amethyst.commons.model.marmotGroups.MarmotGroupChatroom
|
import com.vitorpamplona.amethyst.commons.model.marmotGroups.MarmotGroupChatroom
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||||
|
import com.vitorpamplona.amethyst.ui.note.NonClickableUserPictures
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.header.DisplayUserSetAsSubject
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@@ -179,6 +181,7 @@ fun MarmotGroupListScreen(
|
|||||||
MarmotGroupListItem(
|
MarmotGroupListItem(
|
||||||
groupId = groupId,
|
groupId = groupId,
|
||||||
chatroom = chatroom,
|
chatroom = chatroom,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
onClick = {
|
onClick = {
|
||||||
nav.nav(Route.MarmotGroupChat(groupId))
|
nav.nav(Route.MarmotGroupChat(groupId))
|
||||||
},
|
},
|
||||||
@@ -213,10 +216,13 @@ private fun loadGroupList(
|
|||||||
fun MarmotGroupListItem(
|
fun MarmotGroupListItem(
|
||||||
groupId: HexKey,
|
groupId: HexKey,
|
||||||
chatroom: MarmotGroupChatroom,
|
chatroom: MarmotGroupChatroom,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val displayName by chatroom.displayName.collectAsStateWithLifecycle()
|
val displayName by chatroom.displayName.collectAsStateWithLifecycle()
|
||||||
val unread by chatroom.unreadCount.collectAsStateWithLifecycle()
|
val unread by chatroom.unreadCount.collectAsStateWithLifecycle()
|
||||||
|
val members by chatroom.members.collectAsStateWithLifecycle()
|
||||||
|
val memberPubkeys = remember(members) { members.map { it.pubkey } }
|
||||||
val newestMessage = chatroom.newestMessage
|
val newestMessage = chatroom.newestMessage
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
@@ -225,17 +231,40 @@ fun MarmotGroupListItem(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable(onClick = onClick)
|
.clickable(onClick = onClick)
|
||||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.weight(1f)) {
|
if (memberPubkeys.isNotEmpty()) {
|
||||||
Text(
|
NonClickableUserPictures(
|
||||||
text = displayName ?: "Group ${groupId.take(8)}...",
|
userHexList = memberPubkeys,
|
||||||
style = MaterialTheme.typography.titleSmall,
|
size = 55.dp,
|
||||||
fontWeight = if (unread > 0) FontWeight.Bold else FontWeight.Normal,
|
accountViewModel = accountViewModel,
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
|
if (!displayName.isNullOrBlank()) {
|
||||||
|
Text(
|
||||||
|
text = displayName!!,
|
||||||
|
style = MaterialTheme.typography.titleSmall,
|
||||||
|
fontWeight = if (unread > 0) FontWeight.Bold else FontWeight.Normal,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
} else if (memberPubkeys.isNotEmpty()) {
|
||||||
|
DisplayUserSetAsSubject(
|
||||||
|
userList = memberPubkeys,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
fontWeight = if (unread > 0) FontWeight.Bold else FontWeight.Normal,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Text(
|
||||||
|
text = "Group ${groupId.take(8)}...",
|
||||||
|
style = MaterialTheme.typography.titleSmall,
|
||||||
|
fontWeight = if (unread > 0) FontWeight.Bold else FontWeight.Normal,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
}
|
||||||
if (newestMessage != null) {
|
if (newestMessage != null) {
|
||||||
Text(
|
Text(
|
||||||
text = newestMessage.event?.content ?: "",
|
text = newestMessage.event?.content ?: "",
|
||||||
|
|||||||
+8
@@ -69,7 +69,15 @@ fun DisplayUserSetAsSubject(
|
|||||||
fontWeight: FontWeight = FontWeight.Bold,
|
fontWeight: FontWeight = FontWeight.Bold,
|
||||||
) {
|
) {
|
||||||
val userList = remember(room) { room.users.toList() }
|
val userList = remember(room) { room.users.toList() }
|
||||||
|
DisplayUserSetAsSubject(userList, accountViewModel, fontWeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DisplayUserSetAsSubject(
|
||||||
|
userList: List<HexKey>,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
fontWeight: FontWeight = FontWeight.Bold,
|
||||||
|
) {
|
||||||
if (userList.size == 1) {
|
if (userList.size == 1) {
|
||||||
// Regular Design
|
// Regular Design
|
||||||
Row {
|
Row {
|
||||||
|
|||||||
Reference in New Issue
Block a user