From 24c738dd19fd0193d68273a2a8cc5a1222d74bca Mon Sep 17 00:00:00 2001 From: asepharyana Date: Thu, 2 Jul 2026 03:54:44 +0700 Subject: [PATCH] Revert "fix: split LiveShell into Astro layout + individual islands" This reverts commit 6e1757410a71846f985560fb7c16ed157e6e5d0e. --- .gitmodules | 13 ++ services/frontend/src/islands/LiveShell.tsx | 142 ++++++++++++++++++ .../frontend/src/islands/VoiceControls.tsx | 44 ++---- services/frontend/src/pages/live.astro | 106 +------------ 4 files changed, 174 insertions(+), 131 deletions(-) create mode 100644 .gitmodules create mode 100644 services/frontend/src/islands/LiveShell.tsx diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..dcf5fed --- /dev/null +++ b/.gitmodules @@ -0,0 +1,13 @@ +[submodule "vendor/discord.js-selfbot-v13"] + path = vendor/discord.js-selfbot-v13 + url = https://github.com/MythEclipse/discord.js-selfbot-v13.git + +[submodule "vendor/discord-video-stream"] + path = vendor/discord-video-stream + url = https://github.com/MythEclipse/discord-video-stream.git +[submodule "vendor/drizzle-orm"] + path = vendor/drizzle-orm + url = https://github.com/MythEclipse/drizzle-orm.git +[submodule "vendor/better-sqlite3"] + path = vendor/better-sqlite3 + url = https://github.com/MythEclipse/better-sqlite3.git diff --git a/services/frontend/src/islands/LiveShell.tsx b/services/frontend/src/islands/LiveShell.tsx new file mode 100644 index 0000000..4c8f7ad --- /dev/null +++ b/services/frontend/src/islands/LiveShell.tsx @@ -0,0 +1,142 @@ +// ─── LiveShell.tsx — Page shell for /live ──────────────────────────────────── +// Composes Sidebar, Header, ParticleBackground, and the four live islands +// (VoiceControls, ActiveSpeakers, AudioVisualizer, NowPlaying) into a full-page +// responsive layout. +// ───────────────────────────────────────────────────────────────────────────── + +import { useCallback, useEffect, useState } from "react"; +import { + getGuilds, + getVoiceChannels, + getVoiceStatus, +} from "../shared/api/client.js"; +import { Header } from "../shared/components/Header.js"; +import { + Card, + CardContent, + CardHeader, + CardTitle, +} from "../shared/components/index.js"; +import { ParticleBackground } from "../shared/components/particles/ParticleBackground.js"; +import { Sidebar } from "../shared/components/Sidebar.js"; +import { useTheme } from "../shared/hooks/useTheme.js"; +import type { Channel, Guild } from "../shared/types/guild.js"; +import type { VoiceStatus } from "../shared/types/voice.js"; +import { useVoiceStore } from "../stores/voice-store.js"; +import ActiveSpeakers from "./ActiveSpeakers.js"; +import AudioVisualizer from "./AudioVisualizer.js"; +import NowPlaying from "./NowPlaying.js"; +import VoiceControls from "./VoiceControls.js"; + +const emptyVoiceStatus: VoiceStatus = { + connected: false, + activeGuildId: null, + activeChannelId: null, + activeChannelName: null, + connections: [], +}; + +export default function LiveShell() { + const { mode, isDark, toggle: toggleTheme } = useTheme(); + const [voiceStatus, setVoiceStatus] = useState(emptyVoiceStatus); + const [guilds, setGuilds] = useState([]); + const [voiceChannels, setVoiceChannels] = useState([]); + + // Watch store for guild changes so we can fetch voice channels + const guildId = useVoiceStore((state) => state.guildId); + + // Fetch initial data on mount + useEffect(() => { + getVoiceStatus() + .then(setVoiceStatus) + .catch(() => { + /* offline — leave default */ + }); + getGuilds() + .then(setGuilds) + .catch(() => { + /* offline — leave default */ + }); + }, []); + + // Refetch voice channels when guild selection changes + useEffect(() => { + if (guildId) { + getVoiceChannels(guildId) + .then(setVoiceChannels) + .catch(() => setVoiceChannels([])); + } else { + setVoiceChannels([]); + } + }, [guildId]); + + const handleTabChange = useCallback((tab: string) => { + if (tab !== "live") { + window.location.href = "/"; + } + }, []); + + return ( +
+ {/* Background layers */} + + + ); +} diff --git a/services/frontend/src/islands/VoiceControls.tsx b/services/frontend/src/islands/VoiceControls.tsx index 7d44af3..daa86aa 100644 --- a/services/frontend/src/islands/VoiceControls.tsx +++ b/services/frontend/src/islands/VoiceControls.tsx @@ -1,47 +1,29 @@ // ─── VoiceControls.tsx — Voice bridge island ──────────────────────────────── -// Self-contained: fetches guilds/channels internally, reads/writes useVoiceStore, -// calls API for connect/disconnect. +// Self-contained: reads/writes useVoiceStore, calls API for connect/disconnect. // ───────────────────────────────────────────────────────────────────────────── import { Radio } from "lucide-react"; -import { useCallback, useEffect, useState } from "react"; -import { - connectVoice, - disconnectVoice, - getGuilds, - getVoiceChannels, -} from "../shared/api/client.js"; +import { useCallback, useState } from "react"; +import { connectVoice, disconnectVoice } from "../shared/api/client.js"; import { Button, Select } from "../shared/components/index.js"; import type { Channel, Guild } from "../shared/types/guild.js"; import { useVoiceStore } from "../stores/voice-store.js"; -export default function VoiceControls() { +interface VoiceControlsProps { + guilds: Guild[]; + voiceChannels: Channel[]; +} + +export default function VoiceControls({ + guilds, + voiceChannels, +}: VoiceControlsProps) { const connected = useVoiceStore((state) => state.connected); const guildId = useVoiceStore((state) => state.guildId); const channelId = useVoiceStore((state) => state.channelId); const setGuildChannel = useVoiceStore((state) => state.setGuildChannel); const setConnected = useVoiceStore((state) => state.setConnected); const [loading, setLoading] = useState(false); - const [guilds, setGuilds] = useState([]); - const [voiceChannels, setVoiceChannels] = useState([]); - - // Fetch guilds on mount - useEffect(() => { - getGuilds() - .then(setGuilds) - .catch(() => { /* offline */ }); - }, []); - - // Fetch voice channels when guild selection changes - useEffect(() => { - if (guildId) { - getVoiceChannels(guildId) - .then(setVoiceChannels) - .catch(() => setVoiceChannels([])); - } else { - setVoiceChannels([]); - } - }, [guildId]); const handleConnect = useCallback(async () => { if (!guildId || !channelId) return; @@ -69,7 +51,7 @@ export default function VoiceControls() { }, [setConnected]); return ( -
+

Voice Bridge

diff --git a/services/frontend/src/pages/live.astro b/services/frontend/src/pages/live.astro index 30ce86d..ed35ea6 100644 --- a/services/frontend/src/pages/live.astro +++ b/services/frontend/src/pages/live.astro @@ -1,108 +1,14 @@ --- // ─── live.astro — Voice & Media live monitoring page ──────────────────────── -// Layout shell (Sidebar, Header) rendered as zero-JS Astro components. -// Only the truly interactive parts are React islands with client directives. +// Full-page layout with Sidebar navigation, Header, and the LiveShell React +// island that composes all live components (VoiceControls, ActiveSpeakers, +// AudioVisualizer, NowPlaying). +// Particles and MascotChat are embedded inside the LiveShell island. // ──────────────────────────────────────────────────────────────────────────── import BaseLayout from "../layouts/BaseLayout.astro"; -import Sidebar from "../components/sidebar/Sidebar.astro"; -import Header from "../components/header/Header.astro"; -import Card from "../components/ui/Card.astro"; - -// React islands — interactive only -import ThemeToggle from "../islands/ThemeToggle"; -import VoiceControls from "../islands/VoiceControls"; -import ActiveSpeakers from "../islands/ActiveSpeakers"; -import AudioVisualizer from "../islands/AudioVisualizer"; -import NowPlaying from "../islands/NowPlaying"; -import Particles from "../islands/Particles"; -import MascotChat from "../islands/MascotChat"; +import LiveShell from "../islands/LiveShell"; --- -
- - - -
- -
- -
- -
- -
- -
- - -

Voice Connection

- -
- - - -

Live Audio

- -
- - - -

Now Playing

- -
-
- - - -
-
-
-
- - - - +
- -