diff --git a/services/frontend/src/app/(dashboard)/voice/page.tsx b/services/frontend/src/app/(dashboard)/voice/page.tsx index 152cacf..b7f950c 100644 --- a/services/frontend/src/app/(dashboard)/voice/page.tsx +++ b/services/frontend/src/app/(dashboard)/voice/page.tsx @@ -148,7 +148,7 @@ export default function VoicePage() { )} - {tab === "activity" && } + {tab === "activity" && } ); } diff --git a/services/frontend/src/components/voice/activity-timeline.tsx b/services/frontend/src/components/voice/activity-timeline.tsx index 7ff2691..930dab7 100644 --- a/services/frontend/src/components/voice/activity-timeline.tsx +++ b/services/frontend/src/components/voice/activity-timeline.tsx @@ -1,22 +1,30 @@ "use client"; -import { - Bar, - BarChart, - ResponsiveContainer, - Tooltip, - XAxis, - YAxis, -} from "recharts"; +import { Mic, MicOff } from "lucide-react"; import { GlassCard } from "@/components/glass/card"; -import { useMounted } from "@/lib/hooks/use-mounted"; +import type { ActiveSpeaker } from "@/lib/types"; interface ActivityTimelineProps { - data?: { user: string; duration: number }[]; + data?: ActiveSpeaker[]; } +/** + * Voice Activity — live view of everyone currently in the monitored voice + * channel and whether they are speaking right now. + * + * Previously this rendered a Recharts bar chart fed from a `{user, duration}` + * prop that NO caller ever supplied, so the Activity tab always showed an + * empty, misleading chart. It now renders real live speaker state from the + * WebSocket (same source as the Connection tab's waveform). + */ export function VoiceActivityTimeline({ data = [] }: ActivityTimelineProps) { - const mounted = useMounted(); + const sorted = [...data].sort((a, b) => + a.speaking === b.speaking + ? String(a.username).localeCompare(b.username) + : a.speaking + ? -1 + : 1, + ); return ( @@ -24,54 +32,53 @@ export function VoiceActivityTimeline({ data = [] }: ActivityTimelineProps) { Voice Activity + + {sorted.length} speaker{sorted.length !== 1 ? "s" : ""} · live + -
- {mounted ? ( - - - - - [ - `${(Number(value) / 60).toFixed(1)}m`, - "Duration", - ]} - /> - - - - ) : ( -
- )} -
+ + {sorted.length === 0 ? ( +
+ +

+ No speakers in the monitored voice channel. +

+

+ Connect to a voice channel to see live activity here. +

+
+ ) : ( +
+ {sorted.map((s) => ( +
+ {s.speaking ? ( + + ) : ( + + )} + + {s.username} + + + {s.speaking ? "Speaking" : "Listening"} + +
+ ))} +
+ )} ); }