diff --git a/src/public/app/components/app_context.ts b/src/public/app/components/app_context.ts index e9084c4da..63e3144c2 100644 --- a/src/public/app/components/app_context.ts +++ b/src/public/app/components/app_context.ts @@ -113,6 +113,8 @@ export type CommandMappings = { openNoteInNewWindow: CommandData; hideLeftPane: CommandData; showLeftPane: CommandData; + leaveProtectedSession: CommandData; + enterProtectedSession: CommandData; openInTab: ContextMenuCommandData; openNoteInSplit: ContextMenuCommandData; @@ -214,6 +216,9 @@ export type CommandMappings = { scrollContainerToCommand: CommandData & { position: number; }; + moveThisNoteSplit: CommandData & { + isMovingLeft: boolean; + }; // Geomap deleteFromMap: { noteId: string }, @@ -295,6 +300,7 @@ type EventMappings = { noteContextReorderEvent: { oldMainNtxId: string; newMainNtxId: string; + ntxIdsInOrder: string[]; }; newNoteContextCreated: { noteContext: NoteContext; @@ -303,7 +309,7 @@ type EventMappings = { ntxIds: string[]; }; exportSvg: { - ntxId: string; + ntxId: string | null | undefined; }; geoMapCreateChildNote: { ntxId: string | null | undefined; // TODO: deduplicate ntxId diff --git a/src/public/app/widgets/bookmark_switch.js b/src/public/app/widgets/bookmark_switch.ts similarity index 66% rename from src/public/app/widgets/bookmark_switch.js rename to src/public/app/widgets/bookmark_switch.ts index 1bd28ccd2..8ec8b88b5 100644 --- a/src/public/app/widgets/bookmark_switch.js +++ b/src/public/app/widgets/bookmark_switch.ts @@ -2,13 +2,23 @@ import SwitchWidget from "./switch.js"; import server from "../services/server.js"; import toastService from "../services/toast.js"; import { t } from "../services/i18n.js"; +import type FNote from "../entities/fnote.js"; +import type { EventData } from "../components/app_context.js"; + +// TODO: Deduplicate +type Response = { + success: true; +} | { + success: false; + message: string; +} export default class BookmarkSwitchWidget extends SwitchWidget { isEnabled() { return ( super.isEnabled() && // it's not possible to bookmark root because that would clone it under bookmarks and thus create a cycle - !["root", "_hidden"].includes(this.noteId) + !["root", "_hidden"].includes(this.noteId ?? "") ); } @@ -22,21 +32,21 @@ export default class BookmarkSwitchWidget extends SwitchWidget { this.switchOffTooltip = t("bookmark_switch.remove_bookmark"); } - async toggle(state) { - const resp = await server.put(`notes/${this.noteId}/toggle-in-parent/_lbBookmarks/${!!state}`); + async toggle(state: boolean | null | undefined) { + const resp = await server.put(`notes/${this.noteId}/toggle-in-parent/_lbBookmarks/${!!state}`); if (!resp.success) { toastService.showError(resp.message); } } - async refreshWithNote(note) { + async refreshWithNote(note: FNote) { const isBookmarked = !!note.getParentBranches().find((b) => b.parentNoteId === "_lbBookmarks"); this.isToggled = isBookmarked; } - entitiesReloadedEvent({ loadResults }) { + entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) { if (loadResults.getBranchRows().find((b) => b.noteId === this.noteId)) { this.refresh(); } diff --git a/src/public/app/widgets/buttons/abstract_button.ts b/src/public/app/widgets/buttons/abstract_button.ts index c30c23593..2ba16f8cc 100644 --- a/src/public/app/widgets/buttons/abstract_button.ts +++ b/src/public/app/widgets/buttons/abstract_button.ts @@ -19,7 +19,7 @@ export default class AbstractButtonWidget string; export default class ButtonFromNoteWidget extends CommandButtonWidget { + constructor() { super(); this.settings.buttonNoteIdProvider = null; } - buttonNoteIdProvider(provider) { + buttonNoteIdProvider(provider: ButtonNoteIdProvider) { this.settings.buttonNoteIdProvider = provider; return this; } @@ -21,6 +25,11 @@ export default class ButtonFromNoteWidget extends CommandButtonWidget { } updateIcon() { + if (!this.settings.buttonNoteIdProvider) { + console.error(`buttonNoteId for '${this.componentId}' is not defined.`); + return; + } + const buttonNoteId = this.settings.buttonNoteIdProvider(); if (!buttonNoteId) { @@ -29,13 +38,18 @@ export default class ButtonFromNoteWidget extends CommandButtonWidget { } froca.getNote(buttonNoteId).then((note) => { - this.settings.icon = note.getIcon(); + const icon = note?.getIcon(); + if (icon) { + this.settings.icon = icon; + } this.refreshIcon(); }); } - entitiesReloadedEvent({ loadResults }) { + entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) { + // TODO: this seems incorrect + //@ts-ignore const buttonNote = froca.getNoteFromCache(this.buttonNoteIdProvider()); if (!buttonNote) { diff --git a/src/public/app/widgets/buttons/close_pane_button.js b/src/public/app/widgets/buttons/close_pane_button.ts similarity index 85% rename from src/public/app/widgets/buttons/close_pane_button.js rename to src/public/app/widgets/buttons/close_pane_button.ts index c41bdfc10..09e9aef11 100644 --- a/src/public/app/widgets/buttons/close_pane_button.js +++ b/src/public/app/widgets/buttons/close_pane_button.ts @@ -1,3 +1,4 @@ +import type { EventData } from "../../components/app_context.js"; import { t } from "../../services/i18n.js"; import OnClickButtonWidget from "./onclick_button.js"; @@ -11,7 +12,7 @@ export default class ClosePaneButton extends OnClickButtonWidget { ); } - async noteContextReorderEvent({ ntxIdsInOrder }) { + async noteContextReorderEvent({ ntxIdsInOrder }: EventData<"noteContextReorderEvent">) { this.refresh(); } diff --git a/src/public/app/widgets/buttons/command_button.ts b/src/public/app/widgets/buttons/command_button.ts index 8929f49a8..ba32fb7f8 100644 --- a/src/public/app/widgets/buttons/command_button.ts +++ b/src/public/app/widgets/buttons/command_button.ts @@ -1,6 +1,7 @@ import type { CommandNames } from "../../components/app_context.js"; import keyboardActionsService, { type Action } from "../../services/keyboard_actions.js"; import AbstractButtonWidget, { type AbstractButtonWidgetSettings } from "./abstract_button.js"; +import type { ButtonNoteIdProvider } from "./button_from_note.js"; let actions: Action[]; @@ -13,6 +14,7 @@ type CommandOrCallback = CommandNames | (() => CommandNames); interface CommandButtonWidgetSettings extends AbstractButtonWidgetSettings { command?: CommandOrCallback; onClick?: ClickHandler; + buttonNoteIdProvider?: ButtonNoteIdProvider | null; } export default class CommandButtonWidget extends AbstractButtonWidget { diff --git a/src/public/app/widgets/buttons/create_pane_button.js b/src/public/app/widgets/buttons/create_pane_button.ts similarity index 100% rename from src/public/app/widgets/buttons/create_pane_button.js rename to src/public/app/widgets/buttons/create_pane_button.ts diff --git a/src/public/app/widgets/buttons/move_pane_button.js b/src/public/app/widgets/buttons/move_pane_button.ts similarity index 95% rename from src/public/app/widgets/buttons/move_pane_button.js rename to src/public/app/widgets/buttons/move_pane_button.ts index fa1a73783..b1df66991 100644 --- a/src/public/app/widgets/buttons/move_pane_button.js +++ b/src/public/app/widgets/buttons/move_pane_button.ts @@ -3,7 +3,10 @@ import appContext from "../../components/app_context.js"; import { t } from "../../services/i18n.js"; export default class MovePaneButton extends OnClickButtonWidget { - constructor(isMovingLeft) { + + private isMovingLeft: boolean; + + constructor(isMovingLeft: boolean) { super(); this.isMovingLeft = isMovingLeft; diff --git a/src/public/app/widgets/buttons/open_note_button_widget.js b/src/public/app/widgets/buttons/open_note_button_widget.ts similarity index 72% rename from src/public/app/widgets/buttons/open_note_button_widget.js rename to src/public/app/widgets/buttons/open_note_button_widget.ts index 279c5d670..950ded0a6 100644 --- a/src/public/app/widgets/buttons/open_note_button_widget.js +++ b/src/public/app/widgets/buttons/open_note_button_widget.ts @@ -2,9 +2,13 @@ import OnClickButtonWidget from "./onclick_button.js"; import linkContextMenuService from "../../menus/link_context_menu.js"; import utils from "../../services/utils.js"; import appContext from "../../components/app_context.js"; +import type FNote from "../../entities/fnote.js"; export default class OpenNoteButtonWidget extends OnClickButtonWidget { - constructor(noteToOpen) { + + private noteToOpen: FNote; + + constructor(noteToOpen: FNote) { super(); this.noteToOpen = noteToOpen; @@ -13,10 +17,14 @@ export default class OpenNoteButtonWidget extends OnClickButtonWidget { .icon(() => this.noteToOpen.getIcon()) .onClick((widget, evt) => this.launch(evt)) .onAuxClick((widget, evt) => this.launch(evt)) - .onContextMenu((evt) => linkContextMenuService.openContextMenu(this.noteToOpen.noteId, evt)); + .onContextMenu((evt) => { + if (evt) { + linkContextMenuService.openContextMenu(this.noteToOpen.noteId, evt); + } + }); } - async launch(evt) { + async launch(evt: JQuery.ClickEvent | JQuery.TriggeredEvent | JQuery.ContextMenuEvent) { if (evt.which === 3) { return; } diff --git a/src/public/app/widgets/buttons/protected_session_status.js b/src/public/app/widgets/buttons/protected_session_status.ts similarity index 100% rename from src/public/app/widgets/buttons/protected_session_status.js rename to src/public/app/widgets/buttons/protected_session_status.ts diff --git a/src/public/app/widgets/dialogs/password_not_set.js b/src/public/app/widgets/dialogs/password_not_set.ts similarity index 88% rename from src/public/app/widgets/dialogs/password_not_set.js rename to src/public/app/widgets/dialogs/password_not_set.ts index b8816f370..be608a4c2 100644 --- a/src/public/app/widgets/dialogs/password_not_set.js +++ b/src/public/app/widgets/dialogs/password_not_set.ts @@ -12,7 +12,7 @@ const TPL = ` @@ -21,8 +21,13 @@ const TPL = ` `; export default class PasswordNoteSetDialog extends BasicWidget { + + private modal!: bootstrap.Modal; + private $openPasswordOptionsButton!: JQuery; + doRender() { this.$widget = $(TPL); + //@ts-ignore fix once bootstrap is imported via JQuery. this.modal = bootstrap.Modal.getOrCreateInstance(this.$widget); this.$openPasswordOptionsButton = this.$widget.find(".open-password-options-button"); this.$openPasswordOptionsButton.on("click", () => { diff --git a/src/public/app/widgets/floating_buttons/copy_image_reference_button.js b/src/public/app/widgets/floating_buttons/copy_image_reference_button.ts similarity index 81% rename from src/public/app/widgets/floating_buttons/copy_image_reference_button.js rename to src/public/app/widgets/floating_buttons/copy_image_reference_button.ts index 64eff526b..534f895e5 100644 --- a/src/public/app/widgets/floating_buttons/copy_image_reference_button.js +++ b/src/public/app/widgets/floating_buttons/copy_image_reference_button.ts @@ -8,13 +8,16 @@ const TPL = ` class="copy-image-reference-button" title="${t("copy_image_reference_button.button_title")}"> - +
`; export default class CopyImageReferenceButton extends NoteContextAwareWidget { + + private $hiddenImageCopy!: JQuery; + isEnabled() { - return super.isEnabled() && ["mermaid", "canvas", "mindMap"].includes(this.note?.type) && this.note.isContentAvailable() && this.noteContext?.viewScope.viewMode === "default"; + return super.isEnabled() && ["mermaid", "canvas", "mindMap"].includes(this.note?.type ?? "") && this.note?.isContentAvailable() && this.noteContext?.viewScope?.viewMode === "default"; } doRender() { @@ -24,6 +27,10 @@ export default class CopyImageReferenceButton extends NoteContextAwareWidget { this.$hiddenImageCopy = this.$widget.find(".hidden-image-copy"); this.$widget.on("click", () => { + if (!this.note) { + return; + } + this.$hiddenImageCopy.empty().append($("").attr("src", utils.createImageSrcUrl(this.note))); imageService.copyImageReferenceToClipboard(this.$hiddenImageCopy); diff --git a/src/public/app/widgets/floating_buttons/hide_floating_buttons_button.js b/src/public/app/widgets/floating_buttons/hide_floating_buttons_button.ts similarity index 100% rename from src/public/app/widgets/floating_buttons/hide_floating_buttons_button.js rename to src/public/app/widgets/floating_buttons/hide_floating_buttons_button.ts diff --git a/src/public/app/widgets/floating_buttons/svg_export_button.js b/src/public/app/widgets/floating_buttons/svg_export_button.ts similarity index 84% rename from src/public/app/widgets/floating_buttons/svg_export_button.js rename to src/public/app/widgets/floating_buttons/svg_export_button.ts index b098b18c2..92fa33f41 100644 --- a/src/public/app/widgets/floating_buttons/svg_export_button.js +++ b/src/public/app/widgets/floating_buttons/svg_export_button.ts @@ -11,7 +11,7 @@ const TPL = ` export default class SvgExportButton extends NoteContextAwareWidget { isEnabled() { - return super.isEnabled() && ["mermaid", "mindMap"].includes(this.note?.type) && this.note.isContentAvailable() && this.noteContext?.viewScope.viewMode === "default"; + return super.isEnabled() && ["mermaid", "mindMap"].includes(this.note?.type ?? "") && this.note?.isContentAvailable() && this.noteContext?.viewScope?.viewMode === "default"; } doRender() { diff --git a/src/public/app/widgets/protected_note_switch.js b/src/public/app/widgets/protected_note_switch.ts similarity index 61% rename from src/public/app/widgets/protected_note_switch.js rename to src/public/app/widgets/protected_note_switch.ts index 134a3633a..e4926ae84 100644 --- a/src/public/app/widgets/protected_note_switch.js +++ b/src/public/app/widgets/protected_note_switch.ts @@ -1,3 +1,5 @@ +import type { EventData } from "../components/app_context.js"; +import type FNote from "../entities/fnote.js"; import { t } from "../services/i18n.js"; import protectedSessionService from "../services/protected_session.js"; import SwitchWidget from "./switch.js"; @@ -14,18 +16,22 @@ export default class ProtectedNoteSwitchWidget extends SwitchWidget { } switchOn() { - protectedSessionService.protectNote(this.noteId, true, false); + if (this.noteId) { + protectedSessionService.protectNote(this.noteId, true, false); + } } switchOff() { - protectedSessionService.protectNote(this.noteId, false, false); + if (this.noteId) { + protectedSessionService.protectNote(this.noteId, false, false); + } } - async refreshWithNote(note) { + async refreshWithNote(note: FNote) { this.isToggled = note.isProtected; } - entitiesReloadedEvent({ loadResults }) { + entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) { if (loadResults.isNoteReloaded(this.noteId)) { this.refresh(); } diff --git a/src/public/app/widgets/quick_search_launcher.js b/src/public/app/widgets/quick_search_launcher.ts similarity index 89% rename from src/public/app/widgets/quick_search_launcher.js rename to src/public/app/widgets/quick_search_launcher.ts index 2cd906470..f64d1ca1d 100644 --- a/src/public/app/widgets/quick_search_launcher.js +++ b/src/public/app/widgets/quick_search_launcher.ts @@ -10,7 +10,10 @@ import QuickSearchWidget from "./quick_search.js"; * - Hiding the widget on mobile. */ export default class QuickSearchLauncherWidget extends QuickSearchWidget { - constructor(isHorizontalLayout) { + + private isHorizontalLayout: boolean; + + constructor(isHorizontalLayout: boolean) { super(); this.isHorizontalLayout = isHorizontalLayout; } diff --git a/src/public/app/widgets/ribbon_widgets/note_properties.js b/src/public/app/widgets/ribbon_widgets/note_properties.ts similarity index 88% rename from src/public/app/widgets/ribbon_widgets/note_properties.js rename to src/public/app/widgets/ribbon_widgets/note_properties.ts index 4aacdcba0..bfccfe51f 100644 --- a/src/public/app/widgets/ribbon_widgets/note_properties.js +++ b/src/public/app/widgets/ribbon_widgets/note_properties.ts @@ -1,3 +1,4 @@ +import type FNote from "../../entities/fnote.js"; import { t } from "../../services/i18n.js"; import NoteContextAwareWidget from "../note_context_aware_widget.js"; @@ -19,6 +20,9 @@ const TPL = ` * TODO: figure out better name or conceptualize better. */ export default class NotePropertiesWidget extends NoteContextAwareWidget { + + private $pageUrl!: JQuery; + isEnabled() { return this.note && !!this.note.getLabelValue("pageUrl"); } @@ -39,9 +43,9 @@ export default class NotePropertiesWidget extends NoteContextAwareWidget { this.$pageUrl = this.$widget.find(".page-url"); } - async refreshWithNote(note) { + async refreshWithNote(note: FNote) { const pageUrl = note.getLabelValue("pageUrl"); - this.$pageUrl.attr("href", pageUrl).attr("title", pageUrl).text(pageUrl); + this.$pageUrl.attr("href", pageUrl).attr("title", pageUrl).text(pageUrl ?? ""); } } diff --git a/src/public/app/widgets/search_options/debug.js b/src/public/app/widgets/search_options/debug.ts similarity index 96% rename from src/public/app/widgets/search_options/debug.js rename to src/public/app/widgets/search_options/debug.ts index 9a732507b..46d314fca 100644 --- a/src/public/app/widgets/search_options/debug.js +++ b/src/public/app/widgets/search_options/debug.ts @@ -27,7 +27,7 @@ export default class Debug extends AbstractSearchOption { return "label"; } - static async create(noteId) { + static async create(noteId: string) { await AbstractSearchOption.setAttribute(noteId, "label", "debug"); } diff --git a/src/public/app/widgets/search_options/fast_search.js b/src/public/app/widgets/search_options/fast_search.ts similarity index 94% rename from src/public/app/widgets/search_options/fast_search.js rename to src/public/app/widgets/search_options/fast_search.ts index b49e0eee6..70b89619a 100644 --- a/src/public/app/widgets/search_options/fast_search.js +++ b/src/public/app/widgets/search_options/fast_search.ts @@ -12,7 +12,7 @@ const TPL = ` + @@ -26,7 +26,7 @@ export default class FastSearch extends AbstractSearchOption { return "label"; } - static async create(noteId) { + static async create(noteId: string) { await AbstractSearchOption.setAttribute(noteId, "label", "fastSearch"); } diff --git a/src/public/app/widgets/search_options/include_archived_notes.js b/src/public/app/widgets/search_options/include_archived_notes.ts similarity index 94% rename from src/public/app/widgets/search_options/include_archived_notes.js rename to src/public/app/widgets/search_options/include_archived_notes.ts index 0e461d05e..ad7754196 100644 --- a/src/public/app/widgets/search_options/include_archived_notes.js +++ b/src/public/app/widgets/search_options/include_archived_notes.ts @@ -20,7 +20,7 @@ export default class IncludeArchivedNotes extends AbstractSearchOption { return "label"; } - static async create(noteId) { + static async create(noteId: string) { await AbstractSearchOption.setAttribute(noteId, "label", "includeArchivedNotes"); } diff --git a/src/public/app/widgets/switch.ts b/src/public/app/widgets/switch.ts index 585ab9045..aff5587c9 100644 --- a/src/public/app/widgets/switch.ts +++ b/src/public/app/widgets/switch.ts @@ -123,13 +123,13 @@ export default class SwitchWidget extends NoteContextAwareWidget { private $switchButton!: JQuery; private $switchToggle!: JQuery; private $switchName!: JQuery; - private $helpButton!: JQuery; + protected $helpButton!: JQuery; - private switchOnName = ""; - private switchOnTooltip = ""; + protected switchOnName = ""; + protected switchOnTooltip = ""; - private switchOffName = ""; - private switchOffTooltip = ""; + protected switchOffName = ""; + protected switchOffTooltip = ""; private disabledTooltip = ""; diff --git a/src/public/app/widgets/template_switch.js b/src/public/app/widgets/template_switch.ts similarity index 63% rename from src/public/app/widgets/template_switch.js rename to src/public/app/widgets/template_switch.ts index 6205b95c6..572426020 100644 --- a/src/public/app/widgets/template_switch.js +++ b/src/public/app/widgets/template_switch.ts @@ -1,13 +1,16 @@ import SwitchWidget from "./switch.js"; import attributeService from "../services/attributes.js"; import { t } from "../services/i18n.js"; +import type { EventData } from "../components/app_context.js"; +import type FNote from "../entities/fnote.js"; /** * Switch for the basic properties widget which allows the user to select whether the note is a template or not, which toggles the `#template` attribute. */ export default class TemplateSwitchWidget extends SwitchWidget { + isEnabled() { - return super.isEnabled() && !this.noteId.startsWith("_options"); + return super.isEnabled() && !this.noteId?.startsWith("_options"); } doRender() { @@ -23,21 +26,25 @@ export default class TemplateSwitchWidget extends SwitchWidget { } async switchOn() { - await attributeService.setLabel(this.noteId, "template"); - } - - async switchOff() { - for (const templateAttr of this.note.getOwnedLabels("template")) { - await attributeService.removeAttributeById(this.noteId, templateAttr.attributeId); + if (this.noteId) { + await attributeService.setLabel(this.noteId, "template"); } } - async refreshWithNote(note) { + async switchOff() { + if (this.note && this.noteId) { + for (const templateAttr of this.note.getOwnedLabels("template")) { + await attributeService.removeAttributeById(this.noteId, templateAttr.attributeId); + } + } + } + + async refreshWithNote(note: FNote) { const isTemplate = note.hasLabel("template"); this.isToggled = isTemplate; } - entitiesReloadedEvent({ loadResults }) { + entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) { if (loadResults.getAttributeRows().find((attr) => attr.type === "label" && attr.name === "template" && attr.noteId === this.noteId)) { this.refresh(); } diff --git a/src/public/app/widgets/type_widgets/book.js b/src/public/app/widgets/type_widgets/book.ts similarity index 80% rename from src/public/app/widgets/type_widgets/book.js rename to src/public/app/widgets/type_widgets/book.ts index 17276c396..bb872d50e 100644 --- a/src/public/app/widgets/type_widgets/book.js +++ b/src/public/app/widgets/type_widgets/book.ts @@ -1,5 +1,6 @@ import TypeWidget from "./type_widget.js"; import { t } from "../../services/i18n.js"; +import type FNote from "../../entities/fnote.js"; const TPL = `
@@ -19,6 +20,9 @@ const TPL = `
`; export default class BookTypeWidget extends TypeWidget { + + private $helpNoChildren!: JQuery; + static getType() { return "book"; } @@ -30,7 +34,7 @@ export default class BookTypeWidget extends TypeWidget { super.doRender(); } - async doRefresh(note) { - this.$helpNoChildren.toggle(!this.note.hasChildren()); + async doRefresh(note: FNote) { + this.$helpNoChildren.toggle(!this.note?.hasChildren()); } } diff --git a/src/public/app/widgets/type_widgets/note_map.js b/src/public/app/widgets/type_widgets/note_map.ts similarity index 82% rename from src/public/app/widgets/type_widgets/note_map.js rename to src/public/app/widgets/type_widgets/note_map.ts index dea9a9f00..c942b3d89 100644 --- a/src/public/app/widgets/type_widgets/note_map.js +++ b/src/public/app/widgets/type_widgets/note_map.ts @@ -1,9 +1,13 @@ import TypeWidget from "./type_widget.js"; import NoteMapWidget from "../note_map.js"; +import type FNote from "../../entities/fnote.js"; const TPL = `
`; export default class NoteMapTypeWidget extends TypeWidget { + + private noteMapWidget: NoteMapWidget; + static getType() { return "noteMap"; } @@ -22,7 +26,7 @@ export default class NoteMapTypeWidget extends TypeWidget { super.doRender(); } - async doRefresh(note) { + async doRefresh(note: FNote) { await this.noteMapWidget.refresh(); } }