From 08cd515d8a2dfeadc67a66267132928bb47279b6 Mon Sep 17 00:00:00 2001 From: MythEclipse Date: Thu, 14 May 2026 19:46:47 +0700 Subject: [PATCH] fix: preserve ui state broadcasts in routes --- src/routes/voiceRoutes.ts | 50 ++++++++++++++++++++++++++++++++++++++- src/webserver.ts | 11 +++++++-- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/routes/voiceRoutes.ts b/src/routes/voiceRoutes.ts index fba4a7f..b1726b3 100644 --- a/src/routes/voiceRoutes.ts +++ b/src/routes/voiceRoutes.ts @@ -3,12 +3,40 @@ import express from "express"; import { AppError } from "../errors"; import { createChildLogger } from "../logger"; import type { VoiceController } from "../voiceController"; +import type { ModerationBroadcaster } from "../moderation/broadcaster"; +import type { SharedUIState } from "./uiStateRoutes"; const logger = createChildLogger("voice-routes"); -export function createVoiceRoutes(voiceController: VoiceController): Router { +export interface VoiceRouteOptions { + voiceController: VoiceController; + patchSharedUIState: (patch: Partial) => SharedUIState; + broadcaster: ModerationBroadcaster; +} + +export function createVoiceRoutes( + options: VoiceRouteOptions | VoiceController, +): Router { const router = express.Router(); + // Support both old signature (VoiceController) and new signature (options object) + let voiceController: VoiceController; + let patchSharedUIState: + | ((patch: Partial) => SharedUIState) + | undefined; + let broadcaster: ModerationBroadcaster | undefined; + + if ("connect" in options && "disconnect" in options) { + // Old signature: just VoiceController + voiceController = options as VoiceController; + } else { + // New signature: options object + const opts = options as VoiceRouteOptions; + voiceController = opts.voiceController; + patchSharedUIState = opts.patchSharedUIState; + broadcaster = opts.broadcaster; + } + // GET /api/status - Get voice connection status router.get("/status", (_req, res, next) => { try { @@ -96,6 +124,16 @@ export function createVoiceRoutes(voiceController: VoiceController): Router { logger.info({ guildId, channelId }, "Connecting to voice channel"); const status = await voiceController.connect(guildId, channelId); + + // Update UI state and broadcast to connected clients + if (patchSharedUIState && broadcaster) { + const updatedState = patchSharedUIState({ + selectedGuild: guildId, + selectedVoiceChannel: channelId, + }); + broadcaster.uiState(updatedState); + } + res.json(status); } catch (error) { next(error); @@ -108,6 +146,16 @@ export function createVoiceRoutes(voiceController: VoiceController): Router { logger.info("Disconnecting from voice channel"); const status = await voiceController.disconnect(); + + // Update UI state and broadcast to connected clients + if (patchSharedUIState && broadcaster) { + const updatedState = patchSharedUIState({ + selectedGuild: "", + selectedVoiceChannel: "", + }); + broadcaster.uiState(updatedState); + } + res.json(status); } catch (error) { next(error); diff --git a/src/webserver.ts b/src/webserver.ts index 3b93c9e..dd2fdd7 100644 --- a/src/webserver.ts +++ b/src/webserver.ts @@ -182,7 +182,14 @@ export async function startWebserver( "/api", createUIStateRoutes({ getSharedUIState, patchSharedUIState }), ); - app.use("/api", createVoiceRoutes(voiceController)); + app.use( + "/api", + createVoiceRoutes({ + voiceController, + patchSharedUIState, + broadcaster, + }), + ); app.use("/api", createMessageRoutes()); app.use("/api", createAnalysisRoutes()); app.use("/api", createSyncRoutes(_client)); @@ -197,7 +204,7 @@ export async function startWebserver( const header = Buffer.alloc(4); header.writeInt32LE(hash, 0); const packet = Buffer.concat([header, chunk]); - for (const client of broadcaster.getClients?.() || []) { + for (const client of broadcaster.getClients()) { if (client.readyState === 1) client.send(packet); } };