feat(calls): room header chip for voice limit / audio-only policy (#134)
CI / Build & Quality Checks (push) Successful in 1m35s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 8s
CI / Trigger Desktop Build (push) Successful in 9s
CI / Playwright smoke (e2e) (push) Successful in 2m38s
CI / Build & Quality Checks (push) Successful in 1m35s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 8s
CI / Trigger Desktop Build (push) Successful in 9s
CI / Playwright smoke (e2e) (push) Successful in 2m38s
One small Surface chip next to the header controls, only when a rule exists:
'0/10' (participants / io.lotus.voice_limit) and/or a muted-camera glyph when
the room's io.lotus.room_quality forbids camera and screenshare; the tooltip
and aria-label carry the words ('Voice limit 10 (0 in call) · Audio-only
room'). Desktop only — the mobile header is already tight. Verified headless:
appears with the rules set, disappears when they are cleared.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
@@ -27,6 +27,8 @@ import {
|
|||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Room } from 'matrix-js-sdk';
|
import { Room } from 'matrix-js-sdk';
|
||||||
|
import { useVoiceChannelFull } from '../../hooks/useVoiceChannelFull';
|
||||||
|
import { useRoomCallPolicy } from '../../hooks/useRoomCallPolicy';
|
||||||
import { useStateEvent } from '../../hooks/useStateEvent';
|
import { useStateEvent } from '../../hooks/useStateEvent';
|
||||||
import { PageHeader } from '../../components/page';
|
import { PageHeader } from '../../components/page';
|
||||||
import { RoomAvatar, RoomIcon } from '../../components/room-avatar';
|
import { RoomAvatar, RoomIcon } from '../../components/room-avatar';
|
||||||
@@ -415,6 +417,54 @@ const CallMenu = forwardRef<HTMLDivElement, CallMenuProps>(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [Gitea #134] One small chip that tells members a room's call rules before
|
||||||
|
* they hit the server-side guard: "8/10" when a voice limit is set, a muted
|
||||||
|
* camera glyph when the room is audio-only. Nothing when no rule exists; the
|
||||||
|
* tooltip carries the words. Desktop only (the mobile header is tight).
|
||||||
|
*/
|
||||||
|
function CallRulesChip({ room }: { room: Room }) {
|
||||||
|
const { current, max } = useVoiceChannelFull(room);
|
||||||
|
const { allowCamera, allowScreenshare } = useRoomCallPolicy(room);
|
||||||
|
const audioOnly = !allowCamera && !allowScreenshare;
|
||||||
|
if (max <= 0 && !audioOnly) return null;
|
||||||
|
const words = [
|
||||||
|
max > 0 ? `Voice limit ${max} (${current} in call)` : undefined,
|
||||||
|
audioOnly ? 'Audio-only room' : undefined,
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' · ');
|
||||||
|
return (
|
||||||
|
<TooltipProvider
|
||||||
|
position="Bottom"
|
||||||
|
offset={4}
|
||||||
|
tooltip={
|
||||||
|
<Tooltip>
|
||||||
|
<Text>{words}</Text>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{(triggerRef) => (
|
||||||
|
<Chip
|
||||||
|
ref={triggerRef}
|
||||||
|
variant="Surface"
|
||||||
|
size="400"
|
||||||
|
radii="Pill"
|
||||||
|
aria-label={words}
|
||||||
|
tabIndex={0}
|
||||||
|
before={audioOnly ? <Icon size="50" src={Icons.VideoCameraMute} /> : undefined}
|
||||||
|
>
|
||||||
|
{max > 0 && (
|
||||||
|
<Text size="T200">
|
||||||
|
{current}/{max}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Chip>
|
||||||
|
)}
|
||||||
|
</TooltipProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function CallButton() {
|
function CallButton() {
|
||||||
const room = useRoom();
|
const room = useRoom();
|
||||||
const direct = useIsDirectRoom();
|
const direct = useIsDirectRoom();
|
||||||
@@ -731,6 +781,7 @@ export function RoomViewHeader({ callView }: { callView?: boolean }) {
|
|||||||
</FocusTrap>
|
</FocusTrap>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
{screenSize === ScreenSize.Desktop && <CallRulesChip room={room} />}
|
||||||
{!room.isCallRoom() &&
|
{!room.isCallRoom() &&
|
||||||
livekitSupported &&
|
livekitSupported &&
|
||||||
rtcSupported &&
|
rtcSupported &&
|
||||||
|
|||||||
Reference in New Issue
Block a user