import NoteContextAwareWidget from "../note_context_aware_widget.js"; import utils from "../../services/utils.js"; import branchService from "../../services/branches.js"; import dialogService from "../../services/dialog.js"; import server from "../../services/server.js"; import toastService from "../../services/toast.js"; import ws from "../../services/ws.js"; import appContext, { type EventData } from "../../components/app_context.js"; import { t } from "../../services/i18n.js"; import type FNote from "../../entities/fnote.js"; import type { FAttachmentRow } from "../../entities/fattachment.js"; // TODO: Deduplicate with server interface ConvertToAttachmentResponse { attachment: FAttachmentRow; } const TPL = ` `; export default class NoteActionsWidget extends NoteContextAwareWidget { private $convertNoteIntoAttachmentButton!: JQuery; private $findInTextButton!: JQuery; private $printActiveNoteButton!: JQuery; private $exportAsPdfButton!: JQuery; private $showSourceButton!: JQuery; private $showAttachmentsButton!: JQuery; private $renderNoteButton!: JQuery; private $saveRevisionButton!: JQuery; private $exportNoteButton!: JQuery; private $importNoteButton!: JQuery; private $openNoteExternallyButton!: JQuery; private $openNoteCustomButton!: JQuery; private $deleteNoteButton!: JQuery; isEnabled() { return this.note?.type !== "launcher"; } doRender() { this.$widget = $(TPL); this.$widget.on("show.bs.dropdown", () => { if (this.note) { this.refreshVisibility(this.note); } }); this.$convertNoteIntoAttachmentButton = this.$widget.find("[data-trigger-command='convertNoteIntoAttachment']"); this.$findInTextButton = this.$widget.find(".find-in-text-button"); this.$printActiveNoteButton = this.$widget.find(".print-active-note-button"); this.$exportAsPdfButton = this.$widget.find(".export-as-pdf-button"); this.$showSourceButton = this.$widget.find(".show-source-button"); this.$showAttachmentsButton = this.$widget.find(".show-attachments-button"); this.$renderNoteButton = this.$widget.find(".render-note-button"); this.$saveRevisionButton = this.$widget.find(".save-revision-button"); this.$exportNoteButton = this.$widget.find(".export-note-button"); this.$exportNoteButton.on("click", () => { if (this.$exportNoteButton.hasClass("disabled") || !this.noteContext?.notePath) { return; } this.triggerCommand("showExportDialog", { notePath: this.noteContext.notePath, defaultType: "single" }); }); this.$importNoteButton = this.$widget.find(".import-files-button"); this.$importNoteButton.on("click", () => { if (this.noteId) { this.triggerCommand("showImportDialog", { noteId: this.noteId }); } }); this.$widget.on("click", ".dropdown-item", () => this.$widget.find("[data-bs-toggle='dropdown']").dropdown("toggle")); this.$openNoteExternallyButton = this.$widget.find(".open-note-externally-button"); this.$openNoteCustomButton = this.$widget.find(".open-note-custom-button"); this.$deleteNoteButton = this.$widget.find(".delete-note-button"); this.$deleteNoteButton.on("click", () => { if (!this.note || this.note.noteId === "root") { return; } branchService.deleteNotes([this.note.getParentBranches()[0].branchId], true); }); } async refreshVisibility(note: FNote) { const isInOptions = note.noteId.startsWith("_options"); this.$convertNoteIntoAttachmentButton.toggle(note.isEligibleForConversionToAttachment()); this.toggleDisabled(this.$findInTextButton, ["text", "code", "book"].includes(note.type)); this.toggleDisabled(this.$showAttachmentsButton, !isInOptions); this.toggleDisabled(this.$showSourceButton, ["text", "code", "relationMap", "mermaid", "canvas", "mindMap", "geoMap"].includes(note.type)); const canPrint = ["text", "code"].includes(note.type); this.toggleDisabled(this.$printActiveNoteButton, canPrint); this.toggleDisabled(this.$exportAsPdfButton, canPrint); this.$renderNoteButton.toggle(note.type === "render"); this.toggleDisabled(this.$openNoteExternallyButton, utils.isElectron() && !["search", "book"].includes(note.type)); this.toggleDisabled( this.$openNoteCustomButton, utils.isElectron() && !utils.isMac() && // no implementation for Mac yet !["search", "book"].includes(note.type) ); // I don't want to handle all special notes like this, but intuitively user might want to export content of backend log this.toggleDisabled(this.$exportNoteButton, !["_backendLog"].includes(note.noteId) && !isInOptions); this.toggleDisabled(this.$importNoteButton, !["search"].includes(note.type) && !isInOptions); this.toggleDisabled(this.$deleteNoteButton, !isInOptions); this.toggleDisabled(this.$saveRevisionButton, !isInOptions); } async convertNoteIntoAttachmentCommand() { if (!this.note || !(await dialogService.confirm(t("note_actions.convert_into_attachment_prompt", { title: this.note.title })))) { return; } const { attachment: newAttachment } = await server.post(`notes/${this.noteId}/convert-to-attachment`); if (!newAttachment) { toastService.showMessage(t("note_actions.convert_into_attachment_failed", { title: this.note.title })); return; } toastService.showMessage(t("note_actions.convert_into_attachment_successful", { title: newAttachment.title })); await ws.waitForMaxKnownEntityChangeId(); await appContext.tabManager.getActiveContext().setNote(newAttachment.ownerId, { viewScope: { viewMode: "attachments", attachmentId: newAttachment.attachmentId } }); } toggleDisabled($el: JQuery, enable: boolean) { if (enable) { $el.removeAttr("disabled"); } else { $el.attr("disabled", "disabled"); } } entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) { if (loadResults.isNoteReloaded(this.noteId)) { this.refresh(); } } }