mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-30 18:05:55 +01:00 
			
		
		
		
	chore(react/ribbon): handle search error
This commit is contained in:
		| @@ -61,7 +61,7 @@ function removeOwnedLabelByName(note: FNote, labelName: string) { | |||||||
|  * @param value the value of the attribute to set. |  * @param value the value of the attribute to set. | ||||||
|  */ |  */ | ||||||
| export async function setAttribute(note: FNote, type: "label" | "relation", name: string, value: string | null | undefined) { | export async function setAttribute(note: FNote, type: "label" | "relation", name: string, value: string | null | undefined) { | ||||||
|     if (value) { |     if (value !== null && value !== undefined) { | ||||||
|         // Create or update the attribute. |         // Create or update the attribute. | ||||||
|         await server.put(`notes/${note.noteId}/set-attribute`, { type, name, value }); |         await server.put(`notes/${note.noteId}/set-attribute`, { type, name, value }); | ||||||
|     } else { |     } else { | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| import { TextareaHTMLAttributes } from "preact/compat"; | import { RefObject, TextareaHTMLAttributes } from "preact/compat"; | ||||||
|  |  | ||||||
| interface FormTextAreaProps extends Omit<TextareaHTMLAttributes, "onBlur" | "onChange"> { | interface FormTextAreaProps extends Omit<TextareaHTMLAttributes, "onBlur" | "onChange"> { | ||||||
|     id?: string; |     id?: string; | ||||||
| @@ -6,10 +6,12 @@ interface FormTextAreaProps extends Omit<TextareaHTMLAttributes, "onBlur" | "onC | |||||||
|     onChange?(newValue: string): void; |     onChange?(newValue: string): void; | ||||||
|     onBlur?(newValue: string): void; |     onBlur?(newValue: string): void; | ||||||
|     rows: number; |     rows: number; | ||||||
|  |     inputRef?: RefObject<HTMLTextAreaElement> | ||||||
| } | } | ||||||
| export default function FormTextArea({ id, onBlur, onChange, rows, currentValue, className, ...restProps }: FormTextAreaProps) { | export default function FormTextArea({ inputRef, id, onBlur, onChange, rows, currentValue, className, ...restProps }: FormTextAreaProps) { | ||||||
|     return ( |     return ( | ||||||
|         <textarea |         <textarea | ||||||
|  |             ref={inputRef} | ||||||
|             id={id} |             id={id} | ||||||
|             rows={rows} |             rows={rows} | ||||||
|             className={`form-control ${className ?? ""}`} |             className={`form-control ${className ?? ""}`} | ||||||
|   | |||||||
| @@ -519,6 +519,7 @@ export function useTooltip(elRef: RefObject<HTMLElement>, config: Partial<Toolti | |||||||
|         if (!elRef?.current) return; |         if (!elRef?.current) return; | ||||||
|  |  | ||||||
|         const $el = $(elRef.current); |         const $el = $(elRef.current); | ||||||
|  |         $el.tooltip("dispose"); | ||||||
|         $el.tooltip(config); |         $el.tooltip(config); | ||||||
|     }, [ elRef, config ]); |     }, [ elRef, config ]); | ||||||
|  |  | ||||||
| @@ -527,7 +528,8 @@ export function useTooltip(elRef: RefObject<HTMLElement>, config: Partial<Toolti | |||||||
|  |  | ||||||
|         const $el = $(elRef.current); |         const $el = $(elRef.current); | ||||||
|         $el.tooltip("show"); |         $el.tooltip("show"); | ||||||
|     }, [ elRef ]); |         console.log("Show tooltip ", elRef.current); | ||||||
|  |     }, [ elRef, config ]); | ||||||
|  |  | ||||||
|     const hideTooltip = useCallback(() => { |     const hideTooltip = useCallback(() => { | ||||||
|         if (!elRef?.current) return; |         if (!elRef?.current) return; | ||||||
|   | |||||||
| @@ -13,9 +13,10 @@ import toast from "../../services/toast"; | |||||||
| import froca from "../../services/froca"; | import froca from "../../services/froca"; | ||||||
| import { useContext, useEffect, useRef, useState } from "preact/hooks"; | import { useContext, useEffect, useRef, useState } from "preact/hooks"; | ||||||
| import { ParentComponent } from "../react/react_utils"; | import { ParentComponent } from "../react/react_utils"; | ||||||
| import { useSpacedUpdate, useTriliumEventBeta } from "../react/hooks"; | import { useNoteLabel, useSpacedUpdate, useTooltip, useTriliumEventBeta } from "../react/hooks"; | ||||||
| import appContext from "../../components/app_context"; | import appContext from "../../components/app_context"; | ||||||
| import server from "../../services/server"; | import server from "../../services/server"; | ||||||
|  | import { tooltip } from "leaflet"; | ||||||
|  |  | ||||||
| interface SearchOption { | interface SearchOption { | ||||||
|   attributeName: string; |   attributeName: string; | ||||||
| @@ -32,6 +33,7 @@ interface SearchOptionProps { | |||||||
|   refreshResults: () => void; |   refreshResults: () => void; | ||||||
|   attributeName: string; |   attributeName: string; | ||||||
|   attributeType: "label" | "relation"; |   attributeType: "label" | "relation"; | ||||||
|  |   error?: { message: string }; | ||||||
| } | } | ||||||
|  |  | ||||||
| const SEARCH_OPTIONS: SearchOption[] = [ | const SEARCH_OPTIONS: SearchOption[] = [ | ||||||
| @@ -93,6 +95,7 @@ const SEARCH_OPTIONS: SearchOption[] = [ | |||||||
| export default function SearchDefinitionTab({ note, ntxId }: TabContext) { | export default function SearchDefinitionTab({ note, ntxId }: TabContext) { | ||||||
|   const parentComponent = useContext(ParentComponent); |   const parentComponent = useContext(ParentComponent); | ||||||
|   const [ searchOptions, setSearchOptions ] = useState<{ availableOptions: SearchOption[], activeOptions: SearchOption[] }>(); |   const [ searchOptions, setSearchOptions ] = useState<{ availableOptions: SearchOption[], activeOptions: SearchOption[] }>(); | ||||||
|  |   const [ error, setError ] = useState<{ message: string }>(); | ||||||
|  |  | ||||||
|   function refreshOptions() { |   function refreshOptions() { | ||||||
|     if (!note) return; |     if (!note) return; | ||||||
| @@ -120,9 +123,10 @@ export default function SearchDefinitionTab({ note, ntxId }: TabContext) { | |||||||
|  |  | ||||||
|     try { |     try { | ||||||
|         const result = await froca.loadSearchNote(noteId); |         const result = await froca.loadSearchNote(noteId); | ||||||
|  |         if (result?.error) { | ||||||
|         if (result && result.error) { |           setError({ message: result?.error}) | ||||||
|           //this.handleEvent("showSearchError", { error: result.error }); |         } else { | ||||||
|  |           setError(undefined); | ||||||
|         } |         } | ||||||
|     } catch (e: any) { |     } catch (e: any) { | ||||||
|         toast.showError(e.message); |         toast.showError(e.message); | ||||||
| @@ -147,11 +151,12 @@ export default function SearchDefinitionTab({ note, ntxId }: TabContext) { | |||||||
|             <tr> |             <tr> | ||||||
|               <td className="title-column">{t("search_definition.add_search_option")}</td> |               <td className="title-column">{t("search_definition.add_search_option")}</td> | ||||||
|               <td colSpan={2} className="add-search-option"> |               <td colSpan={2} className="add-search-option"> | ||||||
|                 {searchOptions?.availableOptions.map(({ icon, label, tooltip }) => ( |                 {searchOptions?.availableOptions.map(({ icon, label, tooltip, attributeName, attributeType }) => ( | ||||||
|                   <Button |                   <Button | ||||||
|                     icon={icon} |                     icon={icon} | ||||||
|                     text={label} |                     text={label} | ||||||
|                     title={tooltip} |                     title={tooltip} | ||||||
|  |                     onClick={() => attributes.setAttribute(note, attributeType, attributeName, "")} | ||||||
|                   /> |                   /> | ||||||
|                 ))} |                 ))} | ||||||
|               </td> |               </td> | ||||||
| @@ -162,7 +167,8 @@ export default function SearchDefinitionTab({ note, ntxId }: TabContext) { | |||||||
|                   attributeName, |                   attributeName, | ||||||
|                   attributeType, |                   attributeType, | ||||||
|                   note, |                   note, | ||||||
|                   refreshResults |                   refreshResults, | ||||||
|  |                   error | ||||||
|                 }); |                 }); | ||||||
|               })} |               })} | ||||||
|             </tbody> |             </tbody> | ||||||
| @@ -224,21 +230,39 @@ function SearchOption({ note, title, children, help, attributeName, attributeTyp | |||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
| function SearchStringOption({ note, refreshResults, ...restProps }: SearchOptionProps) { | function SearchStringOption({ note, refreshResults, error, ...restProps }: SearchOptionProps) { | ||||||
|   const currentValue = useRef(""); |   const [ searchString, setSearchString ] = useNoteLabel(note, "searchString"); | ||||||
|  |   const inputRef = useRef<HTMLTextAreaElement>(null); | ||||||
|  |   const currentValue = useRef(searchString ?? ""); | ||||||
|   const spacedUpdate = useSpacedUpdate(async () => { |   const spacedUpdate = useSpacedUpdate(async () => { | ||||||
|     const searchString = currentValue.current; |     const searchString = currentValue.current; | ||||||
|     appContext.lastSearchString = searchString; |     appContext.lastSearchString = searchString; | ||||||
|  |     setSearchString(searchString); | ||||||
|     await attributes.setAttribute(note, "label", "searchString", searchString); |  | ||||||
|  |  | ||||||
|     if (note.title.startsWith(t("search_string.search_prefix"))) { |     if (note.title.startsWith(t("search_string.search_prefix"))) { | ||||||
|       await server.put(`notes/${note.noteId}/title`, { |       await server.put(`notes/${note.noteId}/title`, { | ||||||
|           title: `${t("search_string.search_prefix")} ${searchString.length < 30 ? searchString : `${searchString.substr(0, 30)}…`}` |         title: `${t("search_string.search_prefix")} ${searchString.length < 30 ? searchString : `${searchString.substr(0, 30)}…`}` | ||||||
|       }); |       }); | ||||||
|     } |     } | ||||||
|   }, 1000); |   }, 1000); | ||||||
|  |  | ||||||
|  |   // React to errors | ||||||
|  |   const { showTooltip, hideTooltip } = useTooltip(inputRef, { | ||||||
|  |     trigger: "manual", | ||||||
|  |     title: `${t("search_string.error", { error: error?.message })}`, | ||||||
|  |     html: true, | ||||||
|  |     placement: "bottom" | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   useEffect(() => { | ||||||
|  |     if (error) { | ||||||
|  |       showTooltip(); | ||||||
|  |       setTimeout(() => hideTooltip(), 4000); | ||||||
|  |     } else { | ||||||
|  |       hideTooltip(); | ||||||
|  |     } | ||||||
|  |   }, [ error ]); | ||||||
|  |  | ||||||
|   return <SearchOption     |   return <SearchOption     | ||||||
|     title={t("search_string.title_column")} |     title={t("search_string.title_column")} | ||||||
|     help={<> |     help={<> | ||||||
| @@ -256,8 +280,10 @@ function SearchStringOption({ note, refreshResults, ...restProps }: SearchOption | |||||||
|     note={note} {...restProps} |     note={note} {...restProps} | ||||||
|   > |   > | ||||||
|     <FormTextArea |     <FormTextArea | ||||||
|  |       inputRef={inputRef} | ||||||
|       className="search-string" |       className="search-string" | ||||||
|       placeholder={t("search_string.placeholder")} |       placeholder={t("search_string.placeholder")} | ||||||
|  |       currentValue={searchString ?? ""} | ||||||
|       onChange={text => { |       onChange={text => { | ||||||
|         currentValue.current = text; |         currentValue.current = text; | ||||||
|         spacedUpdate.scheduleUpdate(); |         spacedUpdate.scheduleUpdate(); | ||||||
|   | |||||||
| @@ -8,38 +8,6 @@ import { Tooltip } from "bootstrap"; | |||||||
|  |  | ||||||
| export default class SearchString extends AbstractSearchOption { | export default class SearchString extends AbstractSearchOption { | ||||||
|  |  | ||||||
|     private $searchString!: JQuery<HTMLElement>; |  | ||||||
|     private spacedUpdate!: SpacedUpdate; |  | ||||||
|  |  | ||||||
|     static async create(noteId: string) { |  | ||||||
|         await AbstractSearchOption.setAttribute(noteId, "label", "searchString"); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     doRender() { |  | ||||||
|         const $option = $(TPL); |  | ||||||
|         this.$searchString = $option.find(".search-string"); |  | ||||||
|  |  | ||||||
|         this.spacedUpdate = new SpacedUpdate(async () => { |  | ||||||
|  |  | ||||||
|         }, 1000); |  | ||||||
|  |  | ||||||
|         this.$searchString.val(this.note.getLabelValue("searchString") ?? ""); |  | ||||||
|  |  | ||||||
|         return $option; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     showSearchErrorEvent({ error }: EventData<"showSearchError">) { |  | ||||||
|         let tooltip = new Tooltip(this.$searchString[0], { |  | ||||||
|             trigger: "manual", |  | ||||||
|             title: `${t("search_string.error", { error })}`, |  | ||||||
|             placement: "bottom" |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         tooltip.show(); |  | ||||||
|  |  | ||||||
|         setTimeout(() => tooltip.dispose(), 4000); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     focusOnSearchDefinitionEvent() { |     focusOnSearchDefinitionEvent() { | ||||||
|         this.$searchString |         this.$searchString | ||||||
|             .val(String(this.$searchString.val()).trim() ?? appContext.lastSearchString) |             .val(String(this.$searchString.val()).trim() ?? appContext.lastSearchString) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user