fix(pip): correctly identify whose mic is muted in PiP overlay

Previously PipMuteOverlay fired on useRemoteAllMuted (any remote
muted) and rendered in the bottom-left corner — the conventional
position for local-user mic status — causing users to think their
own mic was muted when it wasn't.

Fix: split into two distinct indicators
- Bottom-left: local mic muted only (from useCallControlState),
  labelled "You" so attribution is unambiguous
- Top-right: "All muted" warning (warning color, not critical) when
  all remote participants are muted

UNTESTED — verify in a real call at chat.lotusguild.org.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-18 19:23:12 -04:00
parent e2b957b6bd
commit 9deeef6e8d
2 changed files with 66 additions and 26 deletions
+53 -26
View File
@@ -419,34 +419,61 @@ function CallUtils({ embed }: { embed: CallEmbed }) {
return null;
}
/** Shown inside the PiP window when the local microphone is muted. */
/**
* PiP status indicators:
* - Bottom-left badge: local mic muted (matches Discord/Slack convention — bottom-left = "your" mic)
* - Top-right badge: all remote participants are muted (quiet room warning)
*
* Deliberately separated so users never mistake remote-mute state for their own.
*/
function PipMuteOverlay({ callEmbed }: { callEmbed: CallEmbed }) {
const allMuted = useRemoteAllMuted(callEmbed);
if (!allMuted) return null;
const mx = useMatrixClient();
const controlState = useCallControlState(callEmbed.control);
const allRemoteMuted = useRemoteAllMuted(callEmbed);
const localMicMuted = !controlState.microphone;
const localUserId = mx.getSafeUserId();
const localDisplayName = getMxIdLocalPart(localUserId) ?? localUserId;
const badgeStyle: React.CSSProperties = {
position: 'absolute',
zIndex: 3,
background: 'rgba(0,0,0,0.65)',
backdropFilter: 'blur(4px)',
borderRadius: '6px',
padding: '3px 7px',
display: 'flex',
alignItems: 'center',
gap: '4px',
pointerEvents: 'none',
fontSize: '12px',
lineHeight: 1,
userSelect: 'none',
};
return (
<div
aria-label="Microphone muted"
style={{
position: 'absolute',
bottom: '8px',
left: '8px',
zIndex: 3,
background: 'rgba(0,0,0,0.60)',
backdropFilter: 'blur(4px)',
borderRadius: '6px',
padding: '3px 7px',
display: 'flex',
alignItems: 'center',
gap: '4px',
pointerEvents: 'none',
color: color.Critical.Main,
fontSize: '13px',
lineHeight: 1,
userSelect: 'none',
}}
>
<Icon size="100" src={Icons.MicMute} filled />
</div>
<>
{localMicMuted && (
<div
aria-label={`Your microphone is muted (${localDisplayName})`}
title={`Your microphone is muted`}
style={{ ...badgeStyle, bottom: '8px', left: '8px', color: color.Critical.Main }}
>
<Icon size="100" src={Icons.MicMute} filled />
<span style={{ fontSize: '11px', fontWeight: 600 }}>You</span>
</div>
)}
{allRemoteMuted && (
<div
aria-label="All other participants are muted"
title="All other participants are muted"
style={{ ...badgeStyle, top: '8px', right: '8px', color: color.Warning.Main }}
>
<Icon size="50" src={Icons.MicMute} />
<span style={{ fontSize: '11px' }}>All muted</span>
</div>
)}
</>
);
}