diff --git a/src/public/app/entities/fnote.ts b/src/public/app/entities/fnote.ts index 9dea5071e..4da479d16 100644 --- a/src/public/app/entities/fnote.ts +++ b/src/public/app/entities/fnote.ts @@ -515,10 +515,10 @@ class FNote { } /** - * @param [name] - label name to filter + * @param name - label name to filter * @returns all note's labels (attributes with type label), including inherited ones */ - getOwnedLabels(name: string) { + getOwnedLabels(name?: string) { return this.getOwnedAttributes(LABEL, name); } diff --git a/src/public/app/widgets/note_icon.js b/src/public/app/widgets/note_icon.ts similarity index 73% rename from src/public/app/widgets/note_icon.js rename to src/public/app/widgets/note_icon.ts index cbf26e827..91094149c 100644 --- a/src/public/app/widgets/note_icon.js +++ b/src/public/app/widgets/note_icon.ts @@ -2,6 +2,8 @@ import { t } from "../services/i18n.js"; import NoteContextAwareWidget from "./note_context_aware_widget.js"; import attributeService from "../services/attributes.js"; import server from "../services/server.js"; +import type FNote from "../entities/fnote.js"; +import type { EventData } from "../components/app_context.js"; const TPL = ` `; +interface Icon { + className?: string; + name: string; +} + +interface IconToCountCache { + iconClassToCountMap: Record; +} + export default class NoteIconWidget extends NoteContextAwareWidget { + + private $icon!: JQuery; + private $iconList!: JQuery; + private $iconCategory!: JQuery; + private $iconSearch!: JQuery; + private $notePathList!: JQuery; + private iconToCountCache!: Promise | null; + doRender() { this.$widget = $(TPL); this.$icon = this.$widget.find("button.note-icon"); @@ -87,7 +106,9 @@ export default class NoteIconWidget extends NoteContextAwareWidget { this.$iconList.on("click", "span", async (e) => { const clazz = $(e.target).attr("class"); - await attributeService.setLabel(this.noteId, this.note.hasOwnedLabel("workspace") ? "workspaceIconClass" : "iconClass", clazz); + if (this.noteId && this.note) { + await attributeService.setLabel(this.noteId, this.note.hasOwnedLabel("workspace") ? "workspaceIconClass" : "iconClass", clazz); + } }); this.$iconCategory = this.$widget.find("select[name='icon-category']"); @@ -113,18 +134,18 @@ export default class NoteIconWidget extends NoteContextAwareWidget { }); } - async refreshWithNote(note) { + async refreshWithNote(note: FNote) { this.$icon.removeClass().addClass(`${note.getIcon()} note-icon`); } - async entitiesReloadedEvent({ loadResults }) { - if (loadResults.isNoteReloaded(this.noteId)) { + async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) { + if (this.noteId && loadResults.isNoteReloaded(this.noteId)) { this.refresh(); return; } for (const attr of loadResults.getAttributeRows()) { - if (attr.type === "label" && ["iconClass", "workspaceIconClass"].includes(attr.name) && attributeService.isAffecting(attr, this.note)) { + if (attr.type === "label" && ["iconClass", "workspaceIconClass"].includes(attr.name ?? "") && attributeService.isAffecting(attr, this.note)) { this.refresh(); break; } @@ -141,14 +162,18 @@ export default class NoteIconWidget extends NoteContextAwareWidget { this.$iconList.append( $(`
`).append( $(``).on("click", () => - this.getIconLabels().forEach((label) => attributeService.removeAttributeById(this.noteId, label.attributeId)) + this.getIconLabels().forEach((label) => { + if (this.noteId) { + attributeService.removeAttributeById(this.noteId, label.attributeId); + } + }) ) ) ); } - const categoryId = parseInt(this.$iconCategory.find("option:selected").val()); - const search = this.$iconSearch.val().trim().toLowerCase(); + const categoryId = parseInt(String(this.$iconCategory.find("option:selected")?.val())); + const search = String(this.$iconSearch.val())?.trim()?.toLowerCase(); const filteredIcons = icons.filter((icon) => { if (categoryId && icon.category_id !== categoryId) { @@ -164,12 +189,14 @@ export default class NoteIconWidget extends NoteContextAwareWidget { return true; }); - filteredIcons.sort((a, b) => { - const countA = iconToCount[a.className] || 0; - const countB = iconToCount[b.className] || 0; + if (iconToCount) { + filteredIcons.sort((a, b) => { + const countA = iconToCount[a.className ?? ""] || 0; + const countB = iconToCount[b.className ?? ""] || 0; - return countB - countA; - }); + return countB - countA; + }); + } for (const icon of filteredIcons) { this.$iconList.append(this.renderIcon(icon)); @@ -180,20 +207,23 @@ export default class NoteIconWidget extends NoteContextAwareWidget { async getIconToCountMap() { if (!this.iconToCountCache) { - this.iconToCountCache = server.get("other/icon-usage"); + this.iconToCountCache = server.get("other/icon-usage"); setTimeout(() => (this.iconToCountCache = null), 20000); // invalidate cache after 20 seconds } - return (await this.iconToCountCache).iconClassToCountMap; + return (await this.iconToCountCache)?.iconClassToCountMap; } - renderIcon(icon) { + renderIcon(icon: Icon) { return $("") .addClass("bx " + icon.className) .attr("title", icon.name); } getIconLabels() { + if (!this.note) { + return []; + } return this.note.getOwnedLabels().filter((label) => ["workspaceIconClass", "iconClass"].includes(label.name)); } }