mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-03 21:19:01 +01:00 
			
		
		
		
	feat(react/floating_buttons): port in-app help button
This commit is contained in:
		
							parent
							
								
									cabe240e7e
								
							
						
					
					
						commit
						f51d944bb3
					
				@ -1,5 +1,5 @@
 | 
			
		||||
import { describe, expect, it } from "vitest";
 | 
			
		||||
import { byBookType, byNoteType } from "./help_button.js";
 | 
			
		||||
import { byBookType, byNoteType } from "./in_app_help.js";
 | 
			
		||||
import fs from "fs";
 | 
			
		||||
import type { HiddenSubtreeItem } from "@triliumnext/commons";
 | 
			
		||||
import path from "path";
 | 
			
		||||
@ -25,7 +25,7 @@ describe("Help button", () => {
 | 
			
		||||
            ...Object.values(byBookType)
 | 
			
		||||
        ].filter((noteId) => noteId) as string[];
 | 
			
		||||
 | 
			
		||||
        const metaPath = path.resolve(path.join(__dirname, "../../../../server/src/assets/doc_notes/en/User Guide/!!!meta.json"));
 | 
			
		||||
        const metaPath = path.resolve(path.join(__dirname, "../../../server/src/assets/doc_notes/en/User Guide/!!!meta.json"));
 | 
			
		||||
        const meta: HiddenSubtreeItem[] = JSON.parse(fs.readFileSync(metaPath, "utf-8"));
 | 
			
		||||
        const allNoteIds = new Set(getNoteIds(meta));
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										43
									
								
								apps/client/src/services/in_app_help.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								apps/client/src/services/in_app_help.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,43 @@
 | 
			
		||||
import { NoteType } from "@triliumnext/commons";
 | 
			
		||||
import { ViewTypeOptions } from "./note_list_renderer";
 | 
			
		||||
import { FNote } from "./frontend_script_entrypoint";
 | 
			
		||||
 | 
			
		||||
export const byNoteType: Record<Exclude<NoteType, "book">, string | null> = {
 | 
			
		||||
    canvas: null,
 | 
			
		||||
    code: null,
 | 
			
		||||
    contentWidget: null,
 | 
			
		||||
    doc: null,
 | 
			
		||||
    file: null,
 | 
			
		||||
    image: null,
 | 
			
		||||
    launcher: null,
 | 
			
		||||
    mermaid: null,
 | 
			
		||||
    mindMap: null,
 | 
			
		||||
    noteMap: null,
 | 
			
		||||
    relationMap: null,
 | 
			
		||||
    render: null,
 | 
			
		||||
    search: null,
 | 
			
		||||
    text: null,
 | 
			
		||||
    webView: null,
 | 
			
		||||
    aiChat: null
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const byBookType: Record<ViewTypeOptions, string | null> = {
 | 
			
		||||
    list: "mULW0Q3VojwY",
 | 
			
		||||
    grid: "8QqnMzx393bx",
 | 
			
		||||
    calendar: "xWbu3jpNWapp",
 | 
			
		||||
    table: "2FvYrpmOXm29",
 | 
			
		||||
    geoMap: "81SGnPGMk7Xc",
 | 
			
		||||
    board: "CtBQqbwXDx1w"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function getHelpUrlForNote(note: FNote | null | undefined) {
 | 
			
		||||
    if (note && note.type !== "book" && byNoteType[note.type]) {
 | 
			
		||||
        return byNoteType[note.type];
 | 
			
		||||
    } else if (note?.hasLabel("calendarRoot")) {
 | 
			
		||||
        return "l0tKav7yLHGF";
 | 
			
		||||
    } else if (note?.hasLabel("textSnippet")) {
 | 
			
		||||
        return "pwc194wlRzcH";
 | 
			
		||||
    } else if (note && note.type === "book") {
 | 
			
		||||
        return byBookType[note.getAttributeValue("label", "viewType") as ViewTypeOptions ?? ""]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,8 +1,8 @@
 | 
			
		||||
import { t } from "i18next";
 | 
			
		||||
import "./FloatingButtons.css";
 | 
			
		||||
import Button from "./react/Button";
 | 
			
		||||
import { useNoteContext, useNoteLabel, useNoteLabelBoolean, useNoteProperty, useTriliumEvent, useTriliumEvents, useTriliumOption, useTriliumOptionBool } from "./react/hooks";
 | 
			
		||||
import { useContext, useEffect, useMemo, useRef, useState } from "preact/hooks";
 | 
			
		||||
import { useNoteContext, useNoteProperty, useTriliumEvent, useTriliumEvents } from "./react/hooks";
 | 
			
		||||
import { useContext, useEffect, useMemo, useState } from "preact/hooks";
 | 
			
		||||
import { ParentComponent } from "./react/react_utils";
 | 
			
		||||
import attributes from "../services/attributes";
 | 
			
		||||
import { EventData, EventNames } from "../components/app_context";
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ import NoteContext from "../components/note_context";
 | 
			
		||||
import FNote from "../entities/fnote";
 | 
			
		||||
import ActionButton, { ActionButtonProps } from "./react/ActionButton";
 | 
			
		||||
import { useNoteLabelBoolean, useTriliumOption } from "./react/hooks";
 | 
			
		||||
import { useEffect, useRef, useState } from "preact/hooks";
 | 
			
		||||
import { useEffect, useMemo, useRef, useState } from "preact/hooks";
 | 
			
		||||
import { createImageSrcUrl, openInAppHelpFromUrl } from "../services/utils";
 | 
			
		||||
import server from "../services/server";
 | 
			
		||||
import { SaveSqlConsoleResponse } from "@triliumnext/commons";
 | 
			
		||||
@ -15,6 +15,7 @@ import { copyImageReferenceToClipboard } from "../services/image";
 | 
			
		||||
import tree from "../services/tree";
 | 
			
		||||
import protected_session_holder from "../services/protected_session_holder";
 | 
			
		||||
import options from "../services/options";
 | 
			
		||||
import { getHelpUrlForNote } from "../services/in_app_help";
 | 
			
		||||
 | 
			
		||||
export interface FloatingButtonDefinition {
 | 
			
		||||
    component: (context: FloatingButtonContext) => VNode;
 | 
			
		||||
@ -104,6 +105,10 @@ export const FLOATING_BUTTON_DEFINITIONS: FloatingButtonDefinition[] = [
 | 
			
		||||
        isEnabled: ({ note, noteContext }) =>
 | 
			
		||||
            ["mermaid", "mindMap"].includes(note?.type ?? "")
 | 
			
		||||
            && note?.isContentAvailable() && noteContext?.viewScope?.viewMode === "default"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        component: InAppHelpButton,
 | 
			
		||||
        isEnabled: ({ note }) => !!getHelpUrlForNote(note)
 | 
			
		||||
    }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@ -303,4 +308,16 @@ function ExportImageButtons({ triggerEvent }: FloatingButtonContext) {
 | 
			
		||||
            />
 | 
			
		||||
        </>
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function InAppHelpButton({ note }: FloatingButtonContext) {
 | 
			
		||||
    const helpUrl = getHelpUrlForNote(note);
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <FloatingButton
 | 
			
		||||
            icon="bx bx-help-circle"
 | 
			
		||||
            text={t("help-button.title")}
 | 
			
		||||
            onClick={() => helpUrl && openInAppHelpFromUrl(helpUrl)}
 | 
			
		||||
        />
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
@ -1,78 +0,0 @@
 | 
			
		||||
import { type EventData } from "../../components/app_context.js";
 | 
			
		||||
import type FNote from "../../entities/fnote.js";
 | 
			
		||||
import type { NoteType } from "../../entities/fnote.js";
 | 
			
		||||
import { t } from "../../services/i18n.js";
 | 
			
		||||
import type { ViewTypeOptions } from "../../services/note_list_renderer.js";
 | 
			
		||||
import NoteContextAwareWidget from "../note_context_aware_widget.js";
 | 
			
		||||
 | 
			
		||||
const TPL = /*html*/`
 | 
			
		||||
<button class="open-contextual-help-button" title="${t("help-button.title")}">
 | 
			
		||||
    <span class="bx bx-help-circle"></span>
 | 
			
		||||
</button>
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
export const byNoteType: Record<Exclude<NoteType, "book">, string | null> = {
 | 
			
		||||
    canvas: null,
 | 
			
		||||
    code: null,
 | 
			
		||||
    contentWidget: null,
 | 
			
		||||
    doc: null,
 | 
			
		||||
    file: null,
 | 
			
		||||
    image: null,
 | 
			
		||||
    launcher: null,
 | 
			
		||||
    mermaid: null,
 | 
			
		||||
    mindMap: null,
 | 
			
		||||
    noteMap: null,
 | 
			
		||||
    relationMap: null,
 | 
			
		||||
    render: null,
 | 
			
		||||
    search: null,
 | 
			
		||||
    text: null,
 | 
			
		||||
    webView: null,
 | 
			
		||||
    aiChat: null
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const byBookType: Record<ViewTypeOptions, string | null> = {
 | 
			
		||||
    list: "mULW0Q3VojwY",
 | 
			
		||||
    grid: "8QqnMzx393bx",
 | 
			
		||||
    calendar: "xWbu3jpNWapp",
 | 
			
		||||
    table: "2FvYrpmOXm29",
 | 
			
		||||
    geoMap: "81SGnPGMk7Xc",
 | 
			
		||||
    board: "CtBQqbwXDx1w"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default class ContextualHelpButton extends NoteContextAwareWidget {
 | 
			
		||||
 | 
			
		||||
    isEnabled() {
 | 
			
		||||
        if (!super.isEnabled()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return !!ContextualHelpButton.#getUrlToOpen(this.note);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    doRender() {
 | 
			
		||||
        this.$widget = $(TPL);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static #getUrlToOpen(note: FNote | null | undefined) {
 | 
			
		||||
        if (note && note.type !== "book" && byNoteType[note.type]) {
 | 
			
		||||
            return byNoteType[note.type];
 | 
			
		||||
        } else if (note?.hasLabel("calendarRoot")) {
 | 
			
		||||
            return "l0tKav7yLHGF";
 | 
			
		||||
        } else if (note?.hasLabel("textSnippet")) {
 | 
			
		||||
            return "pwc194wlRzcH";
 | 
			
		||||
        } else if (note && note.type === "book") {
 | 
			
		||||
            return byBookType[note.getAttributeValue("label", "viewType") as ViewTypeOptions ?? ""]
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async refreshWithNote(note: FNote | null | undefined): Promise<void> {
 | 
			
		||||
        this.$widget.attr("data-in-app-help", ContextualHelpButton.#getUrlToOpen(this.note) ?? "");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
 | 
			
		||||
        if (this.note?.type === "book" && loadResults.getAttributeRows().find((attr) => attr.noteId === this.noteId && attr.name === "viewType")) {
 | 
			
		||||
            this.refresh();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user