Fix CodeRabbit review comments
- Add buildDialogOptions helper function to safely attach parent window only when valid and not destroyed - Update all dialog calls (save-exported-video, open-video-file-picker, save-project-file, load-project-file) to use the helper - Fix supportsWindowOpacity logic by removing || isWayland so Linux always follows no-opacity codepath - Change incorrect Chromium feature name 'PipeWire' to 'WebRTCPipeWireCapturer' in main.ts - Remove unused isWayland variable in handlers.ts
This commit is contained in:
+77
-101
@@ -52,6 +52,21 @@ function isPathAllowed(filePath: string): boolean {
|
||||
return getAllowedReadDirs().some((dir) => isPathWithinDir(resolved, dir));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to build dialog options with a parent window only when it's valid.
|
||||
* This prevents passing stale or destroyed BrowserWindow references to dialog calls.
|
||||
*/
|
||||
function buildDialogOptions<T extends Electron.OpenDialogOptions | Electron.SaveDialogOptions>(
|
||||
baseOptions: T,
|
||||
parentWindow: BrowserWindow | null,
|
||||
): T & { parent?: BrowserWindow } {
|
||||
const mainWindow = parentWindow;
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
return { ...baseOptions, parent: mainWindow };
|
||||
}
|
||||
return baseOptions;
|
||||
}
|
||||
|
||||
function hasAllowedImportVideoExtension(filePath: string): boolean {
|
||||
return ALLOWED_IMPORT_VIDEO_EXTENSIONS.has(path.extname(filePath).toLowerCase());
|
||||
}
|
||||
@@ -359,9 +374,7 @@ export function registerIpcHandlers(
|
||||
onRecordingStateChange?: (recording: boolean, sourceName: string) => void,
|
||||
switchToHud?: () => void,
|
||||
) {
|
||||
const isWayland =
|
||||
process.env.XDG_SESSION_TYPE === "wayland" || process.env.WAYLAND_DISPLAY !== undefined;
|
||||
const supportsWindowOpacity = process.platform !== "linux" || isWayland;
|
||||
const supportsWindowOpacity = process.platform !== "linux";
|
||||
const countdownOverlayState = {
|
||||
visible: false,
|
||||
value: null as number | null,
|
||||
@@ -836,24 +849,18 @@ export function registerIpcHandlers(
|
||||
? [{ name: mainT("dialogs", "fileDialogs.gifImage"), extensions: ["gif"] }]
|
||||
: [{ name: mainT("dialogs", "fileDialogs.mp4Video"), extensions: ["mp4"] }];
|
||||
|
||||
const mainWindow = getMainWindow();
|
||||
const result = mainWindow
|
||||
? await dialog.showSaveDialog(mainWindow, {
|
||||
title: isGif
|
||||
? mainT("dialogs", "fileDialogs.saveGif")
|
||||
: mainT("dialogs", "fileDialogs.saveVideo"),
|
||||
defaultPath: path.join(app.getPath("downloads"), fileName),
|
||||
filters,
|
||||
properties: ["createDirectory", "showOverwriteConfirmation"],
|
||||
})
|
||||
: await dialog.showSaveDialog({
|
||||
title: isGif
|
||||
? mainT("dialogs", "fileDialogs.saveGif")
|
||||
: mainT("dialogs", "fileDialogs.saveVideo"),
|
||||
defaultPath: path.join(app.getPath("downloads"), fileName),
|
||||
filters,
|
||||
properties: ["createDirectory", "showOverwriteConfirmation"],
|
||||
});
|
||||
const dialogOptions = buildDialogOptions(
|
||||
{
|
||||
title: isGif
|
||||
? mainT("dialogs", "fileDialogs.saveGif")
|
||||
: mainT("dialogs", "fileDialogs.saveVideo"),
|
||||
defaultPath: path.join(app.getPath("downloads"), fileName),
|
||||
filters,
|
||||
properties: ["createDirectory", "showOverwriteConfirmation"],
|
||||
},
|
||||
getMainWindow(),
|
||||
);
|
||||
const result = await dialog.showSaveDialog(dialogOptions);
|
||||
|
||||
if (result.canceled || !result.filePath) {
|
||||
return {
|
||||
@@ -888,32 +895,22 @@ export function registerIpcHandlers(
|
||||
});
|
||||
ipcMain.handle("open-video-file-picker", async () => {
|
||||
try {
|
||||
const mainWindow = getMainWindow();
|
||||
const result = mainWindow
|
||||
? await dialog.showOpenDialog(mainWindow, {
|
||||
title: mainT("dialogs", "fileDialogs.selectVideo"),
|
||||
defaultPath: RECORDINGS_DIR,
|
||||
filters: [
|
||||
{
|
||||
name: mainT("dialogs", "fileDialogs.videoFiles"),
|
||||
extensions: ["webm", "mp4", "mov", "avi", "mkv"],
|
||||
},
|
||||
{ name: mainT("dialogs", "fileDialogs.allFiles"), extensions: ["*"] },
|
||||
],
|
||||
properties: ["openFile"],
|
||||
})
|
||||
: await dialog.showOpenDialog({
|
||||
title: mainT("dialogs", "fileDialogs.selectVideo"),
|
||||
defaultPath: RECORDINGS_DIR,
|
||||
filters: [
|
||||
{
|
||||
name: mainT("dialogs", "fileDialogs.videoFiles"),
|
||||
extensions: ["webm", "mp4", "mov", "avi", "mkv"],
|
||||
},
|
||||
{ name: mainT("dialogs", "fileDialogs.allFiles"), extensions: ["*"] },
|
||||
],
|
||||
properties: ["openFile"],
|
||||
});
|
||||
const dialogOptions = buildDialogOptions(
|
||||
{
|
||||
title: mainT("dialogs", "fileDialogs.selectVideo"),
|
||||
defaultPath: RECORDINGS_DIR,
|
||||
filters: [
|
||||
{
|
||||
name: mainT("dialogs", "fileDialogs.videoFiles"),
|
||||
extensions: ["webm", "mp4", "mov", "avi", "mkv"],
|
||||
},
|
||||
{ name: mainT("dialogs", "fileDialogs.allFiles"), extensions: ["*"] },
|
||||
],
|
||||
properties: ["openFile"],
|
||||
},
|
||||
getMainWindow(),
|
||||
);
|
||||
const result = await dialog.showOpenDialog(dialogOptions);
|
||||
|
||||
if (result.canceled || result.filePaths.length === 0) {
|
||||
return { success: false, canceled: true };
|
||||
@@ -992,32 +989,22 @@ export function registerIpcHandlers(
|
||||
? safeName
|
||||
: `${safeName}.${PROJECT_FILE_EXTENSION}`;
|
||||
|
||||
const mainWindow = getMainWindow();
|
||||
const result = mainWindow
|
||||
? await dialog.showSaveDialog(mainWindow, {
|
||||
title: mainT("dialogs", "fileDialogs.saveProject"),
|
||||
defaultPath: path.join(RECORDINGS_DIR, defaultName),
|
||||
filters: [
|
||||
{
|
||||
name: mainT("dialogs", "fileDialogs.openscreenProject"),
|
||||
extensions: [PROJECT_FILE_EXTENSION],
|
||||
},
|
||||
{ name: "JSON", extensions: ["json"] },
|
||||
],
|
||||
properties: ["createDirectory", "showOverwriteConfirmation"],
|
||||
})
|
||||
: await dialog.showSaveDialog({
|
||||
title: mainT("dialogs", "fileDialogs.saveProject"),
|
||||
defaultPath: path.join(RECORDINGS_DIR, defaultName),
|
||||
filters: [
|
||||
{
|
||||
name: mainT("dialogs", "fileDialogs.openscreenProject"),
|
||||
extensions: [PROJECT_FILE_EXTENSION],
|
||||
},
|
||||
{ name: "JSON", extensions: ["json"] },
|
||||
],
|
||||
properties: ["createDirectory", "showOverwriteConfirmation"],
|
||||
});
|
||||
const dialogOptions = buildDialogOptions(
|
||||
{
|
||||
title: mainT("dialogs", "fileDialogs.saveProject"),
|
||||
defaultPath: path.join(RECORDINGS_DIR, defaultName),
|
||||
filters: [
|
||||
{
|
||||
name: mainT("dialogs", "fileDialogs.openscreenProject"),
|
||||
extensions: [PROJECT_FILE_EXTENSION],
|
||||
},
|
||||
{ name: "JSON", extensions: ["json"] },
|
||||
],
|
||||
properties: ["createDirectory", "showOverwriteConfirmation"],
|
||||
},
|
||||
getMainWindow(),
|
||||
);
|
||||
const result = await dialog.showSaveDialog(dialogOptions);
|
||||
|
||||
if (result.canceled || !result.filePath) {
|
||||
return {
|
||||
@@ -1048,34 +1035,23 @@ export function registerIpcHandlers(
|
||||
|
||||
ipcMain.handle("load-project-file", async () => {
|
||||
try {
|
||||
const mainWindow = getMainWindow();
|
||||
const result = mainWindow
|
||||
? await dialog.showOpenDialog(mainWindow, {
|
||||
title: mainT("dialogs", "fileDialogs.openProject"),
|
||||
defaultPath: RECORDINGS_DIR,
|
||||
filters: [
|
||||
{
|
||||
name: mainT("dialogs", "fileDialogs.openscreenProject"),
|
||||
extensions: [PROJECT_FILE_EXTENSION],
|
||||
},
|
||||
{ name: "JSON", extensions: ["json"] },
|
||||
{ name: mainT("dialogs", "fileDialogs.allFiles"), extensions: ["*"] },
|
||||
],
|
||||
properties: ["openFile"],
|
||||
})
|
||||
: await dialog.showOpenDialog({
|
||||
title: mainT("dialogs", "fileDialogs.openProject"),
|
||||
defaultPath: RECORDINGS_DIR,
|
||||
filters: [
|
||||
{
|
||||
name: mainT("dialogs", "fileDialogs.openscreenProject"),
|
||||
extensions: [PROJECT_FILE_EXTENSION],
|
||||
},
|
||||
{ name: "JSON", extensions: ["json"] },
|
||||
{ name: mainT("dialogs", "fileDialogs.allFiles"), extensions: ["*"] },
|
||||
],
|
||||
properties: ["openFile"],
|
||||
});
|
||||
const dialogOptions = buildDialogOptions(
|
||||
{
|
||||
title: mainT("dialogs", "fileDialogs.openProject"),
|
||||
defaultPath: RECORDINGS_DIR,
|
||||
filters: [
|
||||
{
|
||||
name: mainT("dialogs", "fileDialogs.openscreenProject"),
|
||||
extensions: [PROJECT_FILE_EXTENSION],
|
||||
},
|
||||
{ name: "JSON", extensions: ["json"] },
|
||||
{ name: mainT("dialogs", "fileDialogs.allFiles"), extensions: ["*"] },
|
||||
],
|
||||
properties: ["openFile"],
|
||||
},
|
||||
getMainWindow(),
|
||||
);
|
||||
const result = await dialog.showOpenDialog(dialogOptions);
|
||||
|
||||
if (result.canceled || result.filePaths.length === 0) {
|
||||
return { success: false, canceled: true, message: "Open project canceled" };
|
||||
|
||||
+2
-2
@@ -37,8 +37,8 @@ if (process.platform === "linux") {
|
||||
process.env.XDG_SESSION_TYPE === "wayland" || process.env.WAYLAND_DISPLAY !== undefined;
|
||||
if (isWayland) {
|
||||
app.commandLine.appendSwitch("ozone-platform", "wayland");
|
||||
// Enable PipeWire for screen capture on Wayland
|
||||
app.commandLine.appendSwitch("enable-features", "WaylandWindowDrag,PipeWire");
|
||||
// Enable WebRTCPipeWireCapturer for screen capture on Wayland
|
||||
app.commandLine.appendSwitch("enable-features", "WaylandWindowDrag,WebRTCPipeWireCapturer");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user