From 32055f5762515aaca867c2766d39ccd5a2f466ae Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 22 Aug 2025 22:54:05 +0000 Subject: [PATCH] Cleanup: remove polling fallback for /audio/mute status --- ui/src/components/ActionBar.tsx | 35 +----- .../popovers/AudioControlPopover.tsx | 107 ++---------------- web.go | 4 - 3 files changed, 16 insertions(+), 130 deletions(-) diff --git a/ui/src/components/ActionBar.tsx b/ui/src/components/ActionBar.tsx index 956d488..97c9c91 100644 --- a/ui/src/components/ActionBar.tsx +++ b/ui/src/components/ActionBar.tsx @@ -2,7 +2,7 @@ import { MdOutlineContentPasteGo, MdVolumeOff, MdVolumeUp, MdGraphicEq } from "r import { LuCable, LuHardDrive, LuMaximize, LuSettings, LuSignal } from "react-icons/lu"; import { FaKeyboard } from "react-icons/fa6"; import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react"; -import { Fragment, useCallback, useEffect, useRef, useState } from "react"; +import { Fragment, useCallback, useRef } from "react"; import { CommandLineIcon } from "@heroicons/react/20/solid"; import { Button } from "@components/Button"; @@ -21,7 +21,7 @@ import ExtensionPopover from "@/components/popovers/ExtensionPopover"; import AudioControlPopover from "@/components/popovers/AudioControlPopover"; import { useDeviceUiNavigation } from "@/hooks/useAppNavigation"; import { useAudioEvents } from "@/hooks/useAudioEvents"; -import api from "@/api"; + // Type for microphone error interface MicrophoneError { @@ -83,35 +83,10 @@ export default function Actionbar({ ); // Use WebSocket-based audio events for real-time updates - const { audioMuted, isConnected } = useAudioEvents(); + const { audioMuted } = useAudioEvents(); - // Fallback to polling if WebSocket is not connected - const [fallbackMuted, setFallbackMuted] = useState(false); - useEffect(() => { - if (!isConnected) { - // Load initial state - api.GET("/audio/mute").then(async resp => { - if (resp.ok) { - const data = await resp.json(); - setFallbackMuted(!!data.muted); - } - }); - - // Fallback polling when WebSocket is not available - const interval = setInterval(async () => { - const resp = await api.GET("/audio/mute"); - if (resp.ok) { - const data = await resp.json(); - setFallbackMuted(!!data.muted); - } - }, 1000); - - return () => clearInterval(interval); - } - }, [isConnected]); - - // Use WebSocket data when available, fallback to polling data otherwise - const isMuted = isConnected && audioMuted !== null ? audioMuted : fallbackMuted; + // Use WebSocket data exclusively - no polling fallback + const isMuted = audioMuted ?? false; // Default to false if WebSocket data not available yet return ( diff --git a/ui/src/components/popovers/AudioControlPopover.tsx b/ui/src/components/popovers/AudioControlPopover.tsx index e9d29d1..200d5a1 100644 --- a/ui/src/components/popovers/AudioControlPopover.tsx +++ b/ui/src/components/popovers/AudioControlPopover.tsx @@ -41,23 +41,7 @@ interface AudioConfig { FrameSize: string; } -interface AudioMetrics { - frames_received: number; - frames_dropped: number; - bytes_processed: number; - last_frame_time: string; - connection_drops: number; - average_latency: string; -} -interface MicrophoneMetrics { - frames_sent: number; - frames_dropped: number; - bytes_processed: number; - last_frame_time: string; - connection_drops: number; - average_latency: string; -} @@ -94,11 +78,7 @@ export default function AudioControlPopover({ microphone, open }: AudioControlPo isConnected: wsConnected } = useAudioEvents(); - // Fallback state for when WebSocket is not connected - const [fallbackMuted, setFallbackMuted] = useState(false); - const [fallbackMetrics, setFallbackMetrics] = useState(null); - const [fallbackMicMetrics, setFallbackMicMetrics] = useState(null); - const [fallbackConnected, setFallbackConnected] = useState(false); + // WebSocket-only implementation - no fallback polling // Microphone state from props const { @@ -115,11 +95,11 @@ export default function AudioControlPopover({ microphone, open }: AudioControlPo isToggling, } = microphone; - // Use WebSocket data when available, fallback to polling data otherwise - const isMuted = wsConnected && audioMuted !== null ? audioMuted : fallbackMuted; - const metrics = wsConnected && audioMetrics !== null ? audioMetrics : fallbackMetrics; - const micMetrics = wsConnected && microphoneMetrics !== null ? microphoneMetrics : fallbackMicMetrics; - const isConnected = wsConnected ? wsConnected : fallbackConnected; + // Use WebSocket data exclusively - no polling fallback + const isMuted = audioMuted ?? false; + const metrics = audioMetrics; + const micMetrics = microphoneMetrics; + const isConnected = wsConnected; // Audio level monitoring - enable only when popover is open and microphone is active to save resources const analysisEnabled = (open ?? true) && isMicrophoneActive; @@ -150,34 +130,15 @@ export default function AudioControlPopover({ microphone, open }: AudioControlPo } }, [configsLoaded]); - // Optimize fallback polling - only run when WebSocket is not connected + // WebSocket-only implementation - sync microphone state when needed useEffect(() => { - if (!wsConnected && !configsLoaded) { - // Load state once if configs aren't loaded yet - loadAudioState(); - } - - if (!wsConnected) { - loadAudioMetrics(); - loadMicrophoneMetrics(); - - // Reduced frequency for fallback polling (every 3 seconds instead of 2) - const metricsInterval = setInterval(() => { - if (!wsConnected) { // Double-check to prevent unnecessary requests - loadAudioMetrics(); - loadMicrophoneMetrics(); - } - }, 3000); - return () => clearInterval(metricsInterval); - } - // Always sync microphone state, but debounce it const syncTimeout = setTimeout(() => { syncMicrophoneState(); }, 500); return () => clearTimeout(syncTimeout); - }, [wsConnected, syncMicrophoneState, configsLoaded]); + }, [syncMicrophoneState]); const loadAudioConfigurations = async () => { try { @@ -203,60 +164,14 @@ export default function AudioControlPopover({ microphone, open }: AudioControlPo } }; - const loadAudioState = async () => { - try { - // Load mute state only (configurations are loaded separately) - const muteResp = await api.GET("/audio/mute"); - if (muteResp.ok) { - const muteData = await muteResp.json(); - setFallbackMuted(!!muteData.muted); - } - } catch (error) { - console.error("Failed to load audio state:", error); - } - }; - - const loadAudioMetrics = async () => { - try { - const resp = await api.GET("/audio/metrics"); - if (resp.ok) { - const data = await resp.json(); - setFallbackMetrics(data); - // Consider connected if API call succeeds, regardless of frame count - setFallbackConnected(true); - } else { - setFallbackConnected(false); - } - } catch (error) { - console.error("Failed to load audio metrics:", error); - setFallbackConnected(false); - } - }; - - - - const loadMicrophoneMetrics = async () => { - try { - const resp = await api.GET("/microphone/metrics"); - if (resp.ok) { - const data = await resp.json(); - setFallbackMicMetrics(data); - } - } catch (error) { - console.error("Failed to load microphone metrics:", error); - } - }; - const handleToggleMute = async () => { setIsLoading(true); try { const resp = await api.POST("/audio/mute", { muted: !isMuted }); - if (resp.ok) { - // WebSocket will handle the state update, but update fallback for immediate feedback - if (!wsConnected) { - setFallbackMuted(!isMuted); - } + if (!resp.ok) { + console.error("Failed to toggle mute:", resp.statusText); } + // WebSocket will handle the state update automatically } catch (error) { console.error("Failed to toggle mute:", error); } finally { diff --git a/web.go b/web.go index beccf32..20e0f04 100644 --- a/web.go +++ b/web.go @@ -159,10 +159,6 @@ func setupRouter() *gin.Engine { protected.POST("/storage/upload", handleUploadHttp) } - protected.GET("/audio/mute", func(c *gin.Context) { - c.JSON(200, gin.H{"muted": audio.IsAudioMuted()}) - }) - protected.POST("/audio/mute", func(c *gin.Context) { type muteReq struct { Muted bool `json:"muted"`