mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	feat(react/floating_buttons): port in-app help button
This commit is contained in:
		| @@ -1,5 +1,5 @@ | |||||||
| import { describe, expect, it } from "vitest"; | 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 fs from "fs"; | ||||||
| import type { HiddenSubtreeItem } from "@triliumnext/commons"; | import type { HiddenSubtreeItem } from "@triliumnext/commons"; | ||||||
| import path from "path"; | import path from "path"; | ||||||
| @@ -25,7 +25,7 @@ describe("Help button", () => { | |||||||
|             ...Object.values(byBookType) |             ...Object.values(byBookType) | ||||||
|         ].filter((noteId) => noteId) as string[]; |         ].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 meta: HiddenSubtreeItem[] = JSON.parse(fs.readFileSync(metaPath, "utf-8")); | ||||||
|         const allNoteIds = new Set(getNoteIds(meta)); |         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 { t } from "i18next"; | ||||||
| import "./FloatingButtons.css"; | import "./FloatingButtons.css"; | ||||||
| import Button from "./react/Button"; | import Button from "./react/Button"; | ||||||
| import { useNoteContext, useNoteLabel, useNoteLabelBoolean, useNoteProperty, useTriliumEvent, useTriliumEvents, useTriliumOption, useTriliumOptionBool } from "./react/hooks"; | import { useNoteContext, useNoteProperty, useTriliumEvent, useTriliumEvents } from "./react/hooks"; | ||||||
| import { useContext, useEffect, useMemo, useRef, useState } from "preact/hooks"; | import { useContext, useEffect, useMemo, useState } from "preact/hooks"; | ||||||
| import { ParentComponent } from "./react/react_utils"; | import { ParentComponent } from "./react/react_utils"; | ||||||
| import attributes from "../services/attributes"; | import attributes from "../services/attributes"; | ||||||
| import { EventData, EventNames } from "../components/app_context"; | import { EventData, EventNames } from "../components/app_context"; | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ import NoteContext from "../components/note_context"; | |||||||
| import FNote from "../entities/fnote"; | import FNote from "../entities/fnote"; | ||||||
| import ActionButton, { ActionButtonProps } from "./react/ActionButton"; | import ActionButton, { ActionButtonProps } from "./react/ActionButton"; | ||||||
| import { useNoteLabelBoolean, useTriliumOption } from "./react/hooks"; | 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 { createImageSrcUrl, openInAppHelpFromUrl } from "../services/utils"; | ||||||
| import server from "../services/server"; | import server from "../services/server"; | ||||||
| import { SaveSqlConsoleResponse } from "@triliumnext/commons"; | import { SaveSqlConsoleResponse } from "@triliumnext/commons"; | ||||||
| @@ -15,6 +15,7 @@ import { copyImageReferenceToClipboard } from "../services/image"; | |||||||
| import tree from "../services/tree"; | import tree from "../services/tree"; | ||||||
| import protected_session_holder from "../services/protected_session_holder"; | import protected_session_holder from "../services/protected_session_holder"; | ||||||
| import options from "../services/options"; | import options from "../services/options"; | ||||||
|  | import { getHelpUrlForNote } from "../services/in_app_help"; | ||||||
|  |  | ||||||
| export interface FloatingButtonDefinition { | export interface FloatingButtonDefinition { | ||||||
|     component: (context: FloatingButtonContext) => VNode; |     component: (context: FloatingButtonContext) => VNode; | ||||||
| @@ -104,6 +105,10 @@ export const FLOATING_BUTTON_DEFINITIONS: FloatingButtonDefinition[] = [ | |||||||
|         isEnabled: ({ note, noteContext }) => |         isEnabled: ({ note, noteContext }) => | ||||||
|             ["mermaid", "mindMap"].includes(note?.type ?? "") |             ["mermaid", "mindMap"].includes(note?.type ?? "") | ||||||
|             && note?.isContentAvailable() && noteContext?.viewScope?.viewMode === "default" |             && note?.isContentAvailable() && noteContext?.viewScope?.viewMode === "default" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         component: InAppHelpButton, | ||||||
|  |         isEnabled: ({ note }) => !!getHelpUrlForNote(note) | ||||||
|     } |     } | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
| @@ -304,3 +309,15 @@ 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(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
		Reference in New Issue
	
	Block a user