closeTab fix (#1403)

fixes bug with closeTab when the tab didn't exist in the waveWindow cache.  also adds Cmd-Shift-W to close a tab (doesn't work for pinned tabs).  and restores Cmd-W for killing blocks on pinned tabs
This commit is contained in:
Mike Sawka 2024-12-05 18:26:20 -08:00 committed by GitHub
parent 7a61f25331
commit 5744f4b06f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 60 additions and 34 deletions

View File

@ -108,7 +108,7 @@ export class WaveTabView extends WebContentsView {
// TODO: circuitous // TODO: circuitous
const waveWindow = waveWindowMap.get(this.waveWindowId); const waveWindow = waveWindowMap.get(this.waveWindowId);
if (waveWindow) { if (waveWindow) {
waveWindow.allTabViews.delete(this.waveTabId); waveWindow.allLoadedTabViews.delete(this.waveTabId);
} }
} }
} }

View File

@ -22,7 +22,7 @@ export class WaveBrowserWindow extends BaseWindow {
waveWindowId: string; waveWindowId: string;
workspaceId: string; workspaceId: string;
waveReadyPromise: Promise<void>; waveReadyPromise: Promise<void>;
allTabViews: Map<string, WaveTabView>; allLoadedTabViews: Map<string, WaveTabView>;
activeTabView: WaveTabView; activeTabView: WaveTabView;
private canClose: boolean; private canClose: boolean;
private deleteAllowed: boolean; private deleteAllowed: boolean;
@ -108,7 +108,7 @@ export class WaveBrowserWindow extends BaseWindow {
this.tabSwitchQueue = []; this.tabSwitchQueue = [];
this.waveWindowId = waveWindow.oid; this.waveWindowId = waveWindow.oid;
this.workspaceId = waveWindow.workspaceid; this.workspaceId = waveWindow.workspaceid;
this.allTabViews = new Map<string, WaveTabView>(); this.allLoadedTabViews = new Map<string, WaveTabView>();
const winBoundsPoller = setInterval(() => { const winBoundsPoller = setInterval(() => {
if (this.isDestroyed()) { if (this.isDestroyed()) {
clearInterval(winBoundsPoller); clearInterval(winBoundsPoller);
@ -237,7 +237,7 @@ export class WaveBrowserWindow extends BaseWindow {
console.log("win removing window from backend DB", this.waveWindowId); console.log("win removing window from backend DB", this.waveWindowId);
fireAndForget(() => WindowService.CloseWindow(this.waveWindowId, true)); fireAndForget(() => WindowService.CloseWindow(this.waveWindowId, true));
} }
for (const tabView of this.allTabViews.values()) { for (const tabView of this.allLoadedTabViews.values()) {
tabView?.destroy(); tabView?.destroy();
} }
waveWindowMap.delete(this.waveWindowId); waveWindowMap.delete(this.waveWindowId);
@ -278,15 +278,15 @@ export class WaveBrowserWindow extends BaseWindow {
return; return;
} }
console.log("switchWorkspace newWs", newWs); console.log("switchWorkspace newWs", newWs);
if (this.allTabViews.size) { if (this.allLoadedTabViews.size) {
for (const tab of this.allTabViews.values()) { for (const tab of this.allLoadedTabViews.values()) {
this.contentView.removeChildView(tab); this.contentView.removeChildView(tab);
tab?.destroy(); tab?.destroy();
} }
} }
console.log("destroyed all tabs", this.waveWindowId); console.log("destroyed all tabs", this.waveWindowId);
this.workspaceId = workspaceId; this.workspaceId = workspaceId;
this.allTabViews = new Map(); this.allLoadedTabViews = new Map();
await this.setActiveTab(newWs.activetabid, false); await this.setActiveTab(newWs.activetabid, false);
} }
@ -306,17 +306,22 @@ export class WaveBrowserWindow extends BaseWindow {
} }
async closeTab(tabId: string) { async closeTab(tabId: string) {
console.log("closeTab", tabId, this.waveWindowId, this.workspaceId); console.log(`closeTab tabid=${tabId} ws=${this.workspaceId} window=${this.waveWindowId}`);
const tabView = this.allTabViews.get(tabId); const rtn = await WorkspaceService.CloseTab(this.workspaceId, tabId, true);
if (tabView) { if (rtn == null) {
const rtn = await WorkspaceService.CloseTab(this.workspaceId, tabId, true); console.log("[error] closeTab: no return value", tabId, this.workspaceId, this.waveWindowId);
if (rtn?.closewindow) { return;
this.close();
} else if (rtn?.newactivetabid) {
await this.setActiveTab(rtn.newactivetabid, false);
}
this.allTabViews.delete(tabId);
} }
if (rtn.closewindow) {
this.close();
return;
}
if (!rtn.newactivetabid) {
console.log("[error] closeTab, no new active tab", tabId, this.workspaceId, this.waveWindowId);
return;
}
await this.setActiveTab(rtn.newactivetabid, false);
this.allLoadedTabViews.delete(tabId);
} }
async setTabViewIntoWindow(tabView: WaveTabView, tabInitialized: boolean) { async setTabViewIntoWindow(tabView: WaveTabView, tabInitialized: boolean) {
@ -330,7 +335,7 @@ export class WaveBrowserWindow extends BaseWindow {
oldActiveView.isActiveTab = false; oldActiveView.isActiveTab = false;
} }
this.activeTabView = tabView; this.activeTabView = tabView;
this.allTabViews.set(tabView.waveTabId, tabView); this.allLoadedTabViews.set(tabView.waveTabId, tabView);
if (!tabInitialized) { if (!tabInitialized) {
console.log("initializing a new tab"); console.log("initializing a new tab");
await tabView.initPromise; await tabView.initPromise;
@ -407,7 +412,7 @@ export class WaveBrowserWindow extends BaseWindow {
} }
const curBounds = this.getContentBounds(); const curBounds = this.getContentBounds();
this.activeTabView?.positionTabOnScreen(curBounds); this.activeTabView?.positionTabOnScreen(curBounds);
for (const tabView of this.allTabViews.values()) { for (const tabView of this.allLoadedTabViews.values()) {
if (tabView == this.activeTabView) { if (tabView == this.activeTabView) {
continue; continue;
} }
@ -465,7 +470,7 @@ export class WaveBrowserWindow extends BaseWindow {
export function getWaveWindowByTabId(tabId: string): WaveBrowserWindow { export function getWaveWindowByTabId(tabId: string): WaveBrowserWindow {
for (const ww of waveWindowMap.values()) { for (const ww of waveWindowMap.values()) {
if (ww.allTabViews.has(tabId)) { if (ww.allLoadedTabViews.has(tabId)) {
return ww; return ww;
} }
} }
@ -530,17 +535,22 @@ ipcMain.on("set-active-tab", async (event, tabId) => {
ipcMain.on("create-tab", async (event, opts) => { ipcMain.on("create-tab", async (event, opts) => {
const senderWc = event.sender; const senderWc = event.sender;
const ww = getWaveWindowByWebContentsId(senderWc.id); const ww = getWaveWindowByWebContentsId(senderWc.id);
if (!ww) { if (ww != null) {
return; await ww.createTab();
} }
await ww.createTab();
event.returnValue = true; event.returnValue = true;
return null; return null;
}); });
ipcMain.on("close-tab", async (event, tabId) => { ipcMain.on("close-tab", async (event, workspaceId, tabId) => {
const ww = getWaveWindowByTabId(tabId); const ww = getWaveWindowByWorkspaceId(workspaceId);
await ww.closeTab(tabId); if (ww == null) {
console.log(`close-tab: no window found for workspace ws=${workspaceId} tab=${tabId}`);
return;
}
if (ww != null) {
await ww.closeTab(tabId);
}
event.returnValue = true; event.returnValue = true;
return null; return null;
}); });

View File

@ -44,7 +44,7 @@ contextBridge.exposeInMainWorld("api", {
deleteWorkspace: (workspaceId) => ipcRenderer.send("delete-workspace", workspaceId), deleteWorkspace: (workspaceId) => ipcRenderer.send("delete-workspace", workspaceId),
setActiveTab: (tabId) => ipcRenderer.send("set-active-tab", tabId), setActiveTab: (tabId) => ipcRenderer.send("set-active-tab", tabId),
createTab: () => ipcRenderer.send("create-tab"), createTab: () => ipcRenderer.send("create-tab"),
closeTab: (tabId) => ipcRenderer.send("close-tab", tabId), closeTab: (workspaceId, tabId) => ipcRenderer.send("close-tab", workspaceId, tabId),
setWindowInitStatus: (status) => ipcRenderer.send("set-window-init-status", status), setWindowInitStatus: (status) => ipcRenderer.send("set-window-init-status", status),
onWaveInit: (callback) => ipcRenderer.on("wave-init", (_event, initOpts) => callback(initOpts)), onWaveInit: (callback) => ipcRenderer.on("wave-init", (_event, initOpts) => callback(initOpts)),
sendLog: (log) => ipcRenderer.send("fe-log", log), sendLog: (log) => ipcRenderer.send("fe-log", log),

View File

@ -112,7 +112,7 @@ export class Updater {
private set status(value: UpdaterStatus) { private set status(value: UpdaterStatus) {
this._status = value; this._status = value;
getAllWaveWindows().forEach((window) => { getAllWaveWindows().forEach((window) => {
const allTabs = Array.from(window.allTabViews.values()); const allTabs = Array.from(window.allLoadedTabViews.values());
allTabs.forEach((tab) => { allTabs.forEach((tab) => {
tab.webContents.send("app-update-status", value); tab.webContents.send("app-update-status", value);
}); });

View File

@ -71,15 +71,20 @@ function shouldDispatchToBlock(e: WaveKeyboardEvent): boolean {
} }
function genericClose(tabId: string) { function genericClose(tabId: string) {
const ws = globalStore.get(atoms.workspace);
const tabORef = WOS.makeORef("tab", tabId); const tabORef = WOS.makeORef("tab", tabId);
const tabAtom = WOS.getWaveObjectAtom<Tab>(tabORef); const tabAtom = WOS.getWaveObjectAtom<Tab>(tabORef);
const tabData = globalStore.get(tabAtom); const tabData = globalStore.get(tabAtom);
if (tabData == null) { if (tabData == null) {
return; return;
} }
if (ws.pinnedtabids?.includes(tabId) && tabData.blockids?.length == 1) {
// don't allow closing the last block in a pinned tab
return;
}
if (tabData.blockids == null || tabData.blockids.length == 0) { if (tabData.blockids == null || tabData.blockids.length == 0) {
// close tab // close tab
getApi().closeTab(tabId); getApi().closeTab(ws.oid, tabId);
deleteLayoutModelForTab(tabId); deleteLayoutModelForTab(tabId);
return; return;
} }
@ -246,11 +251,21 @@ function registerGlobalKeys() {
return true; return true;
}); });
globalKeyMap.set("Cmd:w", () => { globalKeyMap.set("Cmd:w", () => {
const tabId = globalStore.get(atoms.staticTabId);
genericClose(tabId);
return true;
});
globalKeyMap.set("Cmd:Shift:w", () => {
const tabId = globalStore.get(atoms.staticTabId); const tabId = globalStore.get(atoms.staticTabId);
const ws = globalStore.get(atoms.workspace); const ws = globalStore.get(atoms.workspace);
if (!ws.pinnedtabids?.includes(tabId)) { if (ws.pinnedtabids?.includes(tabId)) {
genericClose(tabId); // switch to first unpinned tab if it exists (for close spamming)
if (ws.tabids != null && ws.tabids.length > 0) {
getApi().setActiveTab(ws.tabids[0]);
}
return true;
} }
getApi().closeTab(ws.oid, tabId);
return true; return true;
}); });
globalKeyMap.set("Cmd:m", () => { globalKeyMap.set("Cmd:m", () => {

View File

@ -5,7 +5,7 @@ import { Button } from "@/app/element/button";
import { modalsModel } from "@/app/store/modalmodel"; import { modalsModel } from "@/app/store/modalmodel";
import { WindowDrag } from "@/element/windowdrag"; import { WindowDrag } from "@/element/windowdrag";
import { deleteLayoutModelForTab } from "@/layout/index"; import { deleteLayoutModelForTab } from "@/layout/index";
import { atoms, createTab, getApi, isDev, PLATFORM, setActiveTab } from "@/store/global"; import { atoms, createTab, getApi, globalStore, isDev, PLATFORM, setActiveTab } from "@/store/global";
import { fireAndForget } from "@/util/util"; import { fireAndForget } from "@/util/util";
import { useAtomValue } from "jotai"; import { useAtomValue } from "jotai";
import { OverlayScrollbars } from "overlayscrollbars"; import { OverlayScrollbars } from "overlayscrollbars";
@ -570,7 +570,8 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
const handleCloseTab = (event: React.MouseEvent<HTMLButtonElement, MouseEvent> | null, tabId: string) => { const handleCloseTab = (event: React.MouseEvent<HTMLButtonElement, MouseEvent> | null, tabId: string) => {
event?.stopPropagation(); event?.stopPropagation();
getApi().closeTab(tabId); const ws = globalStore.get(atoms.workspace);
getApi().closeTab(ws.oid, tabId);
tabsWrapperRef.current.style.setProperty("--tabs-wrapper-transition", "width 0.3s ease"); tabsWrapperRef.current.style.setProperty("--tabs-wrapper-transition", "width 0.3s ease");
deleteLayoutModelForTab(tabId); deleteLayoutModelForTab(tabId);
}; };

View File

@ -93,7 +93,7 @@ declare global {
deleteWorkspace: (workspaceId: string) => void; deleteWorkspace: (workspaceId: string) => void;
setActiveTab: (tabId: string) => void; setActiveTab: (tabId: string) => void;
createTab: () => void; createTab: () => void;
closeTab: (tabId: string) => void; closeTab: (workspaceId: string, tabId: string) => void;
setWindowInitStatus: (status: "ready" | "wave-ready") => void; setWindowInitStatus: (status: "ready" | "wave-ready") => void;
onWaveInit: (callback: (initOpts: WaveInitOpts) => void) => void; onWaveInit: (callback: (initOpts: WaveInitOpts) => void) => void;
sendLog: (log: string) => void; sendLog: (log: string) => void;