feat: migrate and redesign dashboard to modern React
- Full rewrite of legacy vanilla JS UI into React SPA - Implement modern design system using Tailwind CSS and shadcn/ui primitives - Create typed API modules and hooks for voice, media, and moderation - Add new features: separated Music and Screen Share panels, Image Grid - Implement unified WebSocket hook for real-time state and PCM audio - Improve visualizer with smooth CSS transitions and live state sync - Add __dirname polyfill for ES module compatibility - Ensure responsive layout for mobile and desktop Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
3c7d722973
commit
82025a19b2
@@ -0,0 +1,96 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import type { MessageRecord } from "../types/messages";
|
||||
import type { MediaState } from "../types/media";
|
||||
import type { UIState } from "../types/ui";
|
||||
import type { ActiveSpeaker } from "../types/voice";
|
||||
|
||||
export type WebSocketStatus = "connecting" | "connected" | "disconnected" | "error";
|
||||
|
||||
export interface DashboardSocketHandlers {
|
||||
onUIState?: (state: UIState) => void;
|
||||
onUserState?: (users: ActiveSpeaker[]) => void;
|
||||
onMessageCreated?: (message: MessageRecord) => void;
|
||||
onMessageUpdated?: (message: Partial<MessageRecord> & { id: string }) => void;
|
||||
onMessageDeleted?: (message: { id: string }) => void;
|
||||
onMessageAnalyzed?: (message: MessageRecord) => void;
|
||||
onAttachmentUploaded?: () => void;
|
||||
onMediaState?: (state: MediaState) => void;
|
||||
onPcm?: (data: ArrayBuffer) => void;
|
||||
}
|
||||
|
||||
export function useDashboardSocket(handlers: DashboardSocketHandlers) {
|
||||
const [status, setStatus] = useState<WebSocketStatus>("connecting");
|
||||
const handlersRef = useRef(handlers);
|
||||
const socketRef = useRef<WebSocket | null>(null);
|
||||
|
||||
handlersRef.current = handlers;
|
||||
|
||||
useEffect(() => {
|
||||
let closed = false;
|
||||
let reconnectTimer: number | null = null;
|
||||
|
||||
const connect = () => {
|
||||
const protocol = location.protocol === "https:" ? "wss:" : "ws:";
|
||||
const socket = new WebSocket(`${protocol}//${location.host}/ws`);
|
||||
socket.binaryType = "arraybuffer";
|
||||
socketRef.current = socket;
|
||||
setStatus("connecting");
|
||||
|
||||
socket.addEventListener("open", () => setStatus("connected"));
|
||||
socket.addEventListener("error", () => setStatus("error"));
|
||||
socket.addEventListener("close", () => {
|
||||
setStatus("disconnected");
|
||||
if (!closed) reconnectTimer = window.setTimeout(connect, 2500);
|
||||
});
|
||||
socket.addEventListener("message", (event) => {
|
||||
if (event.data instanceof ArrayBuffer) {
|
||||
handlersRef.current.onPcm?.(event.data);
|
||||
return;
|
||||
}
|
||||
if (typeof event.data !== "string") return;
|
||||
try {
|
||||
const message = JSON.parse(event.data);
|
||||
switch (message.type) {
|
||||
case "ui_state":
|
||||
handlersRef.current.onUIState?.(message.state);
|
||||
break;
|
||||
case "user_state":
|
||||
handlersRef.current.onUserState?.(message.users || []);
|
||||
break;
|
||||
case "message_created":
|
||||
handlersRef.current.onMessageCreated?.(message.data);
|
||||
break;
|
||||
case "message_updated":
|
||||
handlersRef.current.onMessageUpdated?.(message.data);
|
||||
break;
|
||||
case "message_deleted":
|
||||
handlersRef.current.onMessageDeleted?.(message.data);
|
||||
break;
|
||||
case "message_analyzed":
|
||||
handlersRef.current.onMessageAnalyzed?.(message.data);
|
||||
break;
|
||||
case "attachment_uploaded":
|
||||
handlersRef.current.onAttachmentUploaded?.();
|
||||
break;
|
||||
case "media_state":
|
||||
handlersRef.current.onMediaState?.(message.state);
|
||||
break;
|
||||
}
|
||||
} catch {
|
||||
// ignore malformed socket messages
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
connect();
|
||||
|
||||
return () => {
|
||||
closed = true;
|
||||
if (reconnectTimer) window.clearTimeout(reconnectTimer);
|
||||
socketRef.current?.close();
|
||||
socketRef.current = null;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return { status, socketRef };
|
||||
}
|
||||
Reference in New Issue
Block a user