diff --git a/apps/client/src/widgets/dialogs/call_to_action.tsx b/apps/client/src/widgets/dialogs/call_to_action.tsx index 9b4668856..1ae4a14eb 100644 --- a/apps/client/src/widgets/dialogs/call_to_action.tsx +++ b/apps/client/src/widgets/dialogs/call_to_action.tsx @@ -5,12 +5,7 @@ import { dismissCallToAction, getCallToActions } from "./call_to_action_definiti import { t } from "../../services/i18n"; export default function CallToActionDialog() { - const activeCallToActions = useMemo(() => getCallToActions(), []); - - if (!activeCallToActions.length) { - return <>; - } - + const activeCallToActions = useMemo(() => getCallToActions(), []); const [ activeIndex, setActiveIndex ] = useState(0); const [ shown, setShown ] = useState(true); const activeItem = activeCallToActions[activeIndex]; @@ -23,7 +18,7 @@ export default function CallToActionDialog() { } } - return ( + return (activeCallToActions.length && { - note_types.getNoteTypeItems().then(noteTypes => { - let index = -1; + useEffect(() => { + note_types.getNoteTypeItems().then(noteTypes => { + let index = -1; - setNoteTypes((noteTypes ?? []).map((item) => { - if (item.title === "----") { - index++; - return { - title: SEPARATOR_TITLE_REPLACEMENTS[index], - enabled: false - } + setNoteTypes((noteTypes ?? []).map((item) => { + if (item.title === "----") { + index++; + return { + title: SEPARATOR_TITLE_REPLACEMENTS[index], + enabled: false } + } - return item; - })); - }); + return item; + })); }); - } + }, []); function onNoteTypeSelected(value: string) { const [ noteType, templateNoteId ] = value.split(","); diff --git a/apps/client/src/widgets/dialogs/recent_changes.tsx b/apps/client/src/widgets/dialogs/recent_changes.tsx index 48222f02c..295650e54 100644 --- a/apps/client/src/widgets/dialogs/recent_changes.tsx +++ b/apps/client/src/widgets/dialogs/recent_changes.tsx @@ -18,34 +18,27 @@ import { useTriliumEvent } from "../react/hooks"; export default function RecentChangesDialog() { const [ ancestorNoteId, setAncestorNoteId ] = useState(); const [ groupedByDate, setGroupedByDate ] = useState>(); - const [ needsRefresh, setNeedsRefresh ] = useState(false); + const [ refreshCounter, setRefreshCounter ] = useState(0); const [ shown, setShown ] = useState(false); - useTriliumEvent("showRecentChanges", ({ ancestorNoteId }) => { - setNeedsRefresh(true); + useTriliumEvent("showRecentChanges", ({ ancestorNoteId }) => { setAncestorNoteId(ancestorNoteId ?? hoisted_note.getHoistedNoteId()); setShown(true); }); - if (!groupedByDate || needsRefresh) { - useEffect(() => { - if (needsRefresh) { - setNeedsRefresh(false); - } + useEffect(() => { + server.get(`recent-changes/${ancestorNoteId}`) + .then(async (recentChanges) => { + // preload all notes into cache + await froca.getNotes( + recentChanges.map((r) => r.noteId), + true + ); - server.get(`recent-changes/${ancestorNoteId}`) - .then(async (recentChanges) => { - // preload all notes into cache - await froca.getNotes( - recentChanges.map((r) => r.noteId), - true - ); - - const groupedByDate = groupByDate(recentChanges); - setGroupedByDate(groupedByDate); - }); - }) - } + const groupedByDate = groupByDate(recentChanges); + setGroupedByDate(groupedByDate); + }); + }, [ shown, refreshCounter ]) return ( { server.post("notes/erase-deleted-notes-now").then(() => { - setNeedsRefresh(true); + setRefreshCounter(refreshCounter + 1); toast.showMessage(t("recent_changes.deleted_notes_message")); }); }} @@ -113,10 +106,6 @@ function RecentChangesTimeline({ groupedByDate, setShown }: { groupedByDate: Map } function NoteLink({ notePath, title }: { notePath: string, title: string }) { - if (!notePath || !title) { - return null; - } - const [ noteLink, setNoteLink ] = useState | null>(null); useEffect(() => { link.createLink(notePath, { diff --git a/apps/client/src/widgets/dialogs/revisions.tsx b/apps/client/src/widgets/dialogs/revisions.tsx index fd94ec130..61e1ed1db 100644 --- a/apps/client/src/widgets/dialogs/revisions.tsx +++ b/apps/client/src/widgets/dialogs/revisions.tsx @@ -201,17 +201,9 @@ function RevisionContent({ revisionItem, fullRevision }: { revisionItem?: Revisi return <>; } - switch (revisionItem.type) { - case "text": { - const contentRef = useRef(null); - useEffect(() => { - if (contentRef.current?.querySelector("span.math-tex")) { - renderMathInElement(contentRef.current, { trust: true }); - } - }); - return
- } + case "text": + return case "code": return
{content}
; case "image": @@ -263,6 +255,16 @@ function RevisionContent({ revisionItem, fullRevision }: { revisionItem?: Revisi } } +function RevisionContentText({ content }: { content: string | Buffer | undefined }) { + const contentRef = useRef(null); + useEffect(() => { + if (contentRef.current?.querySelector("span.math-tex")) { + renderMathInElement(contentRef.current, { trust: true }); + } + }, [content]); + return
+} + function RevisionFooter({ note }: { note?: FNote }) { if (!note) { return <>; diff --git a/apps/client/src/widgets/dialogs/upload_attachments.tsx b/apps/client/src/widgets/dialogs/upload_attachments.tsx index 69fbfdb8d..2a780e616 100644 --- a/apps/client/src/widgets/dialogs/upload_attachments.tsx +++ b/apps/client/src/widgets/dialogs/upload_attachments.tsx @@ -23,12 +23,12 @@ export default function UploadAttachmentsDialog() { setShown(true); }); - if (parentNoteId) { - useEffect(() => { - tree.getNoteTitle(parentNoteId).then((noteTitle) => - setDescription(t("upload_attachments.files_will_be_uploaded", { noteTitle }))); - }, [parentNoteId]); - } + useEffect(() => { + if (!parentNoteId) return; + + tree.getNoteTitle(parentNoteId).then((noteTitle) => + setDescription(t("upload_attachments.files_will_be_uploaded", { noteTitle }))); + }, [parentNoteId]); return ( (); - if (onShown || onHidden) { - useEffect(() => { - const modalElement = modalRef.current; - if (!modalElement) { - return; + useEffect(() => { + const modalElement = modalRef.current; + if (!modalElement) { + return; + } + if (onShown) { + modalElement.addEventListener("shown.bs.modal", onShown); + } + modalElement.addEventListener("hidden.bs.modal", () => { + onHidden(); + if (elementToFocus.current && "focus" in elementToFocus.current) { + (elementToFocus.current as HTMLElement).focus(); } + }); + return () => { if (onShown) { - modalElement.addEventListener("shown.bs.modal", onShown); + modalElement.removeEventListener("shown.bs.modal", onShown); } - modalElement.addEventListener("hidden.bs.modal", () => { - onHidden(); - if (elementToFocus.current && "focus" in elementToFocus.current) { - (elementToFocus.current as HTMLElement).focus(); - } - }); - return () => { - if (onShown) { - modalElement.removeEventListener("shown.bs.modal", onShown); - } - modalElement.removeEventListener("hidden.bs.modal", onHidden); - }; - }, [ ]); - } + modalElement.removeEventListener("hidden.bs.modal", onHidden); + }; + }, [ onShown, onHidden ]); useEffect(() => { if (!parentWidget) { diff --git a/apps/client/src/widgets/react/hooks.tsx b/apps/client/src/widgets/react/hooks.tsx index e031e2cc1..2234a58af 100644 --- a/apps/client/src/widgets/react/hooks.tsx +++ b/apps/client/src/widgets/react/hooks.tsx @@ -216,15 +216,13 @@ export function useNoteContext() { setNote(noteContext?.note); }); - useLegacyImperativeHandlers({ - setNoteContextEvent({ noteContext }: EventData<"setNoteContext">) { - setNoteContext(noteContext); - } - }, true); + const parentComponent = useContext(ParentComponent) as ReactWrappedWidget; + (parentComponent as ReactWrappedWidget & { setNoteContextEvent: (data: EventData<"setNoteContext">) => void }).setNoteContextEvent = ({ noteContext }: EventData<"setNoteContext">) => { + setNoteContext(noteContext); + } useDebugValue(() => `notePath=${notePath}, ntxId=${noteContext?.ntxId}`); - const parentComponent = useContext(ParentComponent) as ReactWrappedWidget; return { note: note, @@ -249,25 +247,21 @@ export function useNoteContext() { * @returns the value of the requested property. */ export function useNoteProperty(note: FNote | null | undefined, property: T, componentId?: string) { - if (!note) { - return null; - } - - const [, setValue ] = useState(note[property]); - const refreshValue = () => setValue(note[property]); + const [, setValue ] = useState(note?.[property]); + const refreshValue = () => setValue(note?.[property]); // Watch for note changes. - useEffect(() => refreshValue(), [ note, note[property] ]); + useEffect(() => refreshValue(), [ note, note?.[property] ]); // Watch for external changes. useTriliumEvent("entitiesReloaded", ({ loadResults }) => { - if (loadResults.isNoteReloaded(note.noteId, componentId)) { + if (loadResults.isNoteReloaded(note?.noteId, componentId)) { refreshValue(); } }); useDebugValue(property); - return note[property]; + return note?.[property]; } export function useNoteRelation(note: FNote | undefined | null, relationName: string): [string | null | undefined, (newValue: string) => void] { @@ -362,10 +356,6 @@ export function useNoteLabelBoolean(note: FNote | undefined | null, labelName: s } export function useNoteBlob(note: FNote | null | undefined): [ FBlob | null | undefined ] { - if (!note) { - return [ undefined ]; - } - const [ blob, setBlob ] = useState(); function refresh() { @@ -379,7 +369,7 @@ export function useNoteBlob(note: FNote | null | undefined): [ FBlob | null | un } }); - useDebugValue(note.noteId); + useDebugValue(note?.noteId); return [ blob ] as const; } @@ -514,13 +504,9 @@ export function useTooltip(elRef: RefObject, config: Partial, force?: boolean) { +export function useLegacyImperativeHandlers(handlers: Record) { const parentComponent = useContext(ParentComponent); - if (!force) { - useEffect(() => { - Object.assign(parentComponent as never, handlers); - }, [ handlers ]) - } else { + useEffect(() => { Object.assign(parentComponent as never, handlers); - } + }, [ handlers ]); } \ No newline at end of file diff --git a/apps/client/src/widgets/ribbon/NoteActions.tsx b/apps/client/src/widgets/ribbon/NoteActions.tsx index 6a6a0f2d3..0ff40f85c 100644 --- a/apps/client/src/widgets/ribbon/NoteActions.tsx +++ b/apps/client/src/widgets/ribbon/NoteActions.tsx @@ -20,16 +20,16 @@ interface NoteActionsProps { noteContext?: NoteContext; } -export default function NoteActions(props: NoteActionsProps) { +export default function NoteActions({ note, noteContext }: NoteActionsProps) { return ( <> - - + {note && } + {note && note.type !== "launcher" && } ); } -function RevisionsButton({ note }: NoteActionsProps) { +function RevisionsButton({ note }: { note: FNote }) { const isEnabled = !["launcher", "doc"].includes(note?.type ?? ""); return (isEnabled && @@ -42,12 +42,7 @@ function RevisionsButton({ note }: NoteActionsProps) { ); } -function NoteContextMenu(props: NoteActionsProps) { - const { note, noteContext } = props; - if (!note || note.type === "launcher") { - return <>; - } - +function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: NoteContext }) { const parentComponent = useContext(ParentComponent); const canBeConvertedToAttachment = note?.isEligibleForConversionToAttachment(); const isSearchable = ["text", "code", "book", "mindMap", "doc"].includes(note.type); @@ -65,7 +60,7 @@ function NoteContextMenu(props: NoteActionsProps) { hideToggleArrow noSelectButtonStyle > - {canBeConvertedToAttachment && } + {canBeConvertedToAttachment && } {note.type === "render" && } @@ -110,7 +105,7 @@ function CommandItem({ icon, text, title, command, disabled }: { icon: string, t >{text} } -function ConvertToAttachment({ note }: NoteActionsProps) { +function ConvertToAttachment({ note }: { note: FNote }) { return ( {t("etapi.no_tokens_yet")}; - } - const renameCallback = useCallback(async (tokenId: string, oldName: string) => { const tokenName = await dialog.prompt({ title: t("etapi.rename_token_title"), @@ -104,41 +100,45 @@ function TokenList({ tokens }: { tokens: EtapiToken[] }) { }, []); return ( -
- - - - - - - - - - {tokens.map(({ etapiTokenId, name, utcDateCreated}) => ( + tokens.length ? ( +
+
{t("etapi.token_name")}{t("etapi.created")}{t("etapi.actions")}
+ - - - + + + - ))} - -
{name}{formatDateTime(utcDateCreated)} - {etapiTokenId && ( - <> - renameCallback(etapiTokenId, name)} - /> - - deleteCallback(etapiTokenId, name)} - /> - - )} - {t("etapi.token_name")}{t("etapi.created")}{t("etapi.actions")}
-
- ) + + + {tokens.map(({ etapiTokenId, name, utcDateCreated}) => ( + + {name} + {formatDateTime(utcDateCreated)} + + {etapiTokenId && ( + <> + renameCallback(etapiTokenId, name)} + /> + + deleteCallback(etapiTokenId, name)} + /> + + )} + + + ))} + + + + ) : ( +
{t("etapi.no_tokens_yet")}
+ ) + ); }