feat(nests): bigger avatars + clearer mute/speaking state in participant grid
100dp avatars across stage and audience tabs, names centered under each cell, and the avatar border now distinguishes actively speaking (green) from publishing-but-muted (red) at a glance — the existing mic-pill keeps its semantics underneath. UsernameDisplay gains an optional textAlign so the grid can opt in to centered labels without disturbing other call sites.
This commit is contained in:
@@ -31,6 +31,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
@@ -100,6 +101,7 @@ fun UsernameDisplay(
|
||||
weight: Modifier = Modifier,
|
||||
fontWeight: FontWeight = FontWeight.Bold,
|
||||
textColor: Color = Color.Unspecified,
|
||||
textAlign: TextAlign? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val userMetadata by observeUserInfo(baseUser, accountViewModel)
|
||||
@@ -107,9 +109,9 @@ fun UsernameDisplay(
|
||||
CrossfadeIfEnabled(targetState = userMetadata, modifier = weight, label = "UsernameDisplay", accountViewModel = accountViewModel) {
|
||||
val name = it?.info?.bestName()
|
||||
if (name != null) {
|
||||
UserDisplay(name, it.tags, weight, fontWeight, textColor)
|
||||
UserDisplay(name, it.tags, weight, fontWeight, textColor, textAlign)
|
||||
} else {
|
||||
NPubDisplay(baseUser, weight, fontWeight, textColor)
|
||||
NPubDisplay(baseUser, weight, fontWeight, textColor, textAlign)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,6 +122,7 @@ private fun NPubDisplay(
|
||||
modifier: Modifier,
|
||||
fontWeight: FontWeight = FontWeight.Bold,
|
||||
textColor: Color = Color.Unspecified,
|
||||
textAlign: TextAlign? = null,
|
||||
) {
|
||||
Text(
|
||||
text = remember { user.pubkeyDisplayHex() },
|
||||
@@ -128,6 +131,7 @@ private fun NPubDisplay(
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = textColor,
|
||||
textAlign = textAlign,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -138,11 +142,13 @@ private fun UserDisplay(
|
||||
modifier: Modifier,
|
||||
fontWeight: FontWeight = FontWeight.Bold,
|
||||
textColor: Color = Color.Unspecified,
|
||||
textAlign: TextAlign? = null,
|
||||
) {
|
||||
CreateTextWithEmoji(
|
||||
text = bestDisplayName,
|
||||
tags = tags,
|
||||
fontWeight = fontWeight,
|
||||
textAlign = textAlign,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = modifier,
|
||||
|
||||
+25
-15
@@ -51,6 +51,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
@@ -62,21 +63,22 @@ import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
|
||||
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size40dp
|
||||
import kotlinx.collections.immutable.ImmutableSet
|
||||
import kotlinx.collections.immutable.persistentSetOf
|
||||
|
||||
private val STAGE_CELL_MIN = 88.dp
|
||||
private val STAGE_AVATAR = 48.dp
|
||||
private val AUDIENCE_CELL_MIN = 76.dp
|
||||
private val AUDIENCE_AVATAR = Size40dp
|
||||
private val STAGE_CELL_MIN = 112.dp
|
||||
private val STAGE_AVATAR = 100.dp
|
||||
private val AUDIENCE_CELL_MIN = 112.dp
|
||||
private val AUDIENCE_AVATAR = 100.dp
|
||||
private val GRID_SPACING = 6.dp
|
||||
private val AVATAR_RING_WIDTH = 3.dp
|
||||
|
||||
// Two rows visible by default. Each cell is roughly avatar + name +
|
||||
// reaction headroom, so this lifts to ~200dp on most phones — tall
|
||||
// enough to show 6-8 speakers without scrolling but small enough to
|
||||
// leave the chat / audience tab the majority of the viewport.
|
||||
private val STAGE_MAX_HEIGHT = 220.dp
|
||||
// reaction headroom; with a 100dp avatar one row lifts to ~140dp, so
|
||||
// two rows + label/padding lands near 320dp — tall enough to show a
|
||||
// handful of speakers without scrolling but small enough to leave the
|
||||
// chat / audience tab the majority of the viewport.
|
||||
private val STAGE_MAX_HEIGHT = 320.dp
|
||||
|
||||
/**
|
||||
* Vertical adaptive grid for the on-stage section. Used as the
|
||||
@@ -194,14 +196,21 @@ private fun MemberCell(
|
||||
accountViewModel: AccountViewModel,
|
||||
onLongPressParticipant: ((String) -> Unit)?,
|
||||
) {
|
||||
val ringColor = MaterialTheme.colorScheme.primary
|
||||
val avatarModifier =
|
||||
val mutedRingColor = MaterialTheme.colorScheme.error
|
||||
// Tri-state ring around the avatar so a glance distinguishes:
|
||||
// - green ring: actively speaking right now
|
||||
// - red ring: on stage and broadcasting but mic flagged muted
|
||||
// - no ring: idle, audience, or unknown mute state
|
||||
val ringColor =
|
||||
when {
|
||||
isSpeaking && member.absent -> Modifier.border(2.dp, ringColor, CircleShape).then(Modifier.alpha(0.5f))
|
||||
isSpeaking -> Modifier.border(2.dp, ringColor, CircleShape)
|
||||
member.absent -> Modifier.alpha(0.5f)
|
||||
else -> Modifier
|
||||
isSpeaking -> NEST_SPEAKING_COLOR
|
||||
showMicBadge && member.publishing && member.muted == true -> mutedRingColor
|
||||
else -> null
|
||||
}
|
||||
val avatarModifier =
|
||||
Modifier
|
||||
.let { if (ringColor != null) it.border(AVATAR_RING_WIDTH, ringColor, CircleShape) else it }
|
||||
.let { if (member.absent) it.alpha(0.5f) else it }
|
||||
val user =
|
||||
remember(member.pubkey) {
|
||||
com.vitorpamplona.amethyst.model.LocalCache
|
||||
@@ -251,6 +260,7 @@ private fun MemberCell(
|
||||
UsernameDisplay(
|
||||
baseUser = user,
|
||||
weight = Modifier.fillMaxWidth().padding(top = 4.dp),
|
||||
textAlign = TextAlign.Center,
|
||||
accountViewModel = accountViewModel,
|
||||
)
|
||||
if (reactions.isNotEmpty()) {
|
||||
|
||||
Reference in New Issue
Block a user