import type { ConversationSummary } from '@chat-app/shared/chat'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useAuth } from '../context/AuthContext'; import { useCall } from '../context/CallContext'; import { useCallPresence } from '../lib/useCallPresence'; import { Avatar } from './Avatar'; import { PhoneIcon, SpinnerIcon, XIcon } from './icons'; interface Props { conversation: ConversationSummary; } const MAX_TILES = 7; /** * Discord-DM-style call preview panel. Renders only while peers are in the * conversation's active call and the local user is NOT in it. Provides large * avatar tiles plus a single "Beitreten" call-to-action. Calls are still * STARTED via the topbar phone icon (`ConversationHeader.startCall`); this * component never initiates — only joins. */ export function CallPreviewPanel({ conversation }: Props) { const { t } = useTranslation(['app']); const { session } = useAuth(); const { state, joinActiveCall } = useCall(); const presentIds = useCallPresence(conversation.id); const [collapsed, setCollapsed] = useState(false); const myId = session?.user.id ?? null; const iAmIn = (state.kind === 'connected' || state.kind === 'connecting' || state.kind === 'reconnecting') && state.conversationId === conversation.id; const others = presentIds.filter((u) => u !== myId); if (iAmIn || others.length === 0) return null; const visibleTiles = others.slice(0, MAX_TILES); const overflow = Math.max(0, others.length - MAX_TILES); const busy = state.kind !== 'idle'; const handleJoin = () => { if (busy) return; void joinActiveCall(conversation.id, 'audio'); }; return (