From 185ee050287960148c1017a2fcfcc3f84a6da5c2 Mon Sep 17 00:00:00 2001 From: MrAlders0n Date: Fri, 29 May 2026 23:13:18 -0400 Subject: [PATCH] Small bug fixes --- src/api/ws-manager.ts | 6 +++++- src/components/Dropdown.tsx | 11 ++++++++++- src/components/SplashScreen.tsx | 8 ++++++-- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/api/ws-manager.ts b/src/api/ws-manager.ts index b9f5081..c5fdffb 100644 --- a/src/api/ws-manager.ts +++ b/src/api/ws-manager.ts @@ -24,6 +24,7 @@ export class WsManager { private subscriptionId: string | null = null; private status: WsStatus = "disconnected"; private reconnectAttempt = 0; + private intentionalClose = false; private reconnectTimer: ReturnType | null = null; private pingTimer: ReturnType | null = null; private msgCounter = 0; @@ -111,6 +112,8 @@ export class WsManager { } disconnect(): void { + this.intentionalClose = true; + this.reconnectAttempt = 0; this.clearTimers(); this.ws?.close(); this.ws = null; @@ -120,6 +123,7 @@ export class WsManager { // exponential backoff w/ jitter to avoid thundering herd on reconnect private doConnect(): void { + this.intentionalClose = false; this.clearTimers(); this.setStatus("connecting"); @@ -142,7 +146,7 @@ export class WsManager { this.ws.onclose = (e: CloseEvent) => { this.clearTimers(); - if (e.code === 1000) { + if (this.intentionalClose || e.code === 1000) { this.setStatus("disconnected"); return; } diff --git a/src/components/Dropdown.tsx b/src/components/Dropdown.tsx index 23fc2d5..33edb8b 100644 --- a/src/components/Dropdown.tsx +++ b/src/components/Dropdown.tsx @@ -1,4 +1,4 @@ -import { useState, useRef, useCallback, type ReactNode } from "react"; +import { useState, useRef, useCallback, useEffect, type ReactNode } from "react"; import { useClickOutside } from "../hooks/useClickOutside"; export function Dropdown({ renderTrigger, align = "right", width = "w-48", children }: { @@ -13,6 +13,15 @@ export function Dropdown({ renderTrigger, align = "right", width = "w-48", child const toggle = useCallback(() => setOpen((v) => !v), []); useClickOutside(ref, open, close); + useEffect(() => { + if (!open) return; + function handleKey(e: KeyboardEvent) { + if (e.key === "Escape") close(); + } + document.addEventListener("keydown", handleKey); + return () => document.removeEventListener("keydown", handleKey); + }, [open, close]); + return (
{renderTrigger({ open, toggle })} diff --git a/src/components/SplashScreen.tsx b/src/components/SplashScreen.tsx index bd230d1..6230e23 100644 --- a/src/components/SplashScreen.tsx +++ b/src/components/SplashScreen.tsx @@ -15,8 +15,12 @@ export function SplashScreen() { // Synchronous gate: decided before first paint so StrictMode's double-mount // (and any same-session reload) never re-shows it. const [render, setRender] = useState(() => { - if (typeof sessionStorage === "undefined") return false; - return sessionStorage.getItem(SPLASH_KEY) !== "1"; + try { + if (typeof sessionStorage === "undefined") return false; + return sessionStorage.getItem(SPLASH_KEY) !== "1"; + } catch { + return false; + } }); const [fading, setFading] = useState(false);