From ea68e4cfc3b979afe109367f6ab7405c2783edde Mon Sep 17 00:00:00 2001 From: Galactic99 Date: Thu, 16 Apr 2026 19:22:57 +0530 Subject: [PATCH] fix:prevent stale countdown IPC updates from repainting overlay --- electron/electron-env.d.ts | 6 ++-- electron/ipc/handlers.ts | 16 +++++++++-- electron/preload.ts | 12 ++++---- src/components/launch/CountdownOverlay.tsx | 5 +++- src/hooks/useScreenRecorder.ts | 32 ++++++++++++++-------- 5 files changed, 47 insertions(+), 24 deletions(-) diff --git a/electron/electron-env.d.ts b/electron/electron-env.d.ts index 4716472..e20cf7f 100644 --- a/electron/electron-env.d.ts +++ b/electron/electron-env.d.ts @@ -135,9 +135,9 @@ interface Window { saveShortcuts: (shortcuts: unknown) => Promise<{ success: boolean; error?: string }>; hudOverlayHide: () => void; hudOverlayClose: () => void; - showCountdownOverlay: (value: number) => Promise; - setCountdownOverlayValue: (value: number) => Promise; - hideCountdownOverlay: () => Promise; + showCountdownOverlay: (value: number, runId: number) => Promise; + setCountdownOverlayValue: (value: number, runId: number) => Promise; + hideCountdownOverlay: (runId: number) => Promise; onCountdownOverlayValue: (callback: (value: number | null) => void) => () => void; setMicrophoneExpanded: (expanded: boolean) => void; setHasUnsavedChanges: (hasChanges: boolean) => void; diff --git a/electron/ipc/handlers.ts b/electron/ipc/handlers.ts index fdabf6c..729408f 100644 --- a/electron/ipc/handlers.ts +++ b/electron/ipc/handlers.ts @@ -362,6 +362,7 @@ export function registerIpcHandlers( const countdownOverlayState = { visible: false, value: null as number | null, + activeRunId: null as number | null, }; const flushCountdownOverlayState = (win: BrowserWindow) => { @@ -379,7 +380,8 @@ export function registerIpcHandlers( } }; - ipcMain.handle("countdown-overlay-show", (_, value: number) => { + ipcMain.handle("countdown-overlay-show", (_, value: number, runId: number) => { + countdownOverlayState.activeRunId = runId; countdownOverlayState.visible = true; countdownOverlayState.value = value; @@ -399,7 +401,11 @@ export function registerIpcHandlers( } }); - ipcMain.handle("countdown-overlay-set-value", (_, value: number) => { + ipcMain.handle("countdown-overlay-set-value", (_, value: number, runId: number) => { + if (countdownOverlayState.activeRunId !== runId || !countdownOverlayState.visible) { + return; + } + countdownOverlayState.value = value; const win = getCountdownOverlayWindow(); @@ -414,7 +420,11 @@ export function registerIpcHandlers( win.webContents.send("countdown-overlay-value", value); }); - ipcMain.handle("countdown-overlay-hide", () => { + ipcMain.handle("countdown-overlay-hide", (_, runId: number) => { + if (countdownOverlayState.activeRunId !== runId) { + return; + } + countdownOverlayState.visible = false; countdownOverlayState.value = null; diff --git a/electron/preload.ts b/electron/preload.ts index 77b7d99..6aa066f 100644 --- a/electron/preload.ts +++ b/electron/preload.ts @@ -130,14 +130,14 @@ contextBridge.exposeInMainWorld("electronAPI", { setHasUnsavedChanges: (hasChanges: boolean) => { ipcRenderer.send("set-has-unsaved-changes", hasChanges); }, - showCountdownOverlay: (value: number) => { - return ipcRenderer.invoke("countdown-overlay-show", value); + showCountdownOverlay: (value: number, runId: number) => { + return ipcRenderer.invoke("countdown-overlay-show", value, runId); }, - setCountdownOverlayValue: (value: number) => { - return ipcRenderer.invoke("countdown-overlay-set-value", value); + setCountdownOverlayValue: (value: number, runId: number) => { + return ipcRenderer.invoke("countdown-overlay-set-value", value, runId); }, - hideCountdownOverlay: () => { - return ipcRenderer.invoke("countdown-overlay-hide"); + hideCountdownOverlay: (runId: number) => { + return ipcRenderer.invoke("countdown-overlay-hide", runId); }, onCountdownOverlayValue: (callback: (value: number | null) => void) => { const listener = (_event: unknown, value: number | null) => callback(value); diff --git a/src/components/launch/CountdownOverlay.tsx b/src/components/launch/CountdownOverlay.tsx index a3a149d..1ee5c97 100644 --- a/src/components/launch/CountdownOverlay.tsx +++ b/src/components/launch/CountdownOverlay.tsx @@ -14,7 +14,10 @@ export function CountdownOverlay() { return (
{value === null ? null : ( -
+
{value}
)} diff --git a/src/hooks/useScreenRecorder.ts b/src/hooks/useScreenRecorder.ts index 6a17c1e..51c57dd 100644 --- a/src/hooks/useScreenRecorder.ts +++ b/src/hooks/useScreenRecorder.ts @@ -336,9 +336,10 @@ export function useScreenRecorder(): UseScreenRecorderReturn { } return () => { + const activeRunId = countdownRunId.current; if (cleanup) cleanup(); countdownRunId.current += 1; - void window.electronAPI.hideCountdownOverlay(); + void window.electronAPI.hideCountdownOverlay(activeRunId); allowAutoFinalize.current = false; restarting.current = false; discardRecordingId.current = null; @@ -370,14 +371,15 @@ export function useScreenRecorder(): UseScreenRecorderReturn { }, [teardownMedia]); const cancelCountdown = () => { + const activeRunId = countdownRunId.current; countdownRunId.current += 1; setCountdownActive(false); - void window.electronAPI.hideCountdownOverlay(); + void window.electronAPI.hideCountdownOverlay(activeRunId); }; - const safeShowCountdownOverlay = async (value: number) => { + const safeShowCountdownOverlay = async (value: number, runId: number) => { try { - await window.electronAPI.showCountdownOverlay(value); + await window.electronAPI.showCountdownOverlay(value, runId); return true; } catch (error) { console.warn("Failed to show countdown overlay:", error); @@ -385,17 +387,17 @@ export function useScreenRecorder(): UseScreenRecorderReturn { } }; - const safeSetCountdownOverlayValue = async (value: number) => { + const safeSetCountdownOverlayValue = async (value: number, runId: number) => { try { - await window.electronAPI.setCountdownOverlayValue(value); + await window.electronAPI.setCountdownOverlayValue(value, runId); } catch (error) { console.warn("Failed to update countdown overlay value:", error); } }; - const safeHideCountdownOverlay = async () => { + const safeHideCountdownOverlay = async (runId: number) => { try { - await window.electronAPI.hideCountdownOverlay(); + await window.electronAPI.hideCountdownOverlay(runId); } catch (error) { console.warn("Failed to hide countdown overlay:", error); } @@ -427,7 +429,11 @@ export function useScreenRecorder(): UseScreenRecorderReturn { try { const values = [3, 2, 1]; - const overlayShown = await safeShowCountdownOverlay(values[0]); + const overlayShown = await safeShowCountdownOverlay(values[0], runId); + + if (countdownRunId.current !== runId) { + return; + } for (const value of values) { if (countdownRunId.current !== runId) { @@ -435,7 +441,11 @@ export function useScreenRecorder(): UseScreenRecorderReturn { } if (overlayShown && value !== values[0]) { - await safeSetCountdownOverlayValue(value); + await safeSetCountdownOverlayValue(value, runId); + + if (countdownRunId.current !== runId) { + return; + } } await new Promise((resolve) => window.setTimeout(resolve, 1000)); @@ -449,7 +459,7 @@ export function useScreenRecorder(): UseScreenRecorderReturn { } finally { if (countdownRunId.current === runId) { setCountdownActive(false); - await safeHideCountdownOverlay(); + await safeHideCountdownOverlay(runId); } } };