chore(react/type_widget): reflect note type changes

This commit is contained in:
Elian Doran
2025-09-21 21:12:22 +03:00
parent 5d833c1ac4
commit b73ea6ac4f
2 changed files with 49 additions and 39 deletions

View File

@@ -20,6 +20,7 @@ import Mermaid from "./type_widgets/Mermaid";
import MindMap from "./type_widgets/MindMap"; import MindMap from "./type_widgets/MindMap";
import { AttachmentDetail, AttachmentList } from "./type_widgets/Attachment"; import { AttachmentDetail, AttachmentList } from "./type_widgets/Attachment";
import ReadOnlyText from "./type_widgets/text/ReadOnlyText"; import ReadOnlyText from "./type_widgets/text/ReadOnlyText";
import attributes from "../services/attributes";
/** /**
* A `NoteType` altered by the note detail widget, taking into consideration whether the note is editable or not and adding special note types such as an empty one, * A `NoteType` altered by the note detail widget, taking into consideration whether the note is editable or not and adding special note types such as an empty one,
@@ -35,7 +36,7 @@ type ExtendedNoteType = Exclude<NoteType, "launcher" | "text" | "code"> | "empty
* - Focuses the content when switching tabs. * - Focuses the content when switching tabs.
*/ */
export default function NoteDetail() { export default function NoteDetail() {
const { note, type, noteContext, parentComponent } = useNoteInfo(); const { note, type, mime, noteContext, parentComponent } = useNoteInfo();
const { ntxId, viewScope } = noteContext ?? {}; const { ntxId, viewScope } = noteContext ?? {};
const [ correspondingWidget, setCorrespondingWidget ] = useState<VNode>(); const [ correspondingWidget, setCorrespondingWidget ] = useState<VNode>();
const isFullHeight = checkFullHeight(noteContext, type); const isFullHeight = checkFullHeight(noteContext, type);
@@ -49,6 +50,48 @@ export default function NoteDetail() {
}; };
useEffect(() => setCorrespondingWidget(getCorrespondingWidget(type, props)), [ note, viewScope, type ]); useEffect(() => setCorrespondingWidget(getCorrespondingWidget(type, props)), [ note, viewScope, type ]);
// Detect note type changes.
useTriliumEvent("entitiesReloaded", async ({ loadResults }) => {
if (!note) return;
// we're detecting note type change on the note_detail level, but triggering the noteTypeMimeChanged
// globally, so it gets also to e.g. ribbon components. But this means that the event can be generated multiple
// times if the same note is open in several tabs.
if (note.noteId && loadResults.isNoteContentReloaded(note.noteId, parentComponent.componentId)) {
// probably incorrect event
// calling this.refresh() is not enough since the event needs to be propagated to children as well
// FIXME: create a separate event to force hierarchical refresh
// this uses handleEvent to make sure that the ordinary content updates are propagated only in the subtree
// to avoid the problem in #3365
parentComponent.handleEvent("noteTypeMimeChanged", { noteId: note.noteId });
} else if (note.noteId
&& loadResults.isNoteReloaded(note.noteId, parentComponent.componentId)
&& (type !== (await getWidgetType(note, noteContext)) || mime !== note?.mime)) {
// this needs to have a triggerEvent so that e.g., note type (not in the component subtree) is updated
parentComponent.triggerEvent("noteTypeMimeChanged", { noteId: note.noteId });
} else {
const attrs = loadResults.getAttributeRows();
const label = attrs.find(
(attr) =>
attr.type === "label" &&
["readOnly", "autoReadOnlyDisabled", "cssClass", "displayRelations", "hideRelations"].includes(attr.name ?? "") &&
attributes.isAffecting(attr, note)
);
const relation = attrs.find((attr) => attr.type === "relation" && ["template", "inherit", "renderNote"]
.includes(attr.name ?? "") && attributes.isAffecting(attr, note));
if (note.noteId && (label || relation)) {
// probably incorrect event
// calling this.refresh() is not enough since the event needs to be propagated to children as well
parentComponent.triggerEvent("noteTypeMimeChanged", { noteId: note.noteId });
}
}
});
// Automatically focus the editor. // Automatically focus the editor.
useTriliumEvent("activeNoteChanged", () => { useTriliumEvent("activeNoteChanged", () => {
// Restore focus to the editor when switching tabs, but only if the note tree is not already focused. // Restore focus to the editor when switching tabs, but only if the note tree is not already focused.
@@ -69,11 +112,13 @@ function useNoteInfo() {
const { note: actualNote, noteContext, parentComponent } = useNoteContext(); const { note: actualNote, noteContext, parentComponent } = useNoteContext();
const [ note, setNote ] = useState<FNote | null | undefined>(); const [ note, setNote ] = useState<FNote | null | undefined>();
const [ type, setType ] = useState<ExtendedNoteType>(); const [ type, setType ] = useState<ExtendedNoteType>();
const [ mime, setMime ] = useState<string>();
function refresh() { function refresh() {
getWidgetType(actualNote, noteContext).then(type => { getWidgetType(actualNote, noteContext).then(type => {
setNote(actualNote); setNote(actualNote);
setType(type); setType(type);
setMime(actualNote?.mime);
}); });
} }
@@ -82,8 +127,9 @@ function useNoteInfo() {
if (eventNoteContext?.ntxId !== noteContext?.ntxId) return; if (eventNoteContext?.ntxId !== noteContext?.ntxId) return;
refresh(); refresh();
}); });
useTriliumEvent("noteTypeMimeChanged", refresh);
return { note, type, noteContext, parentComponent }; return { note, type, mime, noteContext, parentComponent };
} }
function getCorrespondingWidget(noteType: ExtendedNoteType | undefined, props: TypeWidgetProps) { function getCorrespondingWidget(noteType: ExtendedNoteType | undefined, props: TypeWidgetProps) {

View File

@@ -187,42 +187,6 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
} }
} }
async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
// we're detecting note type change on the note_detail level, but triggering the noteTypeMimeChanged
// globally, so it gets also to e.g. ribbon components. But this means that the event can be generated multiple
// times if the same note is open in several tabs.
if (this.noteId && loadResults.isNoteContentReloaded(this.noteId, this.componentId)) {
// probably incorrect event
// calling this.refresh() is not enough since the event needs to be propagated to children as well
// FIXME: create a separate event to force hierarchical refresh
// this uses handleEvent to make sure that the ordinary content updates are propagated only in the subtree
// to avoid the problem in #3365
this.handleEvent("noteTypeMimeChanged", { noteId: this.noteId });
} else if (this.noteId && loadResults.isNoteReloaded(this.noteId, this.componentId) && (this.type !== (await this.getWidgetType()) || this.mime !== this.note?.mime)) {
// this needs to have a triggerEvent so that e.g., note type (not in the component subtree) is updated
this.triggerEvent("noteTypeMimeChanged", { noteId: this.noteId });
} else {
const attrs = loadResults.getAttributeRows();
const label = attrs.find(
(attr) =>
attr.type === "label" &&
["readOnly", "autoReadOnlyDisabled", "cssClass", "displayRelations", "hideRelations"].includes(attr.name ?? "") &&
attributeService.isAffecting(attr, this.note)
);
const relation = attrs.find((attr) => attr.type === "relation" && ["template", "inherit", "renderNote"].includes(attr.name ?? "") && attributeService.isAffecting(attr, this.note));
if (this.noteId && (label || relation)) {
// probably incorrect event
// calling this.refresh() is not enough since the event needs to be propagated to children as well
this.triggerEvent("noteTypeMimeChanged", { noteId: this.noteId });
}
}
}
beforeUnloadEvent() { beforeUnloadEvent() {
return this.spacedUpdate.isAllSavedAndTriggerUpdate(); return this.spacedUpdate.isAllSavedAndTriggerUpdate();
} }