Files
cinny/src/app/features/call/Controls.tsx
T
jared c0f9867218
CI / Build & Quality Checks (push) Successful in 10m42s
CI / Trigger Desktop Build (push) Successful in 10s
Merge upstream v4.12.3 (Element Call 0.20.1) into lotus
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 04:11:41 -04:00

280 lines
7.3 KiB
TypeScript

import React from 'react';
import { Icon, IconButton, Icons, Line, Text, Tooltip, TooltipProvider } from 'folds';
import { useAtom } from 'jotai';
import * as css from './styles.css';
import { callChatAtom } from '../../state/callEmbed';
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
export function ControlDivider() {
return (
<Line variant="SurfaceVariant" size="300" direction="Vertical" className={css.ControlDivider} />
);
}
type MicrophoneButtonProps = {
enabled: boolean;
onToggle: () => Promise<unknown>;
};
export function MicrophoneButton({ enabled, onToggle }: MicrophoneButtonProps) {
const [micState, toggleMic] = useAsyncCallback(onToggle);
const loading = micState.status === AsyncStatus.Loading;
return (
<TooltipProvider
position="Top"
delay={500}
tooltip={
<Tooltip>
<Text size="T200">{enabled ? 'Turn Off Microphone' : 'Turn On Microphone'}</Text>
</Tooltip>
}
>
{(anchorRef) => (
<IconButton
ref={anchorRef}
variant={enabled ? 'Surface' : 'Warning'}
fill="Soft"
radii="400"
size="400"
onClick={toggleMic}
aria-label={enabled ? 'Turn Off Microphone' : 'Turn On Microphone'}
outlined
disabled={loading}
>
<Icon size="400" src={enabled ? Icons.Mic : Icons.MicMute} filled={!enabled} />
</IconButton>
)}
</TooltipProvider>
);
}
type SoundButtonProps = {
enabled: boolean;
onToggle: () => void;
};
export function SoundButton({ enabled, onToggle }: SoundButtonProps) {
return (
<TooltipProvider
position="Top"
delay={500}
tooltip={
<Tooltip>
<Text size="T200">{enabled ? 'Deafen' : 'Undeafen'}</Text>
</Tooltip>
}
>
{(anchorRef) => (
<IconButton
ref={anchorRef}
variant={enabled ? 'Surface' : 'Warning'}
fill="Soft"
radii="400"
size="400"
onClick={() => onToggle()}
aria-label={enabled ? 'Undeafen' : 'Deafen'}
outlined
>
<Icon
size="400"
src={enabled ? Icons.Headphone : Icons.HeadphoneMute}
filled={!enabled}
/>
</IconButton>
)}
</TooltipProvider>
);
}
type VideoButtonProps = {
enabled: boolean;
onToggle: () => Promise<unknown>;
disabled?: boolean;
};
export function VideoButton({ enabled, onToggle, disabled }: VideoButtonProps) {
const [videoState, toggleVideo] = useAsyncCallback(onToggle);
const loading = videoState.status === AsyncStatus.Loading;
return (
<TooltipProvider
position="Top"
delay={500}
tooltip={
<Tooltip>
<Text size="T200">
{disabled ? 'Camera disabled in settings' : enabled ? 'Stop Camera' : 'Start Camera'}
</Text>
</Tooltip>
}
>
{(anchorRef) => (
<IconButton
ref={anchorRef}
variant={enabled ? 'Success' : 'Surface'}
fill="Soft"
radii="400"
size="400"
onClick={toggleVideo}
outlined
disabled={disabled || loading}
aria-label={
disabled ? 'Camera disabled in settings' : enabled ? 'Stop camera' : 'Start camera'
}
aria-pressed={enabled}
style={disabled ? { opacity: 0.4, cursor: 'not-allowed' } : undefined}
>
<Icon
size="400"
src={enabled ? Icons.VideoCamera : Icons.VideoCameraMute}
filled={enabled}
/>
</IconButton>
)}
</TooltipProvider>
);
}
type ScreenShareButtonProps = {
enabled: boolean;
onToggle: () => void;
};
export function ScreenShareButton({ enabled, onToggle }: ScreenShareButtonProps) {
return (
<TooltipProvider
position="Top"
delay={500}
tooltip={
<Tooltip>
<Text size="T200">{enabled ? 'Stop Screenshare' : 'Start Screenshare'}</Text>
</Tooltip>
}
>
{(anchorRef) => (
<IconButton
ref={anchorRef}
variant={enabled ? 'Success' : 'Surface'}
fill="Soft"
radii="400"
size="400"
onClick={() => onToggle()}
aria-label={enabled ? 'Stop Screenshare' : 'Start Screenshare'}
outlined
>
<Icon size="400" src={Icons.ScreenShare} filled={enabled} />
</IconButton>
)}
</TooltipProvider>
);
}
const FullscreenIcon = () => (
<svg width="1em" height="1em" viewBox="0 0 24 24" fill="currentColor">
<path d="M3 3h6v2H5v4H3V3zm12 0h6v6h-2V5h-4V3zM3 15h2v4h4v2H3v-6zm16 4h-4v2h6v-6h-2v4z" />
</svg>
);
const ExitFullscreenIcon = () => (
<svg width="1em" height="1em" viewBox="0 0 24 24" fill="currentColor">
<path d="M9 3H7v4H3v2h6V3zm6 0v6h6V7h-4V3h-2zM3 13v2h4v4h2v-6H3zm14 4v-4h2v6h-6v-2h4z" />
</svg>
);
type FullscreenButtonProps = {
isFullscreen: boolean;
onToggle: () => void;
};
export function FullscreenButton({ isFullscreen, onToggle }: FullscreenButtonProps) {
return (
<TooltipProvider
position="Top"
delay={500}
tooltip={
<Tooltip>
<Text size="T200">{isFullscreen ? 'Exit Fullscreen' : 'Fullscreen'}</Text>
</Tooltip>
}
>
{(anchorRef) => (
<IconButton
ref={anchorRef}
variant="Surface"
fill="Soft"
radii="400"
size="400"
onClick={onToggle}
aria-label={isFullscreen ? 'Exit Fullscreen' : 'Fullscreen'}
aria-pressed={isFullscreen}
outlined
>
{isFullscreen ? <ExitFullscreenIcon /> : <FullscreenIcon />}
</IconButton>
)}
</TooltipProvider>
);
}
type ScreenshareAudioButtonProps = {
muted: boolean;
onToggle: () => void;
};
export function ScreenshareAudioButton({ muted, onToggle }: ScreenshareAudioButtonProps) {
return (
<TooltipProvider
position="Top"
delay={500}
tooltip={
<Tooltip>
<Text size="T200">{muted ? 'Unmute Screenshare Audio' : 'Mute Screenshare Audio'}</Text>
</Tooltip>
}
>
{(anchorRef) => (
<IconButton
ref={anchorRef}
variant={muted ? 'Warning' : 'Surface'}
fill="Soft"
radii="400"
size="400"
onClick={onToggle}
aria-label={muted ? 'Unmute Screenshare Audio' : 'Mute Screenshare Audio'}
aria-pressed={muted}
outlined
>
<Icon size="400" src={muted ? Icons.VolumeMute : Icons.VolumeHigh} filled={muted} />
</IconButton>
)}
</TooltipProvider>
);
}
export function ChatButton() {
const [chat, setChat] = useAtom(callChatAtom);
return (
<TooltipProvider
position="Top"
delay={500}
tooltip={
<Tooltip>
<Text size="T200">{chat ? 'Close Chat' : 'Open Chat'}</Text>
</Tooltip>
}
>
{(anchorRef) => (
<IconButton
ref={anchorRef}
variant={chat ? 'Success' : 'Surface'}
fill="Soft"
radii="400"
size="400"
onClick={() => setChat(!chat)}
aria-label={chat ? 'Close Chat' : 'Open Chat'}
aria-pressed={chat}
outlined
>
<Icon size="400" src={Icons.Message} filled={chat} />
</IconButton>
)}
</TooltipProvider>
);
}