chore: show call participant mute and camera-off state #279

Merged
hodlbod merged 1 commits from userAdityaa/flotilla:call-participants into dev 2026-05-21 20:58:54 +00:00
Collaborator

Summary

Fixes #273. Other participants can now see when someone is muted or has their camera off. LiveKit track events keep remote state updated; your own state comes from the session. The sidebar participant list shows red mic/camera-off badges, and active video tiles show mute (or camera-off on camera streams only). Avatar-only tiles skip badges since that state is already obvious there. Call controls use matching purple-on / red-off styling with microphone-off and videocamera-off icons.

Screenshot

Screenshot 2026-05-21 at 4.39.34 PM.png
### Summary Fixes #273. Other participants can now see when someone is muted or has their camera off. LiveKit track events keep remote state updated; your own state comes from the session. The sidebar participant list shows red mic/camera-off badges, and active video tiles show mute (or camera-off on camera streams only). Avatar-only tiles skip badges since that state is already obvious there. Call controls use matching purple-on / red-off styling with microphone-off and videocamera-off icons. ### Screenshot <img width="270" alt="Screenshot 2026-05-21 at 4.39.34 PM.png" src="attachments/033b6e8e-fb3a-41af-87f5-5f626ac8e552">
userAdityaa force-pushed call-participants from df1c66f240 to 4c5cf70ebc 2026-05-21 11:21:52 +00:00 Compare
userAdityaa force-pushed call-participants from 4c5cf70ebc to 442829695b 2026-05-21 11:23:13 +00:00 Compare
userAdityaa force-pushed call-participants from 442829695b to 2d37c24ba5 2026-05-21 11:36:28 +00:00 Compare
hodlbod reviewed 2026-05-21 16:19:38 +00:00
@@ -44,2 +44,4 @@
export const speakingParticipants = writable<VoiceParticipant[]>([])
export const participantMediaState = writable<Map<string, {muted: boolean; cameraOn: boolean}>>(
new Map(),
Owner

Put the type annotation on the map, not the writable (to avoid redundant Map type)

Put the type annotation on the map, not the writable (to avoid redundant Map type)
hodlbod marked this conversation as resolved
@@ -93,0 +97,4 @@
if (!m.has(identity)) return m
const next = new Map(m)
next.delete(identity)
return next
Owner

Could be simplified as m => new Map([...m].filter(nthEq(0, identity)). Same thing below. No need to guard changes, update triggers regardless.

Could be simplified as `m => new Map([...m].filter(nthEq(0, identity))`. Same thing below. No need to guard changes, `update` triggers regardless.
@@ -290,9 +321,12 @@ export const joinVoiceRoom = async (
}
participantPubkeyMap.set(new Map())
participantMediaState.set(new Map())
Owner

These two stores seem redundant, I bet you could refactor pubkeyMap to mediaState instead of keep track of things separately

These two stores seem redundant, I bet you could refactor pubkeyMap to mediaState instead of keep track of things separately
@@ -126,0 +139,4 @@
source: Track.Source.Camera,
})
}
for (const rp of room.remoteParticipants.values()) {
Owner

We already loop over this, I don't see how this code will ever add a video tile?

We already loop over this, I don't see how this code will ever add a video tile?
hodlbod marked this conversation as resolved
@@ -189,3 +188,3 @@
mediaToggleClass,
"overflow-visible",
!$voiceMicMuted && $isLocalSpeaking && "text-primary",
!$voiceMicMuted && "text-primary",
Owner

While we're at it, I think the unmuted state should not be primary colored.

While we're at it, I think the unmuted state should not be primary colored.
hodlbod marked this conversation as resolved
userAdityaa added 1 commit 2026-05-21 17:36:17 +00:00
userAdityaa force-pushed call-participants from 2d37c24ba5 to fd58de5de4 2026-05-21 17:36:17 +00:00 Compare
hodlbod merged commit 0d61278c56 into dev 2026-05-21 20:58:54 +00:00
hodlbod deleted branch call-participants 2026-05-21 20:58:54 +00:00
Sign in to join this conversation.