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,109 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import {
|
||||
connectVoice,
|
||||
disconnectVoice,
|
||||
getGuilds,
|
||||
getTextChannels,
|
||||
getThreads,
|
||||
getVoiceChannels,
|
||||
getVoiceStatus,
|
||||
} from "../api/voice";
|
||||
import type { Channel, Guild, VoiceStatus } from "../types/voice";
|
||||
|
||||
export function useVoiceControl() {
|
||||
const [guilds, setGuilds] = useState<Guild[]>([]);
|
||||
const [voiceChannels, setVoiceChannels] = useState<Channel[]>([]);
|
||||
const [textChannels, setTextChannels] = useState<Channel[]>([]);
|
||||
const [voiceStatus, setVoiceStatus] = useState<VoiceStatus>({ connected: false });
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const refreshGuilds = useCallback(async () => {
|
||||
setError(null);
|
||||
const nextGuilds = await getGuilds();
|
||||
setGuilds(nextGuilds);
|
||||
return nextGuilds;
|
||||
}, []);
|
||||
|
||||
const refreshVoiceStatus = useCallback(async () => {
|
||||
const status = await getVoiceStatus();
|
||||
setVoiceStatus(status);
|
||||
return status;
|
||||
}, []);
|
||||
|
||||
const loadVoiceChannels = useCallback(async (guildId: string) => {
|
||||
if (!guildId) {
|
||||
setVoiceChannels([]);
|
||||
return [];
|
||||
}
|
||||
const channels = await getVoiceChannels(guildId);
|
||||
setVoiceChannels(channels);
|
||||
return channels;
|
||||
}, []);
|
||||
|
||||
const loadTextTargets = useCallback(async (guildId: string) => {
|
||||
if (!guildId) {
|
||||
setTextChannels([]);
|
||||
return [];
|
||||
}
|
||||
const [channels, threads] = await Promise.all([
|
||||
getTextChannels(guildId),
|
||||
getThreads(guildId).catch(() => []),
|
||||
]);
|
||||
const combined = [...channels, ...threads];
|
||||
setTextChannels(combined);
|
||||
return combined;
|
||||
}, []);
|
||||
|
||||
const joinVoice = useCallback(async (guildId: string, channelId: string) => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const status = await connectVoice(guildId, channelId);
|
||||
setVoiceStatus(status);
|
||||
return status;
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
setError(message);
|
||||
throw err;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const leaveVoice = useCallback(async () => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const status = await disconnectVoice();
|
||||
setVoiceStatus(status);
|
||||
return status;
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
setError(message);
|
||||
throw err;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
refreshGuilds().catch((err) => setError(err instanceof Error ? err.message : String(err)));
|
||||
refreshVoiceStatus().catch((err) => setError(err instanceof Error ? err.message : String(err)));
|
||||
}, [refreshGuilds, refreshVoiceStatus]);
|
||||
|
||||
return {
|
||||
guilds,
|
||||
voiceChannels,
|
||||
textChannels,
|
||||
voiceStatus,
|
||||
loading,
|
||||
error,
|
||||
refreshGuilds,
|
||||
refreshVoiceStatus,
|
||||
loadVoiceChannels,
|
||||
loadTextTargets,
|
||||
joinVoice,
|
||||
leaveVoice,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user