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:
@@ -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>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user