feat(call): hide soundboard button when the user has no sounds
Previously the music icon always rendered in the in-call bar, opening a popover with a "Keine Sounds gespeichert" empty-state. Matches Discord's pattern better to just drop the button entirely until the user has something to play — otherwise it reads as a broken control. Live- subscribes to soundboardStorage so adding/removing sounds mid-call flips the button in or out without reopening the call. Also closes the popover automatically if the user clears their last sound while it's open. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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<number | null>(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'}
|
||||
|
||||
Reference in New Issue
Block a user