mirror of
https://github.com/zadam/trilium.git
synced 2025-10-21 15:49:00 +02:00
chore(react/ribbon): handle search error
This commit is contained in:
parent
e1fa188244
commit
b9193a5562
@ -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,13 +230,14 @@ 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`, {
|
||||||
@ -239,6 +246,23 @@ function SearchStringOption({ note, refreshResults, ...restProps }: SearchOption
|
|||||||
}
|
}
|
||||||
}, 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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user