feat: soundboard, account menu, and call timer
OpenSSF Scorecard / Scorecard analysis (push) Waiting to run
CI / Build & test (Node 20) (push) Canceled after 0s
CI / Build & test (Node 24) (push) Canceled after 0s
CI / Build & test (push) Canceled after 0s
CodeQL / Analyze (javascript-typescript) (push) Canceled after 0s
Security / Secret scan (gitleaks) (push) Canceled after 0s
Security / Dependency scan (OSV-Scanner) (push) Canceled after 0s
Security / IaC/config scan (Trivy) (push) Canceled after 0s
Security / License compliance scan (Trivy) (push) Canceled after 0s
OpenSSF Scorecard / Scorecard analysis (push) Waiting to run
CI / Build & test (Node 20) (push) Canceled after 0s
CI / Build & test (Node 24) (push) Canceled after 0s
CI / Build & test (push) Canceled after 0s
CodeQL / Analyze (javascript-typescript) (push) Canceled after 0s
Security / Secret scan (gitleaks) (push) Canceled after 0s
Security / Dependency scan (OSV-Scanner) (push) Canceled after 0s
Security / IaC/config scan (Trivy) (push) Canceled after 0s
Security / License compliance scan (Trivy) (push) Canceled after 0s
Soundboard: the trigger travels over the WebSocket and every client in the call plays the clip locally, instead of mixing it into the presser's microphone or publishing a LiveKit track. No upstream bandwidth, no media stack changes, and the clip is not degraded by voice processing. Fan-out uses a new sendToRoomParticipants rather than sendToRoom: the latter broadcasts a space room to the whole space, which is right for the presence the sidebar shows and wrong for anything audible. The cooldown is enforced server-side — a client-side one only slows down people not trying to abuse it, and a soundboard is the easiest thing here to turn into a weapon. Playing is open to anyone in the call; deciding what the buttons are needs MANAGE_SPACE. Account menu: the name in the user bar had cursor-pointer and no handler, so the interface was already promising a click that did nothing. Offers profile, status and copy-id — not the Clips or account switching the reference design shows, which would be dead UI here. Call timer: startedAt comes from the server, so a late joiner sees the call's age rather than their own arrival. Empty space rooms are destroyed already, which is what makes the next call start from zero — no reset logic needed.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useVoiceStore } from '../../stores/voiceStore';
|
||||
import { CallTimer } from './CallTimer';
|
||||
import { useSpaceStore } from '../../stores/spaceStore';
|
||||
import { useAuthStore } from '../../stores/authStore';
|
||||
import { useContextMenuStore, type ContextMenuItem } from '../../stores/contextMenuStore';
|
||||
@@ -33,6 +34,9 @@ interface VoiceChannelProps {
|
||||
|
||||
/** Wrapper component for the volume slider so it can use hooks (useState). */
|
||||
export function VoiceChannel({ channelId, channelName, onClick, locked, canManage, onSettingsClick, voiceUserHandlers, dropZone }: VoiceChannelProps) {
|
||||
// Present only while someone is in the channel; the server drops the room
|
||||
// when it empties, which is what makes the next call start from zero.
|
||||
const callStartedAt = useVoiceStore((s) => s.voiceRoomStarts.get(channelId));
|
||||
const serverVoiceUsers = useVoiceStore((s) => s.voiceUsers.get(channelId)) ?? EMPTY_VOICE_USERS;
|
||||
const currentVoiceChannel = useVoiceStore((s) => s.currentVoiceChannelId);
|
||||
const participants = useVoiceStore((s) => s.participants);
|
||||
@@ -141,6 +145,12 @@ export function VoiceChannel({ channelId, channelName, onClick, locked, canManag
|
||||
</svg>
|
||||
)}
|
||||
<span className="truncate text-[15px] font-medium flex-1 text-left">{channelName}</span>
|
||||
{callStartedAt !== undefined && (
|
||||
<CallTimer
|
||||
startedAt={callStartedAt}
|
||||
className="flex-shrink-0 text-[11px] text-txt-tertiary font-medium"
|
||||
/>
|
||||
)}
|
||||
{canManage && (
|
||||
<svg
|
||||
width="16"
|
||||
|
||||
Reference in New Issue
Block a user