Fix zombie tab context menus (#1390)

The memoizing of the tabs was causing the callbacks for
handleContextMenu to become dead ends. This makes more of the callbacks
into memoized callbacks and makes the handleContextMenu function itself
a memoized callback to ensure it's properly updated when its upstream
callbacks change.
This commit is contained in:
Evan Simkowitz 2024-12-04 20:30:28 -05:00 committed by GitHub
parent 87aea21184
commit df2889f280
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 52 additions and 46 deletions

View File

@ -7,7 +7,7 @@ import { TabRpcClient } from "@/app/store/wshrpcutil";
import { Button } from "@/element/button"; import { Button } from "@/element/button";
import { ContextMenuModel } from "@/store/contextmenu"; import { ContextMenuModel } from "@/store/contextmenu";
import { clsx } from "clsx"; import { clsx } from "clsx";
import { forwardRef, memo, useEffect, useImperativeHandle, useRef, useState } from "react"; import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react";
import { ObjectService } from "../store/services"; import { ObjectService } from "../store/services";
import { makeORef, useWaveObjectValue } from "../store/wos"; import { makeORef, useWaveObjectValue } from "../store/wos";
import "./tab.scss"; import "./tab.scss";
@ -144,47 +144,50 @@ const Tab = memo(
event.stopPropagation(); event.stopPropagation();
}; };
function handleContextMenu(e: React.MouseEvent<HTMLDivElement, MouseEvent>) { const handleContextMenu = useCallback(
e.preventDefault(); (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
let menu: ContextMenuItem[] = [ e.preventDefault();
{ label: isPinned ? "Unpin Tab" : "Pin Tab", click: onPinChange }, let menu: ContextMenuItem[] = [
{ label: "Rename Tab", click: () => handleRenameTab(null) }, { label: isPinned ? "Unpin Tab" : "Pin Tab", click: () => onPinChange() },
{ label: "Copy TabId", click: () => navigator.clipboard.writeText(id) }, { label: "Rename Tab", click: () => handleRenameTab(null) },
{ type: "separator" }, { label: "Copy TabId", click: () => navigator.clipboard.writeText(id) },
]; { type: "separator" },
const fullConfig = globalStore.get(atoms.fullConfigAtom); ];
const bgPresets: string[] = []; const fullConfig = globalStore.get(atoms.fullConfigAtom);
for (const key in fullConfig?.presets ?? {}) { const bgPresets: string[] = [];
if (key.startsWith("bg@")) { for (const key in fullConfig?.presets ?? {}) {
bgPresets.push(key); if (key.startsWith("bg@")) {
} bgPresets.push(key);
}
bgPresets.sort((a, b) => {
const aOrder = fullConfig.presets[a]["display:order"] ?? 0;
const bOrder = fullConfig.presets[b]["display:order"] ?? 0;
return aOrder - bOrder;
});
if (bgPresets.length > 0) {
const submenu: ContextMenuItem[] = [];
const oref = makeORef("tab", id);
for (const presetName of bgPresets) {
const preset = fullConfig.presets[presetName];
if (preset == null) {
continue;
} }
submenu.push({
label: preset["display:name"] ?? presetName,
click: () => {
ObjectService.UpdateObjectMeta(oref, preset);
RpcApi.ActivityCommand(TabRpcClient, { settabtheme: 1 });
},
});
} }
menu.push({ label: "Backgrounds", type: "submenu", submenu }, { type: "separator" }); bgPresets.sort((a, b) => {
} const aOrder = fullConfig.presets[a]["display:order"] ?? 0;
menu.push({ label: "Close Tab", click: () => onClose(null) }); const bOrder = fullConfig.presets[b]["display:order"] ?? 0;
ContextMenuModel.showContextMenu(menu, e); return aOrder - bOrder;
} });
if (bgPresets.length > 0) {
const submenu: ContextMenuItem[] = [];
const oref = makeORef("tab", id);
for (const presetName of bgPresets) {
const preset = fullConfig.presets[presetName];
if (preset == null) {
continue;
}
submenu.push({
label: preset["display:name"] ?? presetName,
click: () => {
ObjectService.UpdateObjectMeta(oref, preset);
RpcApi.ActivityCommand(TabRpcClient, { settabtheme: 1 });
},
});
}
menu.push({ label: "Backgrounds", type: "submenu", submenu }, { type: "separator" });
}
menu.push({ label: "Close Tab", click: () => onClose(null) });
ContextMenuModel.showContextMenu(menu, e);
},
[onPinChange, handleRenameTab, id, onClose, isPinned]
);
return ( return (
<div <div

View File

@ -574,12 +574,15 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
deleteLayoutModelForTab(tabId); deleteLayoutModelForTab(tabId);
}; };
const handlePinChange = (tabId: string, pinned: boolean) => { const handlePinChange = useCallback(
console.log("handlePinChange", tabId, pinned); (tabId: string, pinned: boolean) => {
fireAndForget(async () => { console.log("handlePinChange", tabId, pinned);
await WorkspaceService.ChangeTabPinning(workspace.oid, tabId, pinned); fireAndForget(async () => {
}); await WorkspaceService.ChangeTabPinning(workspace.oid, tabId, pinned);
}; });
},
[workspace]
);
const handleTabLoaded = useCallback((tabId: string) => { const handleTabLoaded = useCallback((tabId: string) => {
setTabsLoaded((prev) => { setTabsLoaded((prev) => {