fix:flickering, stale runs, macOS bugs provided by coderabbit and thread countdown token
This commit is contained in:
Vendored
+1
-1
@@ -138,7 +138,7 @@ interface Window {
|
||||
showCountdownOverlay: (value: number) => Promise<void>;
|
||||
setCountdownOverlayValue: (value: number) => Promise<void>;
|
||||
hideCountdownOverlay: () => Promise<void>;
|
||||
onCountdownOverlayValue: (callback: (value: number) => void) => () => void;
|
||||
onCountdownOverlayValue: (callback: (value: number | null) => void) => () => void;
|
||||
setMicrophoneExpanded: (expanded: boolean) => void;
|
||||
setHasUnsavedChanges: (hasChanges: boolean) => void;
|
||||
onRequestSaveBeforeClose: (callback: () => Promise<boolean> | boolean) => () => void;
|
||||
|
||||
@@ -359,7 +359,30 @@ export function registerIpcHandlers(
|
||||
onRecordingStateChange?: (recording: boolean, sourceName: string) => void,
|
||||
switchToHud?: () => void,
|
||||
) {
|
||||
ipcMain.handle("countdown-overlay-show", async (_, value: number) => {
|
||||
const countdownOverlayState = {
|
||||
visible: false,
|
||||
value: null as number | null,
|
||||
};
|
||||
|
||||
const flushCountdownOverlayState = (win: BrowserWindow) => {
|
||||
if (win.isDestroyed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
win.webContents.send("countdown-overlay-value", countdownOverlayState.value);
|
||||
if (countdownOverlayState.visible && !win.isVisible()) {
|
||||
setTimeout(() => {
|
||||
if (!win.isDestroyed() && countdownOverlayState.visible && !win.isVisible()) {
|
||||
win.showInactive();
|
||||
}
|
||||
}, 16);
|
||||
}
|
||||
};
|
||||
|
||||
ipcMain.handle("countdown-overlay-show", (_, value: number) => {
|
||||
countdownOverlayState.visible = true;
|
||||
countdownOverlayState.value = value;
|
||||
|
||||
const win = getCountdownOverlayWindow() ?? createCountdownOverlayWindow();
|
||||
if (win.isDestroyed()) {
|
||||
return;
|
||||
@@ -368,38 +391,47 @@ export function registerIpcHandlers(
|
||||
if (win.webContents.isLoading()) {
|
||||
win.webContents.once("did-finish-load", () => {
|
||||
if (!win.isDestroyed()) {
|
||||
win.webContents.send("countdown-overlay-value", value);
|
||||
win.showInactive();
|
||||
flushCountdownOverlayState(win);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
win.webContents.send("countdown-overlay-value", value);
|
||||
win.showInactive();
|
||||
flushCountdownOverlayState(win);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle("countdown-overlay-set-value", (_, value: number) => {
|
||||
countdownOverlayState.value = value;
|
||||
|
||||
const win = getCountdownOverlayWindow();
|
||||
if (!win || win.isDestroyed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (win.webContents.isLoading()) {
|
||||
return;
|
||||
}
|
||||
|
||||
win.webContents.send("countdown-overlay-value", value);
|
||||
});
|
||||
|
||||
ipcMain.handle("countdown-overlay-hide", () => {
|
||||
countdownOverlayState.visible = false;
|
||||
countdownOverlayState.value = null;
|
||||
|
||||
const win = getCountdownOverlayWindow();
|
||||
if (!win || win.isDestroyed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
win.hide();
|
||||
if (!win.webContents.isLoading()) {
|
||||
win.webContents.send("countdown-overlay-value", countdownOverlayState.value);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle("switch-to-hud", () => {
|
||||
if (switchToHud) switchToHud();
|
||||
});
|
||||
ipcMain.handle("start-new-recording", async () => {
|
||||
ipcMain.handle("start-new-recording", () => {
|
||||
try {
|
||||
setCurrentRecordingSessionState(null);
|
||||
if (switchToHud) {
|
||||
|
||||
+11
-2
@@ -349,8 +349,17 @@ app.on("window-all-closed", () => {
|
||||
app.on("activate", () => {
|
||||
// On OS X it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow();
|
||||
const hasVisibleWindow = BrowserWindow.getAllWindows().some((window) => {
|
||||
if (window.isDestroyed() || !window.isVisible()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const url = window.webContents.getURL();
|
||||
const isCountdownOverlayWindow = url.includes("windowType=countdown-overlay");
|
||||
return !isCountdownOverlayWindow;
|
||||
});
|
||||
if (!hasVisibleWindow) {
|
||||
showMainWindow();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+2
-2
@@ -139,8 +139,8 @@ contextBridge.exposeInMainWorld("electronAPI", {
|
||||
hideCountdownOverlay: () => {
|
||||
return ipcRenderer.invoke("countdown-overlay-hide");
|
||||
},
|
||||
onCountdownOverlayValue: (callback: (value: number) => void) => {
|
||||
const listener = (_event: unknown, value: number) => callback(value);
|
||||
onCountdownOverlayValue: (callback: (value: number | null) => void) => {
|
||||
const listener = (_event: unknown, value: number | null) => callback(value);
|
||||
ipcRenderer.on("countdown-overlay-value", listener);
|
||||
return () => ipcRenderer.removeListener("countdown-overlay-value", listener);
|
||||
},
|
||||
|
||||
+1
-1
@@ -204,7 +204,7 @@ export function createCountdownOverlayWindow(): BrowserWindow {
|
||||
transparent: true,
|
||||
backgroundColor: "#00000000",
|
||||
hasShadow: false,
|
||||
show: !HEADLESS,
|
||||
show: false,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, "preload.mjs"),
|
||||
nodeIntegration: false,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export function CountdownOverlay() {
|
||||
const [value, setValue] = useState(3);
|
||||
const [value, setValue] = useState<number | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = window.electronAPI.onCountdownOverlayValue((nextValue) => {
|
||||
@@ -13,7 +13,11 @@ export function CountdownOverlay() {
|
||||
|
||||
return (
|
||||
<div className="w-screen h-screen bg-transparent flex items-center justify-center pointer-events-none select-none">
|
||||
<div className="text-white/90 text-[120px] font-bold leading-none tabular-nums">{value}</div>
|
||||
{value === null ? null : (
|
||||
<div className="text-white/90 text-[120px] font-bold leading-none tabular-nums">
|
||||
{value}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -401,6 +401,9 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
|
||||
}
|
||||
};
|
||||
|
||||
const isCountdownRunActive = (runId?: number) =>
|
||||
runId === undefined || countdownRunId.current === runId;
|
||||
|
||||
const startRecordCountdown = async () => {
|
||||
if (countdownActive || recording) {
|
||||
return;
|
||||
@@ -442,16 +445,16 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
|
||||
return;
|
||||
}
|
||||
|
||||
await startRecording();
|
||||
await startRecording(runId);
|
||||
} finally {
|
||||
if (countdownRunId.current === runId) {
|
||||
setCountdownActive(false);
|
||||
await safeHideCountdownOverlay();
|
||||
}
|
||||
await safeHideCountdownOverlay();
|
||||
}
|
||||
};
|
||||
|
||||
const startRecording = async () => {
|
||||
const startRecording = async (countdownRunToken?: number) => {
|
||||
try {
|
||||
const selectedSource = await window.electronAPI.getSelectedSource();
|
||||
if (!selectedSource) {
|
||||
@@ -459,6 +462,11 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isCountdownRunActive(countdownRunToken)) {
|
||||
teardownMedia();
|
||||
return;
|
||||
}
|
||||
|
||||
let screenMediaStream: MediaStream;
|
||||
|
||||
const videoConstraints = {
|
||||
@@ -499,6 +507,11 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
|
||||
}
|
||||
screenStream.current = screenMediaStream;
|
||||
|
||||
if (!isCountdownRunActive(countdownRunToken)) {
|
||||
teardownMedia();
|
||||
return;
|
||||
}
|
||||
|
||||
if (microphoneEnabled) {
|
||||
try {
|
||||
microphoneStream.current = await navigator.mediaDevices.getUserMedia({
|
||||
@@ -523,6 +536,11 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
|
||||
}
|
||||
}
|
||||
|
||||
if (!isCountdownRunActive(countdownRunToken)) {
|
||||
teardownMedia();
|
||||
return;
|
||||
}
|
||||
|
||||
if (webcamEnabled) {
|
||||
try {
|
||||
webcamStream.current = await navigator.mediaDevices.getUserMedia({
|
||||
@@ -551,6 +569,11 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
|
||||
}
|
||||
}
|
||||
|
||||
if (!isCountdownRunActive(countdownRunToken)) {
|
||||
teardownMedia();
|
||||
return;
|
||||
}
|
||||
|
||||
stream.current = new MediaStream();
|
||||
const videoTrack = screenMediaStream.getVideoTracks()[0];
|
||||
if (!videoTrack) {
|
||||
@@ -610,6 +633,11 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
|
||||
);
|
||||
|
||||
const hasAudio = stream.current.getAudioTracks().length > 0;
|
||||
if (!isCountdownRunActive(countdownRunToken)) {
|
||||
teardownMedia();
|
||||
return;
|
||||
}
|
||||
|
||||
screenRecorder.current = createRecorderHandle(stream.current, {
|
||||
mimeType,
|
||||
videoBitsPerSecond,
|
||||
|
||||
Reference in New Issue
Block a user