import { useTranslation } from 'react-i18next'; import { HeadphonesIcon, HeadphonesOffIcon, MicIcon, MicOffIcon, MonitorShareIcon, MonitorStopIcon, MusicIcon, PhoneOffIcon, UsersIcon, VideoIcon, } from './icons'; interface Props { muted: boolean; sharing: boolean; video: boolean; deafened: boolean; onToggleMute: () => void; onToggleShare: () => void; /** Right-click on the share button opens the quality picker dialog while * left-click just starts with last-used settings. Optional so pages that * don't need the advanced path (mobile, etc.) can skip it. */ onShareContextMenu?: (e: React.MouseEvent) => void; onToggleVideo?: () => void; onToggleDeafen: () => void; onHangup: () => void; onOpenParticipants?: () => void; /** Toggle the in-call soundboard popover. Active = panel currently open. */ onToggleSoundboard?: () => void; soundboardOpen?: boolean; participantsOpen?: boolean; /** Compact variant used inside the docked call (36px buttons). */ compact?: boolean; /** Glass variant used when controls float on fullscreen cinema mode. */ glass?: boolean; /** Disable transient interactions while the call isn't fully connected. */ disabledMedia?: boolean; } export function CallControls({ muted, sharing, video, deafened, onToggleMute, onToggleShare, onShareContextMenu, onToggleVideo, onToggleDeafen, onHangup, onOpenParticipants, onToggleSoundboard, soundboardOpen = false, participantsOpen = false, compact = false, glass = false, disabledMedia = false, }: Props) { const { t } = useTranslation(['app']); const wrapBase = glass ? 'glass-pill flex items-center justify-center gap-2 rounded-[18px] p-2.5' : 'flex items-center justify-center gap-2 border-t border-line bg-surface-3 p-3'; const btnSize = compact ? 'h-9 w-9 rounded-[10px]' : 'h-12 w-12 rounded-[14px]'; const hangupSize = compact ? 'h-9 w-[54px] rounded-[10px]' : 'h-12 w-[72px] rounded-[14px]'; return (
{muted ? : } {deafened ? ( ) : ( )} {onToggleVideo && ( )} {sharing ? ( ) : ( )} {onToggleSoundboard && ( )} {onOpenParticipants && ( )}
); } interface CallButtonProps { label: string; onClick: () => void; onContextMenu?: (e: React.MouseEvent) => void; disabled?: boolean; active?: boolean; activeTone?: 'accent' | 'danger'; tone?: 'default' | 'danger'; glass?: boolean; className?: string; /** Stable trigger id so portals (popovers) can skip outside-click dismiss * when the user is toggling their own trigger. */ dataTrigger?: string; children: React.ReactNode; } function CallButton({ label, onClick, onContextMenu, disabled, active, activeTone = 'accent', tone = 'default', glass = false, className = '', dataTrigger, children, }: CallButtonProps) { const base = 'inline-flex cursor-pointer items-center justify-center border transition focus:outline-none focus-visible:ring-2 focus-visible:ring-accent/50 disabled:cursor-not-allowed disabled:opacity-50'; let toneClass: string; if (tone === 'danger') { toneClass = 'bg-rose-600 text-white border-rose-600 hover:bg-rose-500'; } else if (active && activeTone === 'danger') { toneClass = 'bg-rose-600 text-white border-rose-600 hover:bg-rose-500'; } else if (active) { toneClass = 'bg-accent text-accent-fg border-accent hover:brightness-110'; } else if (glass) { toneClass = 'border-white/15 bg-white/10 text-white hover:bg-white/15'; } else { toneClass = 'border-line bg-surface-2 text-fg hover:bg-surface'; } return ( ); }