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

View File

@ -44,7 +44,7 @@ contextBridge.exposeInMainWorld("api", {
deleteWorkspace: (workspaceId) => ipcRenderer.send("delete-workspace", workspaceId),
setActiveTab: (tabId) => ipcRenderer.send("set-active-tab", tabId),
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),
onWaveInit: (callback) => ipcRenderer.on("wave-init", (_event, initOpts) => callback(initOpts)),
sendLog: (log) => ipcRenderer.send("fe-log", log),

View File

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

View File

@ -71,15 +71,20 @@ function shouldDispatchToBlock(e: WaveKeyboardEvent): boolean {
}
function genericClose(tabId: string) {
const ws = globalStore.get(atoms.workspace);
const tabORef = WOS.makeORef("tab", tabId);
const tabAtom = WOS.getWaveObjectAtom<Tab>(tabORef);
const tabData = globalStore.get(tabAtom);
if (tabData == null) {
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) {
// close tab
getApi().closeTab(tabId);
getApi().closeTab(ws.oid, tabId);
deleteLayoutModelForTab(tabId);
return;
}
@ -246,11 +251,21 @@ function registerGlobalKeys() {
return true;
});
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 ws = globalStore.get(atoms.workspace);
if (!ws.pinnedtabids?.includes(tabId)) {
genericClose(tabId);
if (ws.pinnedtabids?.includes(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;
});
globalKeyMap.set("Cmd:m", () => {

View File

@ -5,7 +5,7 @@ import { Button } from "@/app/element/button";
import { modalsModel } from "@/app/store/modalmodel";
import { WindowDrag } from "@/element/windowdrag";
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 { useAtomValue } from "jotai";
import { OverlayScrollbars } from "overlayscrollbars";
@ -570,7 +570,8 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
const handleCloseTab = (event: React.MouseEvent<HTMLButtonElement, MouseEvent> | null, tabId: string) => {
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");
deleteLayoutModelForTab(tabId);
};

View File

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