mirror of
https://github.com/zadam/trilium.git
synced 2025-12-05 15:04:24 +01:00
refactor(react/launch_bar): port history navigation
This commit is contained in:
parent
5b310f3e46
commit
8efb849391
@ -1,63 +0,0 @@
|
|||||||
import froca from "../../services/froca.js";
|
|
||||||
import attributeService from "../../services/attributes.js";
|
|
||||||
import CommandButtonWidget from "./command_button.js";
|
|
||||||
import type { EventData } from "../../components/app_context.js";
|
|
||||||
|
|
||||||
export type ButtonNoteIdProvider = () => string;
|
|
||||||
|
|
||||||
export default class ButtonFromNoteWidget extends CommandButtonWidget {
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.settings.buttonNoteIdProvider = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
buttonNoteIdProvider(provider: ButtonNoteIdProvider) {
|
|
||||||
this.settings.buttonNoteIdProvider = provider;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
doRender() {
|
|
||||||
super.doRender();
|
|
||||||
|
|
||||||
this.updateIcon();
|
|
||||||
}
|
|
||||||
|
|
||||||
updateIcon() {
|
|
||||||
if (!this.settings.buttonNoteIdProvider) {
|
|
||||||
console.error(`buttonNoteId for '${this.componentId}' is not defined.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const buttonNoteId = this.settings.buttonNoteIdProvider();
|
|
||||||
|
|
||||||
if (!buttonNoteId) {
|
|
||||||
console.error(`buttonNoteId for '${this.componentId}' is not defined.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
froca.getNote(buttonNoteId).then((note) => {
|
|
||||||
const icon = note?.getIcon();
|
|
||||||
if (icon) {
|
|
||||||
this.settings.icon = icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.refreshIcon();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
|
|
||||||
// TODO: this seems incorrect
|
|
||||||
//@ts-ignore
|
|
||||||
const buttonNote = froca.getNoteFromCache(this.buttonNoteIdProvider());
|
|
||||||
|
|
||||||
if (!buttonNote) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loadResults.getAttributeRows(this.componentId).find((attr) => attr.type === "label" && attr.name === "iconClass" && attributeService.isAffecting(attr, buttonNote))) {
|
|
||||||
this.updateIcon();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,90 +0,0 @@
|
|||||||
import utils from "../../services/utils.js";
|
|
||||||
import contextMenu, { MenuCommandItem } from "../../menus/context_menu.js";
|
|
||||||
import treeService from "../../services/tree.js";
|
|
||||||
import ButtonFromNoteWidget from "./button_from_note.js";
|
|
||||||
import type FNote from "../../entities/fnote.js";
|
|
||||||
import type { CommandNames } from "../../components/app_context.js";
|
|
||||||
import type { WebContents } from "electron";
|
|
||||||
import link from "../../services/link.js";
|
|
||||||
|
|
||||||
export default class HistoryNavigationButton extends ButtonFromNoteWidget {
|
|
||||||
private webContents?: WebContents;
|
|
||||||
|
|
||||||
constructor(launcherNote: FNote, command: string) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.title(() => launcherNote.title)
|
|
||||||
.icon(() => launcherNote.getIcon())
|
|
||||||
.command(() => command as CommandNames)
|
|
||||||
.titlePlacement("right")
|
|
||||||
.buttonNoteIdProvider(() => launcherNote.noteId)
|
|
||||||
.onContextMenu((e) => { if (e) this.showContextMenu(e); })
|
|
||||||
.class("launcher-button");
|
|
||||||
}
|
|
||||||
|
|
||||||
doRender() {
|
|
||||||
super.doRender();
|
|
||||||
|
|
||||||
if (utils.isElectron()) {
|
|
||||||
this.webContents = utils.dynamicRequire("@electron/remote").getCurrentWebContents();
|
|
||||||
|
|
||||||
// without this, the history is preserved across frontend reloads
|
|
||||||
this.webContents?.clearHistory();
|
|
||||||
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async showContextMenu(e: JQuery.ContextMenuEvent) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
if (!this.webContents || this.webContents.navigationHistory.length() < 2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let items: MenuCommandItem<string>[] = [];
|
|
||||||
|
|
||||||
const history = this.webContents.navigationHistory.getAllEntries();
|
|
||||||
const activeIndex = this.webContents.navigationHistory.getActiveIndex();
|
|
||||||
|
|
||||||
for (const idx in history) {
|
|
||||||
const { notePath } = link.parseNavigationStateFromUrl(history[idx].url);
|
|
||||||
if (!notePath) continue;
|
|
||||||
|
|
||||||
const title = await treeService.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 > 20) {
|
|
||||||
items = items.slice(0, 50);
|
|
||||||
}
|
|
||||||
|
|
||||||
contextMenu.show({
|
|
||||||
x: e.pageX,
|
|
||||||
y: e.pageY,
|
|
||||||
items,
|
|
||||||
selectMenuItemHandler: (item: MenuCommandItem<string>) => {
|
|
||||||
if (item && item.command && this.webContents) {
|
|
||||||
const idx = parseInt(item.command, 10);
|
|
||||||
this.webContents.navigationHistory.goToIndex(idx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
activeNoteChangedEvent() {
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -7,13 +7,13 @@ import ScriptLauncher from "../buttons/launcher/script_launcher.js";
|
|||||||
import CommandButtonWidget from "../buttons/command_button.js";
|
import CommandButtonWidget from "../buttons/command_button.js";
|
||||||
import utils from "../../services/utils.js";
|
import utils from "../../services/utils.js";
|
||||||
import TodayLauncher from "../buttons/launcher/today_launcher.js";
|
import TodayLauncher from "../buttons/launcher/today_launcher.js";
|
||||||
import HistoryNavigationButton from "../buttons/history_navigation.js";
|
|
||||||
import QuickSearchLauncherWidget from "../quick_search_launcher.js";
|
import QuickSearchLauncherWidget from "../quick_search_launcher.js";
|
||||||
import type FNote from "../../entities/fnote.js";
|
import type FNote from "../../entities/fnote.js";
|
||||||
import type { CommandNames } from "../../components/app_context.js";
|
import type { CommandNames } from "../../components/app_context.js";
|
||||||
import AiChatButton from "../buttons/ai_chat_button.js";
|
import AiChatButton from "../buttons/ai_chat_button.js";
|
||||||
import BookmarkButtons from "../launch_bar/BookmarkButtons.jsx";
|
import BookmarkButtons from "../launch_bar/BookmarkButtons.jsx";
|
||||||
import SpacerWidget from "../launch_bar/SpacerWidget.jsx";
|
import SpacerWidget from "../launch_bar/SpacerWidget.jsx";
|
||||||
|
import HistoryNavigationButton from "../launch_bar/HistoryNavigation.jsx";
|
||||||
|
|
||||||
interface InnerWidget extends BasicWidget {
|
interface InnerWidget extends BasicWidget {
|
||||||
settings?: {
|
settings?: {
|
||||||
@ -117,9 +117,9 @@ export default class LauncherWidget extends BasicWidget {
|
|||||||
case "syncStatus":
|
case "syncStatus":
|
||||||
return new SyncStatusWidget();
|
return new SyncStatusWidget();
|
||||||
case "backInHistoryButton":
|
case "backInHistoryButton":
|
||||||
return new HistoryNavigationButton(note, "backInNoteHistory");
|
return <HistoryNavigationButton launcherNote={note} command="backInNoteHistory" />
|
||||||
case "forwardInHistoryButton":
|
case "forwardInHistoryButton":
|
||||||
return new HistoryNavigationButton(note, "forwardInNoteHistory");
|
return <HistoryNavigationButton launcherNote={note} command="forwardInNoteHistory" />
|
||||||
case "todayInJournal":
|
case "todayInJournal":
|
||||||
return new TodayLauncher(note);
|
return new TodayLauncher(note);
|
||||||
case "quickSearch":
|
case "quickSearch":
|
||||||
|
|||||||
86
apps/client/src/widgets/launch_bar/HistoryNavigation.tsx
Normal file
86
apps/client/src/widgets/launch_bar/HistoryNavigation.tsx
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import { useEffect, useRef } from "preact/hooks";
|
||||||
|
import FNote from "../../entities/fnote";
|
||||||
|
import { dynamicRequire, escapeHtml, isElectron } from "../../services/utils";
|
||||||
|
import { useNoteLabel, useNoteProperty } from "../react/hooks";
|
||||||
|
import { LaunchBarActionButton } from "./launch_bar_widgets";
|
||||||
|
import type { WebContents } from "electron";
|
||||||
|
import contextMenu, { MenuCommandItem } from "../../menus/context_menu";
|
||||||
|
import tree from "../../services/tree";
|
||||||
|
import link from "../../services/link";
|
||||||
|
|
||||||
|
interface HistoryNavigationProps {
|
||||||
|
launcherNote: FNote;
|
||||||
|
command: "backInNoteHistory" | "forwardInNoteHistory";
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function HistoryNavigationButton({ launcherNote, command }: HistoryNavigationProps) {
|
||||||
|
const [ iconClass ] = useNoteLabel(launcherNote, "iconClass");
|
||||||
|
const title = useNoteProperty(launcherNote, "title");
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return iconClass && title && (
|
||||||
|
<LaunchBarActionButton
|
||||||
|
icon={iconClass}
|
||||||
|
text={escapeHtml(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 > 20) {
|
||||||
|
items = items.slice(0, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user