diff --git a/js/web/src/components/dashboard/dashboard-metrics.tsx b/js/web/src/components/dashboard/dashboard-metrics.tsx index 9358bb1d..90ccb2ac 100644 --- a/js/web/src/components/dashboard/dashboard-metrics.tsx +++ b/js/web/src/components/dashboard/dashboard-metrics.tsx @@ -8,7 +8,6 @@ import { type ReactNode, } from "react"; import { useStore } from "zustand"; -import { useShallow } from "zustand/react/shallow"; import { useDashboardStore } from "./dashboard-store-context"; /** How often we sample the live metrics into the history buffer. */ @@ -97,57 +96,39 @@ export function DashboardMetricsProvider({ children }: ProviderProps) { const currentBitrateRef = useRef(0); const currentSegmentTimingRef = useRef(0); const currentFpsRef = useRef(0); - const latestInputsRef = useRef({ chat: [] as any[], viewers: 0 }); - latestInputsRef.current = { - chat: store.getState().chat, - viewers: store.getState().viewers ?? 0, - }; - - // Subscribe to the store so the refs stay current. We don't render off - // of these — they're read inside the sampling interval. - const storeState = useStore( - store, - useShallow((s) => ({ - segment: s.segment, - livestream: s.livestream, - chat: s.chat, - viewers: s.viewers, - })), - ); + + // Subscribe to the slice so the effect deps below fire when segments + // change. The effects themselves only write to refs, so we don't render + // off the segment subscription. `livestream` is read for `hasLivestream`. + const segment = useStore(store, (s) => s.segment); + const livestream = useStore(store, (s) => s.livestream); // Update bitrate from the current segment. useEffect(() => { - const seg = store.getState().segment; - if (!seg?.size || !seg?.duration) return; - const kbps = (seg.size * 8) / (seg.duration / 1_000_000_000) / 1000; + if (!segment?.size || !segment?.duration) return; + const kbps = (segment.size * 8) / (segment.duration / 1_000_000_000) / 1000; currentBitrateRef.current = kbps; - }, [ - store, - store.getState().segment?.size, - store.getState().segment?.duration, - ]); + }, [segment?.size, segment?.duration]); // Update FPS from the current segment's video track. useEffect(() => { - const seg = store.getState().segment; - const videoTrack = seg?.video?.[0]; + const videoTrack = segment?.video?.[0]; if (!videoTrack?.framerate) return; const { num, den } = videoTrack.framerate; if (!den) return; currentFpsRef.current = num / den; - }, [store, store.getState().segment?.video]); + }, [segment?.video]); // Update segment timing (ms since previous segment). const lastSegmentAtRef = useRef(null); useEffect(() => { - const seg = store.getState().segment; - if (!seg) return; + if (!segment) return; const now = Date.now(); if (lastSegmentAtRef.current !== null) { currentSegmentTimingRef.current = now - lastSegmentAtRef.current; } lastSegmentAtRef.current = now; - }, [store, store.getState().segment]); + }, [segment]); // When the user changes the range, resize the history buffer. The window // then refills as new samples arrive. @@ -195,9 +176,9 @@ export function DashboardMetricsProvider({ children }: ProviderProps) { history, range, setRange, - hasLivestream: !!storeState.livestream, + hasLivestream: !!livestream, }), - [history, range, storeState.livestream], + [history, range, livestream], ); return ( diff --git a/js/web/src/lib/useSonare.ts b/js/web/src/lib/useSonare.ts index 0b833384..fce4cbda 100644 --- a/js/web/src/lib/useSonare.ts +++ b/js/web/src/lib/useSonare.ts @@ -17,18 +17,18 @@ function generateSonare(length: number, suffixLength: number) { return word; } -// Sonare-based random ID generator. It generates three words, -// separated by dashes, and optionally adds a random suffix of specified length. +// Sonare-based random ID generator. It generates a string of N words +// separated by dashes, optionally with a random suffix of specified length. // Note that THIS IS NOT A CRYPTOGRAPHICALLY SECURE ID GENERATOR. export function useSonare( - { length: number, suffixLength } = { length: 2, suffixLength: 4 }, + { length, suffixLength } = { length: 2, suffixLength: 4 }, ) { - let [sona, setSona] = useState(() => { - return generateSonare(number, suffixLength); + const [sona, setSona] = useState(() => { + return generateSonare(length, suffixLength); }); const regenerate = () => { - setSona(generateSonare(number, suffixLength)); + setSona(generateSonare(length, suffixLength)); }; return [sona, regenerate] as const;