mirror of
https://github.com/zadam/trilium.git
synced 2025-11-12 16:25:51 +01:00
refactor(react): fix a few rules of hooks violations
This commit is contained in:
@@ -6,11 +6,6 @@ import { t } from "../../services/i18n";
|
|||||||
|
|
||||||
export default function CallToActionDialog() {
|
export default function CallToActionDialog() {
|
||||||
const activeCallToActions = useMemo(() => getCallToActions(), []);
|
const activeCallToActions = useMemo(() => getCallToActions(), []);
|
||||||
|
|
||||||
if (!activeCallToActions.length) {
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [ activeIndex, setActiveIndex ] = useState(0);
|
const [ activeIndex, setActiveIndex ] = useState(0);
|
||||||
const [ shown, setShown ] = useState(true);
|
const [ shown, setShown ] = useState(true);
|
||||||
const activeItem = activeCallToActions[activeIndex];
|
const activeItem = activeCallToActions[activeIndex];
|
||||||
@@ -23,7 +18,7 @@ export default function CallToActionDialog() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (activeCallToActions.length &&
|
||||||
<Modal
|
<Modal
|
||||||
className="call-to-action"
|
className="call-to-action"
|
||||||
size="md"
|
size="md"
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ export default function NoteTypeChooserDialogComponent() {
|
|||||||
setShown(true);
|
setShown(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!noteTypes.length) {
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
note_types.getNoteTypeItems().then(noteTypes => {
|
note_types.getNoteTypeItems().then(noteTypes => {
|
||||||
let index = -1;
|
let index = -1;
|
||||||
@@ -53,8 +52,7 @@ export default function NoteTypeChooserDialogComponent() {
|
|||||||
return item;
|
return item;
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
});
|
}, []);
|
||||||
}
|
|
||||||
|
|
||||||
function onNoteTypeSelected(value: string) {
|
function onNoteTypeSelected(value: string) {
|
||||||
const [ noteType, templateNoteId ] = value.split(",");
|
const [ noteType, templateNoteId ] = value.split(",");
|
||||||
|
|||||||
@@ -18,21 +18,15 @@ import { useTriliumEvent } from "../react/hooks";
|
|||||||
export default function RecentChangesDialog() {
|
export default function RecentChangesDialog() {
|
||||||
const [ ancestorNoteId, setAncestorNoteId ] = useState<string>();
|
const [ ancestorNoteId, setAncestorNoteId ] = useState<string>();
|
||||||
const [ groupedByDate, setGroupedByDate ] = useState<Map<string, RecentChangeRow[]>>();
|
const [ groupedByDate, setGroupedByDate ] = useState<Map<string, RecentChangeRow[]>>();
|
||||||
const [ needsRefresh, setNeedsRefresh ] = useState(false);
|
const [ refreshCounter, setRefreshCounter ] = useState(0);
|
||||||
const [ shown, setShown ] = useState(false);
|
const [ shown, setShown ] = useState(false);
|
||||||
|
|
||||||
useTriliumEvent("showRecentChanges", ({ ancestorNoteId }) => {
|
useTriliumEvent("showRecentChanges", ({ ancestorNoteId }) => {
|
||||||
setNeedsRefresh(true);
|
|
||||||
setAncestorNoteId(ancestorNoteId ?? hoisted_note.getHoistedNoteId());
|
setAncestorNoteId(ancestorNoteId ?? hoisted_note.getHoistedNoteId());
|
||||||
setShown(true);
|
setShown(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!groupedByDate || needsRefresh) {
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (needsRefresh) {
|
|
||||||
setNeedsRefresh(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
server.get<RecentChangeRow[]>(`recent-changes/${ancestorNoteId}`)
|
server.get<RecentChangeRow[]>(`recent-changes/${ancestorNoteId}`)
|
||||||
.then(async (recentChanges) => {
|
.then(async (recentChanges) => {
|
||||||
// preload all notes into cache
|
// preload all notes into cache
|
||||||
@@ -44,8 +38,7 @@ export default function RecentChangesDialog() {
|
|||||||
const groupedByDate = groupByDate(recentChanges);
|
const groupedByDate = groupByDate(recentChanges);
|
||||||
setGroupedByDate(groupedByDate);
|
setGroupedByDate(groupedByDate);
|
||||||
});
|
});
|
||||||
})
|
}, [ shown, refreshCounter ])
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@@ -60,7 +53,7 @@ export default function RecentChangesDialog() {
|
|||||||
style={{ padding: "0 10px" }}
|
style={{ padding: "0 10px" }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
server.post("notes/erase-deleted-notes-now").then(() => {
|
server.post("notes/erase-deleted-notes-now").then(() => {
|
||||||
setNeedsRefresh(true);
|
setRefreshCounter(refreshCounter + 1);
|
||||||
toast.showMessage(t("recent_changes.deleted_notes_message"));
|
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 }) {
|
function NoteLink({ notePath, title }: { notePath: string, title: string }) {
|
||||||
if (!notePath || !title) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [ noteLink, setNoteLink ] = useState<JQuery<HTMLElement> | null>(null);
|
const [ noteLink, setNoteLink ] = useState<JQuery<HTMLElement> | null>(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
link.createLink(notePath, {
|
link.createLink(notePath, {
|
||||||
|
|||||||
@@ -201,17 +201,9 @@ function RevisionContent({ revisionItem, fullRevision }: { revisionItem?: Revisi
|
|||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
switch (revisionItem.type) {
|
switch (revisionItem.type) {
|
||||||
case "text": {
|
case "text":
|
||||||
const contentRef = useRef<HTMLDivElement>(null);
|
return <RevisionContentText content={content} />
|
||||||
useEffect(() => {
|
|
||||||
if (contentRef.current?.querySelector("span.math-tex")) {
|
|
||||||
renderMathInElement(contentRef.current, { trust: true });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return <div ref={contentRef} className="ck-content" dangerouslySetInnerHTML={{ __html: content as string }}></div>
|
|
||||||
}
|
|
||||||
case "code":
|
case "code":
|
||||||
return <pre style={CODE_STYLE}>{content}</pre>;
|
return <pre style={CODE_STYLE}>{content}</pre>;
|
||||||
case "image":
|
case "image":
|
||||||
@@ -263,6 +255,16 @@ function RevisionContent({ revisionItem, fullRevision }: { revisionItem?: Revisi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function RevisionContentText({ content }: { content: string | Buffer<ArrayBufferLike> | undefined }) {
|
||||||
|
const contentRef = useRef<HTMLDivElement>(null);
|
||||||
|
useEffect(() => {
|
||||||
|
if (contentRef.current?.querySelector("span.math-tex")) {
|
||||||
|
renderMathInElement(contentRef.current, { trust: true });
|
||||||
|
}
|
||||||
|
}, [content]);
|
||||||
|
return <div ref={contentRef} className="ck-content" dangerouslySetInnerHTML={{ __html: content as string }}></div>
|
||||||
|
}
|
||||||
|
|
||||||
function RevisionFooter({ note }: { note?: FNote }) {
|
function RevisionFooter({ note }: { note?: FNote }) {
|
||||||
if (!note) {
|
if (!note) {
|
||||||
return <></>;
|
return <></>;
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ export default function UploadAttachmentsDialog() {
|
|||||||
setShown(true);
|
setShown(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (parentNoteId) {
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!parentNoteId) return;
|
||||||
|
|
||||||
tree.getNoteTitle(parentNoteId).then((noteTitle) =>
|
tree.getNoteTitle(parentNoteId).then((noteTitle) =>
|
||||||
setDescription(t("upload_attachments.files_will_be_uploaded", { noteTitle })));
|
setDescription(t("upload_attachments.files_will_be_uploaded", { noteTitle })));
|
||||||
}, [parentNoteId]);
|
}, [parentNoteId]);
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ export default function Modal({ children, className, size, title, header, footer
|
|||||||
const parentWidget = useContext(ParentComponent);
|
const parentWidget = useContext(ParentComponent);
|
||||||
const elementToFocus = useRef<Element | null>();
|
const elementToFocus = useRef<Element | null>();
|
||||||
|
|
||||||
if (onShown || onHidden) {
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const modalElement = modalRef.current;
|
const modalElement = modalRef.current;
|
||||||
if (!modalElement) {
|
if (!modalElement) {
|
||||||
@@ -92,8 +91,7 @@ export default function Modal({ children, className, size, title, header, footer
|
|||||||
}
|
}
|
||||||
modalElement.removeEventListener("hidden.bs.modal", onHidden);
|
modalElement.removeEventListener("hidden.bs.modal", onHidden);
|
||||||
};
|
};
|
||||||
}, [ ]);
|
}, [ onShown, onHidden ]);
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!parentWidget) {
|
if (!parentWidget) {
|
||||||
|
|||||||
@@ -216,15 +216,13 @@ export function useNoteContext() {
|
|||||||
setNote(noteContext?.note);
|
setNote(noteContext?.note);
|
||||||
});
|
});
|
||||||
|
|
||||||
useLegacyImperativeHandlers({
|
const parentComponent = useContext(ParentComponent) as ReactWrappedWidget;
|
||||||
setNoteContextEvent({ noteContext }: EventData<"setNoteContext">) {
|
(parentComponent as ReactWrappedWidget & { setNoteContextEvent: (data: EventData<"setNoteContext">) => void }).setNoteContextEvent = ({ noteContext }: EventData<"setNoteContext">) => {
|
||||||
setNoteContext(noteContext);
|
setNoteContext(noteContext);
|
||||||
}
|
}
|
||||||
}, true);
|
|
||||||
|
|
||||||
useDebugValue(() => `notePath=${notePath}, ntxId=${noteContext?.ntxId}`);
|
useDebugValue(() => `notePath=${notePath}, ntxId=${noteContext?.ntxId}`);
|
||||||
|
|
||||||
const parentComponent = useContext(ParentComponent) as ReactWrappedWidget;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
note: note,
|
note: note,
|
||||||
@@ -249,25 +247,21 @@ export function useNoteContext() {
|
|||||||
* @returns the value of the requested property.
|
* @returns the value of the requested property.
|
||||||
*/
|
*/
|
||||||
export function useNoteProperty<T extends keyof FNote>(note: FNote | null | undefined, property: T, componentId?: string) {
|
export function useNoteProperty<T extends keyof FNote>(note: FNote | null | undefined, property: T, componentId?: string) {
|
||||||
if (!note) {
|
const [, setValue ] = useState<FNote[T] | undefined>(note?.[property]);
|
||||||
return null;
|
const refreshValue = () => setValue(note?.[property]);
|
||||||
}
|
|
||||||
|
|
||||||
const [, setValue ] = useState<FNote[T]>(note[property]);
|
|
||||||
const refreshValue = () => setValue(note[property]);
|
|
||||||
|
|
||||||
// Watch for note changes.
|
// Watch for note changes.
|
||||||
useEffect(() => refreshValue(), [ note, note[property] ]);
|
useEffect(() => refreshValue(), [ note, note?.[property] ]);
|
||||||
|
|
||||||
// Watch for external changes.
|
// Watch for external changes.
|
||||||
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
|
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
|
||||||
if (loadResults.isNoteReloaded(note.noteId, componentId)) {
|
if (loadResults.isNoteReloaded(note?.noteId, componentId)) {
|
||||||
refreshValue();
|
refreshValue();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
useDebugValue(property);
|
useDebugValue(property);
|
||||||
return note[property];
|
return note?.[property];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useNoteRelation(note: FNote | undefined | null, relationName: string): [string | null | undefined, (newValue: string) => void] {
|
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 ] {
|
export function useNoteBlob(note: FNote | null | undefined): [ FBlob | null | undefined ] {
|
||||||
if (!note) {
|
|
||||||
return [ undefined ];
|
|
||||||
}
|
|
||||||
|
|
||||||
const [ blob, setBlob ] = useState<FBlob | null>();
|
const [ blob, setBlob ] = useState<FBlob | null>();
|
||||||
|
|
||||||
function refresh() {
|
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;
|
return [ blob ] as const;
|
||||||
}
|
}
|
||||||
@@ -514,13 +504,9 @@ export function useTooltip(elRef: RefObject<HTMLElement>, config: Partial<Toolti
|
|||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
export function useLegacyImperativeHandlers(handlers: Record<string, Function>, force?: boolean) {
|
export function useLegacyImperativeHandlers(handlers: Record<string, Function>) {
|
||||||
const parentComponent = useContext(ParentComponent);
|
const parentComponent = useContext(ParentComponent);
|
||||||
if (!force) {
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Object.assign(parentComponent as never, handlers);
|
Object.assign(parentComponent as never, handlers);
|
||||||
}, [ handlers ])
|
}, [ handlers ]);
|
||||||
} else {
|
|
||||||
Object.assign(parentComponent as never, handlers);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -20,16 +20,16 @@ interface NoteActionsProps {
|
|||||||
noteContext?: NoteContext;
|
noteContext?: NoteContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NoteActions(props: NoteActionsProps) {
|
export default function NoteActions({ note, noteContext }: NoteActionsProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<RevisionsButton {...props} />
|
{note && <RevisionsButton note={note} />}
|
||||||
<NoteContextMenu {...props} />
|
{note && note.type !== "launcher" && <NoteContextMenu note={note as FNote} noteContext={noteContext}/>}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function RevisionsButton({ note }: NoteActionsProps) {
|
function RevisionsButton({ note }: { note: FNote }) {
|
||||||
const isEnabled = !["launcher", "doc"].includes(note?.type ?? "");
|
const isEnabled = !["launcher", "doc"].includes(note?.type ?? "");
|
||||||
|
|
||||||
return (isEnabled &&
|
return (isEnabled &&
|
||||||
@@ -42,12 +42,7 @@ function RevisionsButton({ note }: NoteActionsProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function NoteContextMenu(props: NoteActionsProps) {
|
function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: NoteContext }) {
|
||||||
const { note, noteContext } = props;
|
|
||||||
if (!note || note.type === "launcher") {
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const parentComponent = useContext(ParentComponent);
|
const parentComponent = useContext(ParentComponent);
|
||||||
const canBeConvertedToAttachment = note?.isEligibleForConversionToAttachment();
|
const canBeConvertedToAttachment = note?.isEligibleForConversionToAttachment();
|
||||||
const isSearchable = ["text", "code", "book", "mindMap", "doc"].includes(note.type);
|
const isSearchable = ["text", "code", "book", "mindMap", "doc"].includes(note.type);
|
||||||
@@ -65,7 +60,7 @@ function NoteContextMenu(props: NoteActionsProps) {
|
|||||||
hideToggleArrow
|
hideToggleArrow
|
||||||
noSelectButtonStyle
|
noSelectButtonStyle
|
||||||
>
|
>
|
||||||
{canBeConvertedToAttachment && <ConvertToAttachment {...props} /> }
|
{canBeConvertedToAttachment && <ConvertToAttachment note={note} /> }
|
||||||
{note.type === "render" && <CommandItem command="renderActiveNote" icon="bx bx-extension" text={t("note_actions.re_render_note")} />}
|
{note.type === "render" && <CommandItem command="renderActiveNote" icon="bx bx-extension" text={t("note_actions.re_render_note")} />}
|
||||||
<CommandItem command="findInText" icon="bx bx-search" disabled={!isSearchable} text={t("note_actions.search_in_note")} />
|
<CommandItem command="findInText" icon="bx bx-search" disabled={!isSearchable} text={t("note_actions.search_in_note")} />
|
||||||
<CommandItem command="printActiveNote" icon="bx bx-printer" disabled={!isPrintable} text={t("note_actions.print_note")} />
|
<CommandItem command="printActiveNote" icon="bx bx-printer" disabled={!isPrintable} text={t("note_actions.print_note")} />
|
||||||
@@ -110,7 +105,7 @@ function CommandItem({ icon, text, title, command, disabled }: { icon: string, t
|
|||||||
>{text}</FormListItem>
|
>{text}</FormListItem>
|
||||||
}
|
}
|
||||||
|
|
||||||
function ConvertToAttachment({ note }: NoteActionsProps) {
|
function ConvertToAttachment({ note }: { note: FNote }) {
|
||||||
return (
|
return (
|
||||||
<FormListItem
|
<FormListItem
|
||||||
icon="bx bx-paperclip"
|
icon="bx bx-paperclip"
|
||||||
|
|||||||
@@ -77,10 +77,6 @@ export default function EtapiSettings() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function TokenList({ tokens }: { tokens: EtapiToken[] }) {
|
function TokenList({ tokens }: { tokens: EtapiToken[] }) {
|
||||||
if (!tokens.length) {
|
|
||||||
return <div>{t("etapi.no_tokens_yet")}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const renameCallback = useCallback<RenameTokenCallback>(async (tokenId: string, oldName: string) => {
|
const renameCallback = useCallback<RenameTokenCallback>(async (tokenId: string, oldName: string) => {
|
||||||
const tokenName = await dialog.prompt({
|
const tokenName = await dialog.prompt({
|
||||||
title: t("etapi.rename_token_title"),
|
title: t("etapi.rename_token_title"),
|
||||||
@@ -104,6 +100,7 @@ function TokenList({ tokens }: { tokens: EtapiToken[] }) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
tokens.length ? (
|
||||||
<div style={{ overflow: "auto", height: "500px"}}>
|
<div style={{ overflow: "auto", height: "500px"}}>
|
||||||
<table className="table table-stripped">
|
<table className="table table-stripped">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -140,5 +137,8 @@ function TokenList({ tokens }: { tokens: EtapiToken[] }) {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>{t("etapi.no_tokens_yet")}</div>
|
||||||
)
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user