feat(nests): tap an audience avatar to open the participant sheet

The host's primary path to add an audience member to the stage was the
long-press → "Promote to Speaker" row, which is hard to discover and was
also being swallowed by ClickableUserPicture (which ignores onLongClick
when onClick is null). Wire a tap handler on audience cells so a single
tap opens the same per-participant sheet, surfacing Promote to Speaker
for hosts and View Profile / Follow / Mute for everyone else.
This commit is contained in:
Claude
2026-04-29 22:43:23 +00:00
parent c8327e829f
commit 208d9a8f7f
2 changed files with 26 additions and 7 deletions
@@ -285,10 +285,16 @@ internal fun NestFullScreen(
} }
NestTab.Audience -> { NestTab.Audience -> {
// Tap and long-press on an audience avatar both open
// the per-participant sheet — the host's primary path
// for promoting a listener to the stage. Without the
// tap binding, the only way to reach "Promote to
// Speaker" is the discoverability-poor long-press.
AudienceGrid( AudienceGrid(
members = participantGrid.audience, members = participantGrid.audience,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
onLongPressParticipant = onLongPressParticipant, onLongPressParticipant = onLongPressParticipant,
onTapParticipant = onLongPressParticipant,
myPubkey = myPubkey, myPubkey = myPubkey,
modifier = modifier =
Modifier Modifier
@@ -281,6 +281,7 @@ internal fun AudienceGrid(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onLongPressParticipant: ((String) -> Unit)? = null, onLongPressParticipant: ((String) -> Unit)? = null,
onTapParticipant: ((String) -> Unit)? = null,
myPubkey: String? = null, myPubkey: String? = null,
) { ) {
if (members.isEmpty()) { if (members.isEmpty()) {
@@ -323,6 +324,7 @@ internal fun AudienceGrid(
reactions = emptyList(), reactions = emptyList(),
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
onLongPressParticipant = onLongPressParticipant, onLongPressParticipant = onLongPressParticipant,
onTapParticipant = onTapParticipant,
isSelf = myPubkey != null && member.pubkey == myPubkey, isSelf = myPubkey != null && member.pubkey == myPubkey,
modifier = Modifier.animateItem(), modifier = Modifier.animateItem(),
) )
@@ -343,6 +345,7 @@ private fun MemberCell(
onLongPressParticipant: ((String) -> Unit)?, onLongPressParticipant: ((String) -> Unit)?,
isSelf: Boolean = false, isSelf: Boolean = false,
onTapSelf: (() -> Unit)? = null, onTapSelf: (() -> Unit)? = null,
onTapParticipant: ((String) -> Unit)? = null,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val mutedRingColor = MaterialTheme.colorScheme.error val mutedRingColor = MaterialTheme.colorScheme.error
@@ -408,14 +411,24 @@ private fun MemberCell(
onLongPressParticipant?.let { cb -> { hex: String -> cb(hex) } } onLongPressParticipant?.let { cb -> { hex: String -> cb(hex) } }
} }
// Self-cell tap shortcut: tap your own avatar to toggle mic-mute // Self-cell tap shortcut: tap your own avatar to toggle mic-mute
// when broadcasting. For other cells we leave tap unhandled so // when broadcasting. For non-self cells, [onTapParticipant] (if
// the existing nav-to-profile path stays consistent (and there's // provided) routes the tap to the per-participant context sheet —
// nothing visually different from a non-clickable avatar). // used by the audience grid so a host can promote an audience
// member to the stage with a single tap, matching the existing
// long-press affordance.
val onClick = val onClick =
if (isSelf && onTapSelf != null) { when {
remember(onTapSelf) { { _: String -> onTapSelf() } } isSelf && onTapSelf != null -> {
} else { remember(onTapSelf) { { _: String -> onTapSelf() } }
null }
!isSelf && onTapParticipant != null -> {
remember(member.pubkey, onTapParticipant) { { hex: String -> onTapParticipant(hex) } }
}
else -> {
null
}
} }
val selfTint = if (isSelf) MaterialTheme.colorScheme.primary else Color.Unspecified val selfTint = if (isSelf) MaterialTheme.colorScheme.primary else Color.Unspecified
Column( Column(