Skip to content

Commit f858d3b

Browse files
authored
Pass workspace id to contextmenu-show (#1429)
Sometimes, the context menu click handlers don't seem to get passed any window object. Here, I'm sending over the workspace id with the `contextmenu-show` event so that we can resolve our cached copy of the object in case the value from the click handler is empty.
1 parent 7d21f55 commit f858d3b

7 files changed

Lines changed: 71 additions & 59 deletions

File tree

emain/emain-window.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,10 @@ export class WaveBrowserWindow extends BaseWindow {
299299
const workspaceList = await WorkspaceService.ListWorkspaces();
300300
if (!workspaceList.find((wse) => wse.workspaceid === workspaceId)?.windowid) {
301301
const curWorkspace = await WorkspaceService.GetWorkspace(this.workspaceId);
302-
if (curWorkspace.tabids.length > 1 && (!curWorkspace.name || !curWorkspace.icon)) {
302+
if (
303+
(curWorkspace.tabids?.length || curWorkspace.pinnedtabids?.length) &&
304+
(!curWorkspace.name || !curWorkspace.icon)
305+
) {
303306
const choice = dialog.showMessageBoxSync(this, {
304307
type: "question",
305308
buttons: ["Cancel", "Open in New Window", "Yes"],

emain/emain.ts

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ import {
4444
import { ElectronWshClient, initElectronWshClient } from "./emain-wsh";
4545
import { getLaunchSettings } from "./launchsettings";
4646
import { log } from "./log";
47-
import { instantiateAppMenu, makeAppMenu } from "./menu";
47+
import { makeAppMenu } from "./menu";
4848
import {
4949
getElectronAppBasePath,
5050
getElectronAppUnpackedBasePath,
@@ -426,17 +426,6 @@ function saveImageFileWithNativeDialog(defaultFileName: string, mimeType: string
426426

427427
electron.ipcMain.on("open-new-window", () => fireAndForget(createNewWaveWindow));
428428

429-
electron.ipcMain.on("contextmenu-show", (event, menuDefArr?: ElectronContextMenuItem[]) => {
430-
if (menuDefArr?.length === 0) {
431-
return;
432-
}
433-
fireAndForget(async () => {
434-
const menu = menuDefArr ? convertMenuDefArrToMenu(menuDefArr) : await instantiateAppMenu();
435-
menu.popup();
436-
});
437-
event.returnValue = true;
438-
});
439-
440429
// we try to set the primary display as index [0]
441430
function getActivityDisplays(): ActivityDisplayType[] {
442431
const displays = electron.screen.getAllDisplays();
@@ -488,28 +477,6 @@ function runActiveTimer() {
488477
setTimeout(runActiveTimer, 60000);
489478
}
490479

491-
function convertMenuDefArrToMenu(menuDefArr: ElectronContextMenuItem[]): electron.Menu {
492-
const menuItems: electron.MenuItem[] = [];
493-
for (const menuDef of menuDefArr) {
494-
const menuItemTemplate: electron.MenuItemConstructorOptions = {
495-
role: menuDef.role as any,
496-
label: menuDef.label,
497-
type: menuDef.type,
498-
click: (_, window) => {
499-
const ww = window as WaveBrowserWindow;
500-
ww?.activeTabView?.webContents?.send("contextmenu-click", menuDef.id);
501-
},
502-
checked: menuDef.checked,
503-
};
504-
if (menuDef.submenu != null) {
505-
menuItemTemplate.submenu = convertMenuDefArrToMenu(menuDef.submenu);
506-
}
507-
const menuItem = new electron.MenuItem(menuItemTemplate);
508-
menuItems.push(menuItem);
509-
}
510-
return electron.Menu.buildFromTemplate(menuItems);
511-
}
512-
513480
function hideWindowWithCatch(window: WaveBrowserWindow) {
514481
if (window == null) {
515482
return;

emain/menu.ts

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
createNewWaveWindow,
1111
createWorkspace,
1212
focusedWaveWindow,
13+
getWaveWindowByWorkspaceId,
1314
relaunchBrowserWindows,
1415
WaveBrowserWindow,
1516
} from "./emain-window";
@@ -36,15 +37,14 @@ function getWindowWebContents(window: electron.BaseWindow): electron.WebContents
3637
return null;
3738
}
3839

39-
async function getWorkspaceMenu(): Promise<Electron.MenuItemConstructorOptions[]> {
40+
async function getWorkspaceMenu(ww?: WaveBrowserWindow): Promise<Electron.MenuItemConstructorOptions[]> {
4041
const workspaceList = await RpcApi.WorkspaceListCommand(ElectronWshClient);
4142
console.log("workspaceList:", workspaceList);
4243
const workspaceMenu: Electron.MenuItemConstructorOptions[] = [
4344
{
4445
label: "Create New Workspace",
4546
click: (_, window) => {
46-
const ww = window as WaveBrowserWindow;
47-
fireAndForget(() => createWorkspace(ww));
47+
fireAndForget(() => createWorkspace((window as WaveBrowserWindow) ?? ww));
4848
},
4949
},
5050
];
@@ -65,8 +65,7 @@ async function getWorkspaceMenu(): Promise<Electron.MenuItemConstructorOptions[]
6565
return {
6666
label: `Switch to ${workspace.workspacedata.name} (${workspace.workspacedata.oid.slice(0, 5)})`,
6767
click: (_, window) => {
68-
const ww = window as WaveBrowserWindow;
69-
ww.switchWorkspace(workspace.workspacedata.oid);
68+
((window as WaveBrowserWindow) ?? ww)?.switchWorkspace(workspace.workspacedata.oid);
7069
},
7170
accelerator: getWorkspaceSwitchAccelerator(i),
7271
};
@@ -75,7 +74,8 @@ async function getWorkspaceMenu(): Promise<Electron.MenuItemConstructorOptions[]
7574
return workspaceMenu;
7675
}
7776

78-
async function getAppMenu(callbacks: AppMenuCallbacks): Promise<Electron.Menu> {
77+
async function getAppMenu(callbacks: AppMenuCallbacks, workspaceId?: string): Promise<Electron.Menu> {
78+
const ww = workspaceId && getWaveWindowByWorkspaceId(workspaceId);
7979
const fileMenu: Electron.MenuItemConstructorOptions[] = [
8080
{
8181
label: "New Window",
@@ -94,7 +94,7 @@ async function getAppMenu(callbacks: AppMenuCallbacks): Promise<Electron.Menu> {
9494
{
9595
label: "About Wave Terminal",
9696
click: (_, window) => {
97-
getWindowWebContents(window)?.send("menu-item-about");
97+
getWindowWebContents(window ?? ww)?.send("menu-item-about");
9898
},
9999
},
100100
{
@@ -172,7 +172,7 @@ async function getAppMenu(callbacks: AppMenuCallbacks): Promise<Electron.Menu> {
172172
label: "Reload Tab",
173173
accelerator: "Shift+CommandOrControl+R",
174174
click: (_, window) => {
175-
getWindowWebContents(window)?.reloadIgnoringCache();
175+
getWindowWebContents(window ?? ww)?.reloadIgnoringCache();
176176
},
177177
},
178178
{
@@ -191,7 +191,7 @@ async function getAppMenu(callbacks: AppMenuCallbacks): Promise<Electron.Menu> {
191191
label: "Toggle DevTools",
192192
accelerator: devToolsAccel,
193193
click: (_, window) => {
194-
let wc = getWindowWebContents(window);
194+
let wc = getWindowWebContents(window ?? ww);
195195
wc?.toggleDevTools();
196196
},
197197
},
@@ -202,14 +202,14 @@ async function getAppMenu(callbacks: AppMenuCallbacks): Promise<Electron.Menu> {
202202
label: "Reset Zoom",
203203
accelerator: "CommandOrControl+0",
204204
click: (_, window) => {
205-
getWindowWebContents(window)?.setZoomFactor(1);
205+
getWindowWebContents(window ?? ww)?.setZoomFactor(1);
206206
},
207207
},
208208
{
209209
label: "Zoom In",
210210
accelerator: "CommandOrControl+=",
211211
click: (_, window) => {
212-
const wc = getWindowWebContents(window);
212+
const wc = getWindowWebContents(window ?? ww);
213213
if (wc == null) {
214214
return;
215215
}
@@ -223,7 +223,7 @@ async function getAppMenu(callbacks: AppMenuCallbacks): Promise<Electron.Menu> {
223223
label: "Zoom In (hidden)",
224224
accelerator: "CommandOrControl+Shift+=",
225225
click: (_, window) => {
226-
const wc = getWindowWebContents(window);
226+
const wc = getWindowWebContents(window ?? ww);
227227
if (wc == null) {
228228
return;
229229
}
@@ -239,7 +239,7 @@ async function getAppMenu(callbacks: AppMenuCallbacks): Promise<Electron.Menu> {
239239
label: "Zoom Out",
240240
accelerator: "CommandOrControl+-",
241241
click: (_, window) => {
242-
const wc = getWindowWebContents(window);
242+
const wc = getWindowWebContents(window ?? ww);
243243
if (wc == null) {
244244
return;
245245
}
@@ -253,7 +253,7 @@ async function getAppMenu(callbacks: AppMenuCallbacks): Promise<Electron.Menu> {
253253
label: "Zoom Out (hidden)",
254254
accelerator: "CommandOrControl+Shift+-",
255255
click: (_, window) => {
256-
const wc = getWindowWebContents(window);
256+
const wc = getWindowWebContents(window ?? ww);
257257
if (wc == null) {
258258
return;
259259
}
@@ -313,11 +313,14 @@ async function getAppMenu(callbacks: AppMenuCallbacks): Promise<Electron.Menu> {
313313
return electron.Menu.buildFromTemplate(menuTemplate);
314314
}
315315

316-
export function instantiateAppMenu(): Promise<electron.Menu> {
317-
return getAppMenu({
318-
createNewWaveWindow,
319-
relaunchBrowserWindows,
320-
});
316+
export function instantiateAppMenu(workspaceId?: string): Promise<electron.Menu> {
317+
return getAppMenu(
318+
{
319+
createNewWaveWindow,
320+
relaunchBrowserWindows,
321+
},
322+
workspaceId
323+
);
321324
}
322325

323326
export function makeAppMenu() {
@@ -332,4 +335,43 @@ waveEventSubscribe({
332335
handler: makeAppMenu,
333336
});
334337

338+
function convertMenuDefArrToMenu(workspaceId: string, menuDefArr: ElectronContextMenuItem[]): electron.Menu {
339+
const menuItems: electron.MenuItem[] = [];
340+
for (const menuDef of menuDefArr) {
341+
const menuItemTemplate: electron.MenuItemConstructorOptions = {
342+
role: menuDef.role as any,
343+
label: menuDef.label,
344+
type: menuDef.type,
345+
click: (_, window) => {
346+
const ww = (window as WaveBrowserWindow) ?? getWaveWindowByWorkspaceId(workspaceId);
347+
if (!ww) {
348+
console.error("invalid window for context menu click handler:", ww, window, workspaceId);
349+
return;
350+
}
351+
ww?.activeTabView?.webContents?.send("contextmenu-click", menuDef.id);
352+
},
353+
checked: menuDef.checked,
354+
};
355+
if (menuDef.submenu != null) {
356+
menuItemTemplate.submenu = convertMenuDefArrToMenu(workspaceId, menuDef.submenu);
357+
}
358+
const menuItem = new electron.MenuItem(menuItemTemplate);
359+
menuItems.push(menuItem);
360+
}
361+
return electron.Menu.buildFromTemplate(menuItems);
362+
}
363+
364+
electron.ipcMain.on("contextmenu-show", (event, workspaceId: string, menuDefArr?: ElectronContextMenuItem[]) => {
365+
if (menuDefArr?.length === 0) {
366+
return;
367+
}
368+
fireAndForget(async () => {
369+
const menu = menuDefArr
370+
? convertMenuDefArrToMenu(workspaceId, menuDefArr)
371+
: await instantiateAppMenu(workspaceId);
372+
menu.popup();
373+
});
374+
event.returnValue = true;
375+
});
376+
335377
export { getAppMenu };

emain/preload.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ contextBridge.exposeInMainWorld("api", {
1616
getDocsiteUrl: () => ipcRenderer.sendSync("get-docsite-url"),
1717
getWebviewPreload: () => ipcRenderer.sendSync("get-webview-preload"),
1818
openNewWindow: () => ipcRenderer.send("open-new-window"),
19-
showContextMenu: (menu, position) => ipcRenderer.send("contextmenu-show", menu, position),
19+
showContextMenu: (workspaceId, menu) => ipcRenderer.send("contextmenu-show", workspaceId, menu),
2020
onContextMenuClick: (callback) => ipcRenderer.on("contextmenu-click", (_event, id) => callback(id)),
2121
downloadFile: (filePath) => ipcRenderer.send("download", { filePath }),
2222
openExternal: (url) => {

frontend/app/store/contextmenu.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright 2024, Command Line Inc.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
import { getApi } from "./global";
4+
import { atoms, getApi, globalStore } from "./global";
55

66
class ContextMenuModelType {
77
handlers: Map<string, () => void> = new Map(); // id -> handler
@@ -48,7 +48,7 @@ class ContextMenuModelType {
4848
showContextMenu(menu: ContextMenuItem[], ev: React.MouseEvent<any>): void {
4949
this.handlers.clear();
5050
const electronMenuItems = this._convertAndRegisterMenu(menu);
51-
getApi().showContextMenu(electronMenuItems);
51+
getApi().showContextMenu(globalStore.get(atoms.workspace).oid, electronMenuItems);
5252
}
5353
}
5454

frontend/app/tab/tabbar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,7 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
599599
};
600600

601601
function onEllipsisClick() {
602-
getApi().showContextMenu();
602+
getApi().showContextMenu(workspace.oid);
603603
}
604604

605605
const tabsWrapperWidth = tabIds.length * tabWidthRef.current;

frontend/types/custom.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ declare global {
7272
getWebviewPreload: () => string;
7373
getAboutModalDetails: () => AboutModalDetails;
7474
getDocsiteUrl: () => string;
75-
showContextMenu: (menu?: ElectronContextMenuItem[]) => void;
75+
showContextMenu: (workspaceId: string, menu?: ElectronContextMenuItem[]) => void;
7676
onContextMenuClick: (callback: (id: string) => void) => void;
7777
onNavigate: (callback: (url: string) => void) => void;
7878
onIframeNavigate: (callback: (url: string) => void) => void;

0 commit comments

Comments
 (0)