diff --git a/apps/desktop/src/components/InCallPanel.tsx b/apps/desktop/src/components/InCallPanel.tsx index ecbd934..36fa3a9 100644 --- a/apps/desktop/src/components/InCallPanel.tsx +++ b/apps/desktop/src/components/InCallPanel.tsx @@ -11,6 +11,10 @@ import { type PttSettings, subscribePttSettings, } from '../lib/pttSettings'; +import { + listSounds, + subscribeSoundboardChanges, +} from '../lib/soundboardStorage'; import { useActiveSpeakers } from '../lib/useActiveSpeakers'; import { CallControls } from './CallControls'; import { CallParticipantTile } from './CallParticipantTile'; @@ -93,6 +97,37 @@ export function InCallPanel({ conversation }: Props) { const [shareMenu, setShareMenu] = useState< { userId: string; displayName: string; hasAudio: boolean; x: number; y: number } | null >(null); + // Soundboard-count so the in-call bar only surfaces the music button when + // the user actually has something to play. Matches Discord's "hide soundboard + // when empty" behaviour — no point dangling a button that opens to a blank + // "Keine Sounds" popover. Subscribes live so a sound added mid-call makes + // the button pop in without reopening the call. + const [soundboardCount, setSoundboardCount] = useState(null); + useEffect(() => { + let cancelled = false; + const refresh = async () => { + try { + const all = await listSounds(); + if (!cancelled) setSoundboardCount(all.length); + } catch { + if (!cancelled) setSoundboardCount(0); + } + }; + void refresh(); + const unsub = subscribeSoundboardChanges(() => { + void refresh(); + }); + return () => { + cancelled = true; + unsub(); + }; + }, []); + // Close the popover if the user just cleared their last sound while it was + // open — keeps the panel from lingering over an empty list. + useEffect(() => { + if (soundboardCount === 0) setSoundboardOpen(false); + }, [soundboardCount]); + // Active-speaker auto-focus uses "who most recently started speaking" // rather than "exactly one speaker" — matches Discord more closely and // handles the case where two people talk briefly without the focus @@ -230,8 +265,16 @@ export function InCallPanel({ conversation }: Props) { onToggleDeafen={toggleDeafen} onOpenParticipants={() => setParticipantsOpen((v) => !v)} participantsOpen={participantsOpen} - onToggleSoundboard={() => setSoundboardOpen((v) => !v)} - soundboardOpen={soundboardOpen} + // Soundboard-Button nur wenn mind. ein Sound existiert. Bis der Count + // aus IndexedDB geladen ist (null), auch nicht rendern — verhindert + // einen Flash des Buttons beim Call-Start wenn der User eh keine + // Sounds hat. + {...(soundboardCount && soundboardCount > 0 + ? { + onToggleSoundboard: () => setSoundboardOpen((v) => !v), + soundboardOpen, + } + : {})} onHangup={() => void hangup()} compact={callMode !== 'fullscreen'} glass={callMode === 'fullscreen'}