trilium/apps/client/src/widgets/launch_bar/LauncherDefinitions.tsx

161 lines
5.7 KiB
TypeScript

import { useCallback, useContext, useEffect, useMemo, useState } from "preact/hooks";
import { useGlobalShortcut, useLegacyWidget, useNoteLabel, useNoteRelationTarget, useTriliumOptionBool } from "../react/hooks";
import { ParentComponent } from "../react/react_utils";
import BasicWidget from "../basic_widget";
import FNote from "../../entities/fnote";
import QuickSearchWidget from "../quick_search";
import { getErrorMessage, isMobile } from "../../services/utils";
import date_notes from "../../services/date_notes";
import { CustomNoteLauncher } from "./GenericButtons";
import { LaunchBarActionButton, LaunchBarContext, LauncherNoteProps, useLauncherIconAndTitle } from "./launch_bar_widgets";
import dialog from "../../services/dialog";
import { t } from "../../services/i18n";
import appContext, { CommandNames } from "../../components/app_context";
import toast from "../../services/toast";
export function CommandButton({ launcherNote }: LauncherNoteProps) {
const { icon, title } = useLauncherIconAndTitle(launcherNote);
const [ command ] = useNoteLabel(launcherNote, "command");
return command && (
<LaunchBarActionButton
icon={icon}
text={title}
triggerCommand={command as CommandNames}
/>
)
}
// we're intentionally displaying the launcher title and icon instead of the target,
// e.g. you want to make launchers to 2 mermaid diagrams which both have mermaid icon (ok),
// but on the launchpad you want them distinguishable.
// for titles, the note titles may follow a different scheme than maybe desirable on the launchpad
// another reason is the discrepancy between what user sees on the launchpad and in the config (esp. icons).
// The only downside is more work in setting up the typical case
// where you actually want to have both title and icon in sync, but for those cases there are bookmarks
export function NoteLauncher({ launcherNote, ...restProps }: { launcherNote: FNote, hoistedNoteId?: string }) {
return (
<CustomNoteLauncher
launcherNote={launcherNote}
getTargetNoteId={(launcherNote) => {
const targetNoteId = launcherNote.getRelationValue("target");
if (!targetNoteId) {
dialog.info(t("note_launcher.this_launcher_doesnt_define_target_note"));
return null;
}
return targetNoteId;
}}
getHoistedNoteId={launcherNote => launcherNote.getRelationValue("hoistedNote")}
{...restProps}
/>
);
}
export function ScriptLauncher({ launcherNote }: LauncherNoteProps) {
const { icon, title } = useLauncherIconAndTitle(launcherNote);
const launch = useCallback(async () => {
if (launcherNote.isLabelTruthy("scriptInLauncherContent")) {
await launcherNote.executeScript();
} else {
const script = await launcherNote.getRelationTarget("script");
if (script) {
await script.executeScript();
}
}
}, [ launcherNote ]);
// Keyboard shortcut.
const [ shortcut ] = useNoteLabel(launcherNote, "keyboardShortcut");
useGlobalShortcut(shortcut, launch);
return (
<LaunchBarActionButton
icon={icon}
text={title}
onClick={launch}
/>
)
}
export function AiChatButton({ launcherNote }: LauncherNoteProps) {
const [ aiEnabled ] = useTriliumOptionBool("aiEnabled");
const { icon, title } = useLauncherIconAndTitle(launcherNote);
return aiEnabled && (
<LaunchBarActionButton
icon={icon}
text={title}
triggerCommand="createAiChat"
/>
)
}
export function TodayLauncher({ launcherNote }: LauncherNoteProps) {
return (
<CustomNoteLauncher
launcherNote={launcherNote}
getTargetNoteId={async () => {
const todayNote = await date_notes.getTodayNote();
return todayNote?.noteId ?? null;
}}
/>
);
}
export function QuickSearchLauncherWidget() {
const { isHorizontalLayout } = useContext(LaunchBarContext);
const widget = useMemo(() => new QuickSearchWidget(), []);
const parentComponent = useContext(ParentComponent) as BasicWidget | null;
const isEnabled = isHorizontalLayout && !isMobile();
parentComponent?.contentSized();
return (
<div>
{isEnabled && <LegacyWidgetRenderer widget={widget} />}
</div>
)
}
export function CustomWidget({ launcherNote }: LauncherNoteProps) {
const [ widgetNote ] = useNoteRelationTarget(launcherNote, "widget");
const [ widget, setWidget ] = useState<BasicWidget>();
const parentComponent = useContext(ParentComponent) as BasicWidget | null;
parentComponent?.contentSized();
useEffect(() => {
(async function() {
let widget: BasicWidget;
try {
widget = await widgetNote?.executeScript();
} catch (e) {
toast.showError(t("toast.bundle-error.message", {
id: widgetNote?.noteId,
title: widgetNote?.title,
message: getErrorMessage(e)
}));
return;
}
if (widgetNote && widget instanceof BasicWidget) {
widget._noteId = widgetNote.noteId;
}
setWidget(widget);
})();
}, [ widgetNote ]);
return (
<div>
{widget && <LegacyWidgetRenderer widget={widget} />}
</div>
)
}
export function LegacyWidgetRenderer({ widget }: { widget: BasicWidget }) {
const [ widgetEl ] = useLegacyWidget(() => widget, {
noteContext: appContext.tabManager.getActiveContext() ?? undefined
});
return widgetEl;
}