feat(tab_navigation): functional context menu

This commit is contained in:
Elian Doran 2025-12-09 14:23:06 +02:00
parent e3f5b3535a
commit 9e099444b6
No known key found for this signature in database
2 changed files with 68 additions and 65 deletions

View File

@ -2,19 +2,27 @@ import "./TabHistoryNavigationButtons.css";
import { t } from "../services/i18n";
import ActionButton from "./react/ActionButton";
import { useCallback, useMemo } from "preact/hooks";
import { handleHistoryContextMenu } from "./launch_bar/HistoryNavigation";
import { dynamicRequire } from "../services/utils";
export default function TabHistoryNavigationButtons() {
const webContents = useMemo(() => dynamicRequire("@electron/remote").getCurrentWebContents(), []);
const onContextMenu = handleHistoryContextMenu(webContents);
return (
<div className="tab-history-navigation-buttons">
<ActionButton
icon="bx bx-left-arrow-alt"
text={t("tab_history_navigation_buttons.go-back")}
triggerCommand="backInNoteHistory"
onContextMenu={onContextMenu}
/>
<ActionButton
icon="bx bx-right-arrow-alt"
text={t("tab_history_navigation_buttons.go-forward")}
triggerCommand="forwardInNoteHistory"
onContextMenu={onContextMenu}
/>
</div>
);

View File

@ -1,11 +1,12 @@
import { useEffect, useRef } from "preact/hooks";
import FNote from "../../entities/fnote";
import { dynamicRequire, isElectron } from "../../services/utils";
import { LaunchBarActionButton, useLauncherIconAndTitle } from "./launch_bar_widgets";
import type { WebContents } from "electron";
import { useCallback, useMemo } from "preact/hooks";
import FNote from "../../entities/fnote";
import contextMenu, { MenuCommandItem } from "../../menus/context_menu";
import tree from "../../services/tree";
import link from "../../services/link";
import tree from "../../services/tree";
import { dynamicRequire } from "../../services/utils";
import { LaunchBarActionButton, useLauncherIconAndTitle } from "./launch_bar_widgets";
interface HistoryNavigationProps {
launcherNote: FNote;
@ -16,71 +17,65 @@ const HISTORY_LIMIT = 20;
export default function HistoryNavigationButton({ launcherNote, command }: HistoryNavigationProps) {
const { icon, title } = useLauncherIconAndTitle(launcherNote);
const webContentsRef = useRef<WebContents>(null);
useEffect(() => {
if (isElectron()) {
const webContents = dynamicRequire("@electron/remote").getCurrentWebContents();
// without this, the history is preserved across frontend reloads
webContents?.clearHistory();
webContentsRef.current = webContents;
}
}, []);
const webContents = useMemo(() => dynamicRequire("@electron/remote").getCurrentWebContents(), []);
return (
<LaunchBarActionButton
icon={icon}
text={title}
triggerCommand={command}
onContextMenu={async (e) => {
e.preventDefault();
const webContents = webContentsRef.current;
if (!webContents || webContents.navigationHistory.length() < 2) {
return;
}
let items: MenuCommandItem<string>[] = [];
const history = webContents.navigationHistory.getAllEntries();
const activeIndex = webContents.navigationHistory.getActiveIndex();
for (const idx in history) {
const { notePath } = link.parseNavigationStateFromUrl(history[idx].url);
if (!notePath) continue;
const title = await tree.getNotePathTitle(notePath);
items.push({
title,
command: idx,
uiIcon:
parseInt(idx) === activeIndex
? "bx bx-radio-circle-marked" // compare with type coercion!
: parseInt(idx) < activeIndex
? "bx bx-left-arrow-alt"
: "bx bx-right-arrow-alt"
});
}
items.reverse();
if (items.length > HISTORY_LIMIT) {
items = items.slice(0, HISTORY_LIMIT);
}
contextMenu.show({
x: e.pageX,
y: e.pageY,
items,
selectMenuItemHandler: (item: MenuCommandItem<string>) => {
if (item && item.command && webContents) {
const idx = parseInt(item.command, 10);
webContents.navigationHistory.goToIndex(idx);
}
}
});
}}
onContextMenu={handleHistoryContextMenu(webContents)}
/>
)
);
}
export function handleHistoryContextMenu(webContents: WebContents) {
return async (e: MouseEvent) => {
e.preventDefault();
if (!webContents || webContents.navigationHistory.length() < 2) {
return;
}
let items: MenuCommandItem<string>[] = [];
const history = webContents.navigationHistory.getAllEntries();
const activeIndex = webContents.navigationHistory.getActiveIndex();
for (const idx in history) {
const { notePath } = link.parseNavigationStateFromUrl(history[idx].url);
if (!notePath) continue;
const title = await tree.getNotePathTitle(notePath);
items.push({
title,
command: idx,
uiIcon:
parseInt(idx, 10) === activeIndex
? "bx bx-radio-circle-marked" // compare with type coercion!
: parseInt(idx, 10) < activeIndex
? "bx bx-left-arrow-alt"
: "bx bx-right-arrow-alt"
});
}
items.reverse();
if (items.length > HISTORY_LIMIT) {
items = items.slice(0, HISTORY_LIMIT);
}
contextMenu.show({
x: e.pageX,
y: e.pageY,
items,
selectMenuItemHandler: (item: MenuCommandItem<string>) => {
if (item && item.command && webContents) {
const idx = parseInt(item.command, 10);
webContents.navigationHistory.goToIndex(idx);
}
}
});
};
}