cinny/src/app/features/call-status/CallControl.tsx
Ajay Bura bc6caddcc8
Add own control buttons for element-call (#2744)
* add mutation observer hok

* add hook to read speaking member by observing iframe content

* display speaking member name in call status bar and improve layout

* fix shrining

* add joined call control bar

* remove chat toggle from room header

* change member speaking icon to mic

* fix joined call control appear in other

* show spinner on end call button

* hide call statusbar for mobile view when room is selected

* make call statusbar more mobile friendly

* fix call status bar item align
2026-03-09 08:34:48 +05:30

189 lines
4.9 KiB
TypeScript

import { Box, Chip, Icon, IconButton, Icons, Spinner, Text, Tooltip, TooltipProvider } from 'folds';
import React, { useCallback } from 'react';
import { StatusDivider } from './components';
import { CallEmbed, useCallControlState } from '../../plugins/call';
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
type MicrophoneButtonProps = {
enabled: boolean;
onToggle: () => Promise<unknown>;
};
function MicrophoneButton({ enabled, onToggle }: MicrophoneButtonProps) {
return (
<TooltipProvider
position="Top"
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="300"
size="300"
onClick={() => onToggle()}
outlined
>
<Icon size="100" src={enabled ? Icons.Mic : Icons.MicMute} filled={!enabled} />
</IconButton>
)}
</TooltipProvider>
);
}
type SoundButtonProps = {
enabled: boolean;
onToggle: () => void;
};
function SoundButton({ enabled, onToggle }: SoundButtonProps) {
return (
<TooltipProvider
position="Top"
tooltip={
<Tooltip>
<Text size="T200">{enabled ? 'Turn Off Sound' : 'Turn On Sound'}</Text>
</Tooltip>
}
>
{(anchorRef) => (
<IconButton
ref={anchorRef}
variant={enabled ? 'Surface' : 'Warning'}
fill="Soft"
radii="300"
size="300"
onClick={() => onToggle()}
outlined
>
<Icon
size="100"
src={enabled ? Icons.Headphone : Icons.HeadphoneMute}
filled={!enabled}
/>
</IconButton>
)}
</TooltipProvider>
);
}
type VideoButtonProps = {
enabled: boolean;
onToggle: () => Promise<unknown>;
};
function VideoButton({ enabled, onToggle }: VideoButtonProps) {
return (
<TooltipProvider
position="Top"
tooltip={
<Tooltip>
<Text size="T200">{enabled ? 'Stop Camera' : 'Start Camera'}</Text>
</Tooltip>
}
>
{(anchorRef) => (
<IconButton
ref={anchorRef}
variant={enabled ? 'Success' : 'Surface'}
fill="Soft"
radii="300"
size="300"
onClick={() => onToggle()}
outlined
>
<Icon
size="100"
src={enabled ? Icons.VideoCamera : Icons.VideoCameraMute}
filled={enabled}
/>
</IconButton>
)}
</TooltipProvider>
);
}
type ScreenShareButtonProps = {
enabled: boolean;
onToggle: () => void;
};
function ScreenShareButton({ enabled, onToggle }: ScreenShareButtonProps) {
return (
<TooltipProvider
position="Top"
tooltip={
<Tooltip>
<Text size="T200">{enabled ? 'Stop Screenshare' : 'Start Screenshare'}</Text>
</Tooltip>
}
>
{(anchorRef) => (
<IconButton
ref={anchorRef}
variant={enabled ? 'Success' : 'Surface'}
fill="Soft"
radii="300"
size="300"
onClick={onToggle}
outlined
>
<Icon size="100" src={Icons.ScreenShare} filled={enabled} />
</IconButton>
)}
</TooltipProvider>
);
}
export function CallControl({ callEmbed, compact }: { callEmbed: CallEmbed; compact: boolean }) {
const { microphone, video, sound, screenshare } = useCallControlState(callEmbed.control);
const [hangupState, hangup] = useAsyncCallback(
useCallback(() => callEmbed.hangup(), [callEmbed])
);
const exiting =
hangupState.status === AsyncStatus.Loading || hangupState.status === AsyncStatus.Success;
return (
<Box shrink="No" alignItems="Center" gap="300">
<Box alignItems="Inherit" gap="200">
<MicrophoneButton
enabled={microphone}
onToggle={() => callEmbed.control.toggleMicrophone()}
/>
<SoundButton enabled={sound} onToggle={() => callEmbed.control.toggleSound()} />
{!compact && <StatusDivider />}
<VideoButton enabled={video} onToggle={() => callEmbed.control.toggleVideo()} />
{!compact && (
<ScreenShareButton
enabled={screenshare}
onToggle={() => callEmbed.control.toggleScreenshare()}
/>
)}
</Box>
<StatusDivider />
<Chip
variant="Critical"
radii="Pill"
fill="Soft"
before={
exiting ? (
<Spinner variant="Critical" fill="Soft" size="50" />
) : (
<Icon size="50" src={Icons.PhoneDown} filled />
)
}
disabled={exiting}
outlined
onClick={hangup}
>
{!compact && (
<Text as="span" size="L400">
End
</Text>
)}
</Chip>
</Box>
);
}