diff --git a/apps/desktop/src/components/VoiceChannelRail.tsx b/apps/desktop/src/components/VoiceChannelRail.tsx deleted file mode 100644 index b40d834..0000000 --- a/apps/desktop/src/components/VoiceChannelRail.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import type { ConversationSummary } from '@chat-app/shared/chat'; -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 } from './icons'; - -interface Props { - conversation: ConversationSummary; -} - -const MAX_AVATARS = 5; - -/** - * Discord-style persistent voice-channel band rendered at the top of the - * message surface. Always visible for groups so any member can pop in - * without an invite-ring; for 1:1 conversations only when someone's already - * in (a soft "rejoin" affordance). Joining uses the existing joinActiveCall - * path which sends no invite signals. - */ -export function VoiceChannelRail({ conversation }: Props) { - const { t } = useTranslation(['app']); - const { session } = useAuth(); - const { state, joinActiveCall } = useCall(); - const presentIds = useCallPresence(conversation.id); - - 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); - const isGroup = conversation.type === 'group'; - - // For groups: persistent channel feel — show even when empty. - // For DMs: only when somebody is already in (matches Discord-DM behaviour). - const visible = !iAmIn && (isGroup || others.length > 0); - if (!visible) return null; - - const showAvatars = others.slice(0, MAX_AVATARS); - const overflow = Math.max(0, others.length - MAX_AVATARS); - const busy = state.kind !== 'idle'; - - const handleJoin = () => { - if (busy) return; - void joinActiveCall(conversation.id, 'audio'); - }; - - return ( -
{ - if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault(); - handleJoin(); - } - }} - className={ - 'flex cursor-pointer items-center gap-3 border-b border-line px-5 py-2.5 text-sm transition focus:outline-none focus-visible:ring-2 focus-visible:ring-accent/40 ' + - (others.length > 0 - ? 'bg-emerald-500/10 hover:bg-emerald-500/15' - : 'bg-surface-3/70 hover:bg-surface-3') - } - > -
0 ? 'bg-emerald-500/20 text-emerald-500' : 'bg-accent/15 text-accent') - } - > - -
-
-

- {t('app:call.voice_channel', { defaultValue: 'Sprach-Channel' })} -

-

- {others.length === 0 - ? t('app:call.voice_empty', { - defaultValue: 'Niemand drin — sei der Erste.', - }) - : t('app:call.voice_count', { - defaultValue: '{{count}} im Channel', - count: others.length, - })} -

-
- {showAvatars.length > 0 && ( -
- {showAvatars.map((id) => { - const member = conversation.members.find((m) => m.userId === id); - return ( -
- -
- ); - })} - {overflow > 0 && ( - - +{overflow} - - )} -
- )} - -
- ); -} diff --git a/apps/desktop/src/pages/ConversationPage.tsx b/apps/desktop/src/pages/ConversationPage.tsx index ca47a37..2a64e1d 100644 --- a/apps/desktop/src/pages/ConversationPage.tsx +++ b/apps/desktop/src/pages/ConversationPage.tsx @@ -23,7 +23,7 @@ import { } from '../components/icons'; import { InCallPanel } from '../components/InCallPanel'; import { IncomingCallPanel } from '../components/IncomingCallPanel'; -import { VoiceChannelRail } from '../components/VoiceChannelRail'; +import { CallPreviewPanel } from '../components/CallPreviewPanel'; import { MediaFilesDrawer } from '../components/MediaFilesDrawer'; import { MentionAutocomplete } from '../components/MentionAutocomplete'; import { MessageBubble, type QuotedRef } from '../components/MessageBubble'; @@ -654,7 +654,7 @@ export function ConversationPage() { {/* Discord-style persistent voice-channel rail. Always visible in groups so anyone can pop in without an invite-ring; hidden in 1:1s unless someone is already waiting. Hides automatically once we're in. */} - {conversation && !incomingHere && } + {conversation && !incomingHere && } {incomingHere && conversation && } {conversation && }