mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +01:00 
			
		
		
		
	Merge pull request #1260 from TriliumNext/chore_port-to-ts_attach
chore(ts): port various attachment related files to TS
This commit is contained in:
		| @@ -1,4 +1,5 @@ | |||||||
| // TODO: Booleans should probably be numbers instead (as SQLite does not have booleans.); | // TODO: Booleans should probably be numbers instead (as SQLite does not have booleans.); | ||||||
|  | // TODO: check against schema.sql which properties really are "optional" | ||||||
|  |  | ||||||
| export interface AttachmentRow { | export interface AttachmentRow { | ||||||
|     attachmentId?: string; |     attachmentId?: string; | ||||||
| @@ -12,6 +13,8 @@ export interface AttachmentRow { | |||||||
|     dateModified?: string; |     dateModified?: string; | ||||||
|     utcDateModified?: string; |     utcDateModified?: string; | ||||||
|     utcDateScheduledForErasureSince?: string; |     utcDateScheduledForErasureSince?: string; | ||||||
|  |     isDeleted?: boolean; | ||||||
|  |     deleteId?: string; | ||||||
|     contentLength?: number; |     contentLength?: number; | ||||||
|     content?: Buffer | string; |     content?: Buffer | string; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -19,18 +19,18 @@ export interface FAttachmentRow { | |||||||
| class FAttachment { | class FAttachment { | ||||||
|     private froca: Froca; |     private froca: Froca; | ||||||
|     attachmentId!: string; |     attachmentId!: string; | ||||||
|     private ownerId!: string; |     ownerId!: string; | ||||||
|     role!: string; |     role!: string; | ||||||
|     mime!: string; |     mime!: string; | ||||||
|     title!: string; |     title!: string; | ||||||
|     isProtected!: boolean; // TODO: Is this used? |     isProtected!: boolean; // TODO: Is this used? | ||||||
|     private dateModified!: string; |     private dateModified!: string; | ||||||
|     utcDateModified!: string; |     utcDateModified!: string; | ||||||
|     private utcDateScheduledForErasureSince!: string; |     utcDateScheduledForErasureSince!: string; | ||||||
|     /** |     /** | ||||||
|      * optionally added to the entity |      * optionally added to the entity | ||||||
|      */ |      */ | ||||||
|     private contentLength!: number; |     contentLength!: number; | ||||||
|  |  | ||||||
|     constructor(froca: Froca, row: FAttachmentRow) { |     constructor(froca: Froca, row: FAttachmentRow) { | ||||||
|         /** @type {Froca} */ |         /** @type {Froca} */ | ||||||
|   | |||||||
| @@ -24,7 +24,8 @@ interface Options { | |||||||
|  |  | ||||||
| const CODE_MIME_TYPES = new Set(["application/json"]); | const CODE_MIME_TYPES = new Set(["application/json"]); | ||||||
|  |  | ||||||
| async function getRenderedContent(this: {} | { ctx: string }, entity: FNote, options: Options = {}) { | async function getRenderedContent(this: {} | { ctx: string }, entity: FNote | FAttachment, options: Options = {}) { | ||||||
|  |  | ||||||
|     options = Object.assign( |     options = Object.assign( | ||||||
|         { |         { | ||||||
|             tooltip: false |             tooltip: false | ||||||
| @@ -47,7 +48,7 @@ async function getRenderedContent(this: {} | { ctx: string }, entity: FNote, opt | |||||||
|         renderFile(entity, type, $renderedContent); |         renderFile(entity, type, $renderedContent); | ||||||
|     } else if (type === "mermaid") { |     } else if (type === "mermaid") { | ||||||
|         await renderMermaid(entity, $renderedContent); |         await renderMermaid(entity, $renderedContent); | ||||||
|     } else if (type === "render") { |     } else if (type === "render" && entity instanceof FNote) { | ||||||
|         const $content = $("<div>"); |         const $content = $("<div>"); | ||||||
|  |  | ||||||
|         await renderService.render(entity, $content); |         await renderService.render(entity, $content); | ||||||
| @@ -79,7 +80,7 @@ async function getRenderedContent(this: {} | { ctx: string }, entity: FNote, opt | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
|  |  | ||||||
| async function renderText(note: FNote, $renderedContent: JQuery<HTMLElement>) { | async function renderText(note: FNote | FAttachment, $renderedContent: JQuery<HTMLElement>) { | ||||||
|     // entity must be FNote |     // entity must be FNote | ||||||
|     const blob = await note.getBlob(); |     const blob = await note.getBlob(); | ||||||
|  |  | ||||||
| @@ -102,7 +103,7 @@ async function renderText(note: FNote, $renderedContent: JQuery<HTMLElement>) { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         await applySyntaxHighlight($renderedContent); |         await applySyntaxHighlight($renderedContent); | ||||||
|     } else { |     } else if (note instanceof FNote) { | ||||||
|         await renderChildrenList($renderedContent, note); |         await renderChildrenList($renderedContent, note); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -110,7 +111,7 @@ async function renderText(note: FNote, $renderedContent: JQuery<HTMLElement>) { | |||||||
| /** | /** | ||||||
|  * Renders a code note, by displaying its content and applying syntax highlighting based on the selected MIME type. |  * Renders a code note, by displaying its content and applying syntax highlighting based on the selected MIME type. | ||||||
|  */ |  */ | ||||||
| async function renderCode(note: FNote, $renderedContent: JQuery<HTMLElement>) { | async function renderCode(note: FNote | FAttachment, $renderedContent: JQuery<HTMLElement>) { | ||||||
|     const blob = await note.getBlob(); |     const blob = await note.getBlob(); | ||||||
|  |  | ||||||
|     const $codeBlock = $("<code>"); |     const $codeBlock = $("<code>"); | ||||||
| @@ -208,7 +209,7 @@ function renderFile(entity: FNote | FAttachment, type: string, $renderedContent: | |||||||
|     $renderedContent.append($content); |     $renderedContent.append($content); | ||||||
| } | } | ||||||
|  |  | ||||||
| async function renderMermaid(note: FNote, $renderedContent: JQuery<HTMLElement>) { | async function renderMermaid(note: FNote | FAttachment, $renderedContent: JQuery<HTMLElement>) { | ||||||
|     await libraryLoader.requireLibrary(libraryLoader.MERMAID); |     await libraryLoader.requireLibrary(libraryLoader.MERMAID); | ||||||
|  |  | ||||||
|     const blob = await note.getBlob(); |     const blob = await note.getBlob(); | ||||||
|   | |||||||
| @@ -70,7 +70,7 @@ interface CreateLinkOptions { | |||||||
|     viewScope?: ViewScope; |     viewScope?: ViewScope; | ||||||
| } | } | ||||||
|  |  | ||||||
| async function createLink(notePath: string, options: CreateLinkOptions = {}) { | async function createLink(notePath: string | undefined, options: CreateLinkOptions = {}) { | ||||||
|     if (!notePath || !notePath.trim()) { |     if (!notePath || !notePath.trim()) { | ||||||
|         logError("Missing note path"); |         logError("Missing note path"); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| import type { TaskRow } from "../../../becca/entities/rows.js"; | import type { TaskRow, AttachmentRow } from "../../../becca/entities/rows.js"; | ||||||
| import type { AttributeType } from "../entities/fattribute.js"; | import type { AttributeType } from "../entities/fattribute.js"; | ||||||
| import type { EntityChange } from "../server_types.js"; | import type { EntityChange } from "../server_types.js"; | ||||||
|  |  | ||||||
| @@ -37,8 +37,6 @@ interface ContentNoteIdToComponentIdRow { | |||||||
|     componentId: string; |     componentId: string; | ||||||
| } | } | ||||||
|  |  | ||||||
| interface AttachmentRow {} |  | ||||||
|  |  | ||||||
| interface OptionRow {} | interface OptionRow {} | ||||||
|  |  | ||||||
| interface NoteReorderingRow {} | interface NoteReorderingRow {} | ||||||
|   | |||||||
| @@ -7,6 +7,8 @@ import imageService from "../services/image.js"; | |||||||
| import linkService from "../services/link.js"; | import linkService from "../services/link.js"; | ||||||
| import contentRenderer from "../services/content_renderer.js"; | import contentRenderer from "../services/content_renderer.js"; | ||||||
| import toastService from "../services/toast.js"; | import toastService from "../services/toast.js"; | ||||||
|  | import type FAttachment from "../entities/fattachment.js"; | ||||||
|  | import type { EventData } from "../components/app_context.js"; | ||||||
| 
 | 
 | ||||||
| const TPL = ` | const TPL = ` | ||||||
| <div class="attachment-detail-widget"> | <div class="attachment-detail-widget"> | ||||||
| @@ -96,7 +98,12 @@ const TPL = ` | |||||||
| </div>`;
 | </div>`;
 | ||||||
| 
 | 
 | ||||||
| export default class AttachmentDetailWidget extends BasicWidget { | export default class AttachmentDetailWidget extends BasicWidget { | ||||||
|     constructor(attachment, isFullDetail) { |     attachment: FAttachment; | ||||||
|  |     attachmentActionsWidget: AttachmentActionsWidget; | ||||||
|  |     isFullDetail: boolean; | ||||||
|  |     $wrapper!: JQuery<HTMLElement>; | ||||||
|  | 
 | ||||||
|  |     constructor(attachment: FAttachment, isFullDetail: boolean) { | ||||||
|         super(); |         super(); | ||||||
| 
 | 
 | ||||||
|         this.contentSized(); |         this.contentSized(); | ||||||
| @@ -140,7 +147,8 @@ export default class AttachmentDetailWidget extends BasicWidget { | |||||||
|             this.$wrapper.addClass("scheduled-for-deletion"); |             this.$wrapper.addClass("scheduled-for-deletion"); | ||||||
| 
 | 
 | ||||||
|             const scheduledSinceTimestamp = utils.parseDate(utcDateScheduledForErasureSince)?.getTime(); |             const scheduledSinceTimestamp = utils.parseDate(utcDateScheduledForErasureSince)?.getTime(); | ||||||
|             const intervalMs = options.getInt("eraseUnusedAttachmentsAfterSeconds") * 1000; |             // use default value (30 days in seconds) from options_init as fallback, in case getInt returns null
 | ||||||
|  |             const intervalMs = options.getInt("eraseUnusedAttachmentsAfterSeconds") || 2592000 * 1000; | ||||||
|             const deletionTimestamp = scheduledSinceTimestamp + intervalMs; |             const deletionTimestamp = scheduledSinceTimestamp + intervalMs; | ||||||
|             const willBeDeletedInMs = deletionTimestamp - Date.now(); |             const willBeDeletedInMs = deletionTimestamp - Date.now(); | ||||||
| 
 | 
 | ||||||
| @@ -185,7 +193,7 @@ export default class AttachmentDetailWidget extends BasicWidget { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async entitiesReloadedEvent({ loadResults }) { |     async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) { | ||||||
|         const attachmentRow = loadResults.getAttachmentRows().find((att) => att.attachmentId === this.attachment.attachmentId); |         const attachmentRow = loadResults.getAttachmentRows().find((att) => att.attachmentId === this.attachment.attachmentId); | ||||||
| 
 | 
 | ||||||
|         if (attachmentRow) { |         if (attachmentRow) { | ||||||
| @@ -8,6 +8,9 @@ import appContext from "../../components/app_context.js"; | |||||||
| import openService from "../../services/open.js"; | import openService from "../../services/open.js"; | ||||||
| import utils from "../../services/utils.js"; | import utils from "../../services/utils.js"; | ||||||
| import { Dropdown } from "bootstrap"; | import { Dropdown } from "bootstrap"; | ||||||
|  | import type attachmentsApiRoute from "../../../../routes/api/attachments.js" | ||||||
|  | import type FAttachment from "../../entities/fattachment.js"; | ||||||
|  | import type AttachmentDetailWidget from "../attachment_detail.js"; | ||||||
| 
 | 
 | ||||||
| const TPL = ` | const TPL = ` | ||||||
| <div class="dropdown attachment-actions"> | <div class="dropdown attachment-actions"> | ||||||
| @@ -79,7 +82,12 @@ const TPL = ` | |||||||
| </div>`;
 | </div>`;
 | ||||||
| 
 | 
 | ||||||
| export default class AttachmentActionsWidget extends BasicWidget { | export default class AttachmentActionsWidget extends BasicWidget { | ||||||
|     constructor(attachment, isFullDetail) { |     $uploadNewRevisionInput!: JQuery<HTMLInputElement>; | ||||||
|  |     attachment: FAttachment; | ||||||
|  |     isFullDetail: boolean; | ||||||
|  |     dropdown!: Dropdown; | ||||||
|  | 
 | ||||||
|  |     constructor(attachment: FAttachment, isFullDetail: boolean) { | ||||||
|         super(); |         super(); | ||||||
| 
 | 
 | ||||||
|         this.attachment = attachment; |         this.attachment = attachment; | ||||||
| @@ -92,21 +100,22 @@ export default class AttachmentActionsWidget extends BasicWidget { | |||||||
| 
 | 
 | ||||||
|     doRender() { |     doRender() { | ||||||
|         this.$widget = $(TPL); |         this.$widget = $(TPL); | ||||||
|         this.dropdown = Dropdown.getOrCreateInstance(this.$widget.find("[data-bs-toggle='dropdown']")); |         this.dropdown = Dropdown.getOrCreateInstance(this.$widget.find("[data-bs-toggle='dropdown']")[0]); | ||||||
|         this.$widget.on("click", ".dropdown-item", () => this.dropdown.toggle()); |         this.$widget.on("click", ".dropdown-item", () => this.dropdown.toggle()); | ||||||
| 
 | 
 | ||||||
|         this.$uploadNewRevisionInput = this.$widget.find(".attachment-upload-new-revision-input"); |         this.$uploadNewRevisionInput = this.$widget.find(".attachment-upload-new-revision-input"); | ||||||
|         this.$uploadNewRevisionInput.on("change", async () => { |         this.$uploadNewRevisionInput.on("change", async () => { | ||||||
|             const fileToUpload = this.$uploadNewRevisionInput[0].files[0]; // copy to allow reset below
 | 
 | ||||||
|  |             const fileToUpload = this.$uploadNewRevisionInput[0].files?.item(0);  // copy to allow reset below
 | ||||||
|             this.$uploadNewRevisionInput.val(""); |             this.$uploadNewRevisionInput.val(""); | ||||||
| 
 |             if (fileToUpload) { | ||||||
|                 const result = await server.upload(`attachments/${this.attachmentId}/file`, fileToUpload); |                 const result = await server.upload(`attachments/${this.attachmentId}/file`, fileToUpload); | ||||||
| 
 |  | ||||||
|                 if (result.uploaded) { |                 if (result.uploaded) { | ||||||
|                     toastService.showMessage(t("attachments_actions.upload_success")); |                     toastService.showMessage(t("attachments_actions.upload_success")); | ||||||
|                 } else { |                 } else { | ||||||
|                     toastService.showError(t("attachments_actions.upload_failed")); |                     toastService.showError(t("attachments_actions.upload_failed")); | ||||||
|                 } |                 } | ||||||
|  |             } | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         const isElectron = utils.isElectron(); |         const isElectron = utils.isElectron(); | ||||||
| @@ -122,6 +131,7 @@ export default class AttachmentActionsWidget extends BasicWidget { | |||||||
|             const $openAttachmentCustomButton = this.$widget.find("[data-trigger-command='openAttachmentCustom']"); |             const $openAttachmentCustomButton = this.$widget.find("[data-trigger-command='openAttachmentCustom']"); | ||||||
|             $openAttachmentCustomButton.addClass("disabled").append($('<span class="bx bx-info-circle disabled-tooltip" />').attr("title", t("attachments_actions.open_custom_client_only"))); |             $openAttachmentCustomButton.addClass("disabled").append($('<span class="bx bx-info-circle disabled-tooltip" />').attr("title", t("attachments_actions.open_custom_client_only"))); | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async openAttachmentCommand() { |     async openAttachmentCommand() { | ||||||
| @@ -141,7 +151,9 @@ export default class AttachmentActionsWidget extends BasicWidget { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async copyAttachmentLinkToClipboardCommand() { |     async copyAttachmentLinkToClipboardCommand() { | ||||||
|         this.parent.copyAttachmentLinkToClipboard(); |         if (this.parent && "copyAttachmentLinkToClipboard" in this.parent) { | ||||||
|  |             (this.parent as AttachmentDetailWidget).copyAttachmentLinkToClipboard(); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async deleteAttachmentCommand() { |     async deleteAttachmentCommand() { | ||||||
| @@ -158,7 +170,8 @@ export default class AttachmentActionsWidget extends BasicWidget { | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const { note: newNote } = await server.post(`attachments/${this.attachmentId}/convert-to-note`); | 
 | ||||||
|  |         const { note: newNote } = await server.post<ReturnType<typeof attachmentsApiRoute.convertAttachmentToNote>>(`attachments/${this.attachmentId}/convert-to-note`); | ||||||
|         toastService.showMessage(t("attachments_actions.convert_success", { title: this.attachment.title })); |         toastService.showMessage(t("attachments_actions.convert_success", { title: this.attachment.title })); | ||||||
|         await ws.waitForMaxKnownEntityChangeId(); |         await ws.waitForMaxKnownEntityChangeId(); | ||||||
|         await appContext.tabManager.getActiveContext().setNote(newNote.noteId); |         await appContext.tabManager.getActiveContext().setNote(newNote.noteId); | ||||||
| @@ -4,6 +4,8 @@ import linkService from "../../services/link.js"; | |||||||
| import froca from "../../services/froca.js"; | import froca from "../../services/froca.js"; | ||||||
| import utils from "../../services/utils.js"; | import utils from "../../services/utils.js"; | ||||||
| import { t } from "../../services/i18n.js"; | import { t } from "../../services/i18n.js"; | ||||||
|  | import type FNote from "../../entities/fnote.js"; | ||||||
|  | import type { EventData } from "../../components/app_context.js"; | ||||||
| 
 | 
 | ||||||
| const TPL = ` | const TPL = ` | ||||||
| <div class="attachment-detail note-detail-printable"> | <div class="attachment-detail note-detail-printable"> | ||||||
| @@ -32,6 +34,9 @@ const TPL = ` | |||||||
| </div>`;
 | </div>`;
 | ||||||
| 
 | 
 | ||||||
| export default class AttachmentDetailTypeWidget extends TypeWidget { | export default class AttachmentDetailTypeWidget extends TypeWidget { | ||||||
|  |     $wrapper!: JQuery<HTMLElement>; | ||||||
|  |     $linksWrapper!: JQuery<HTMLElement>; | ||||||
|  | 
 | ||||||
|     static getType() { |     static getType() { | ||||||
|         return "attachmentDetail"; |         return "attachmentDetail"; | ||||||
|     } |     } | ||||||
| @@ -44,7 +49,7 @@ export default class AttachmentDetailTypeWidget extends TypeWidget { | |||||||
|         super.doRender(); |         super.doRender(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async doRefresh(note) { |     async doRefresh(note: Parameters<TypeWidget["doRefresh"]>[0]) { | ||||||
|         this.$wrapper.empty(); |         this.$wrapper.empty(); | ||||||
|         this.children = []; |         this.children = []; | ||||||
| 
 | 
 | ||||||
| @@ -69,7 +74,7 @@ export default class AttachmentDetailTypeWidget extends TypeWidget { | |||||||
|             $helpButton |             $helpButton | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         const attachment = await froca.getAttachment(this.attachmentId, true); |         const attachment = (this.attachmentId) ? await froca.getAttachment(this.attachmentId, true) : null; | ||||||
| 
 | 
 | ||||||
|         if (!attachment) { |         if (!attachment) { | ||||||
|             this.$wrapper.html("<strong>" + t("attachment_detail.attachment_deleted") + "</strong>"); |             this.$wrapper.html("<strong>" + t("attachment_detail.attachment_deleted") + "</strong>"); | ||||||
| @@ -82,7 +87,7 @@ export default class AttachmentDetailTypeWidget extends TypeWidget { | |||||||
|         this.$wrapper.append(attachmentDetailWidget.render()); |         this.$wrapper.append(attachmentDetailWidget.render()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async entitiesReloadedEvent({ loadResults }) { |     async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) { | ||||||
|         const attachmentRow = loadResults.getAttachmentRows().find((att) => att.attachmentId === this.attachmentId); |         const attachmentRow = loadResults.getAttachmentRows().find((att) => att.attachmentId === this.attachmentId); | ||||||
| 
 | 
 | ||||||
|         if (attachmentRow?.isDeleted) { |         if (attachmentRow?.isDeleted) { | ||||||
| @@ -91,6 +96,6 @@ export default class AttachmentDetailTypeWidget extends TypeWidget { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     get attachmentId() { |     get attachmentId() { | ||||||
|         return this.noteContext.viewScope.attachmentId; |         return this?.noteContext?.viewScope?.attachmentId; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -3,6 +3,7 @@ import AttachmentDetailWidget from "../attachment_detail.js"; | |||||||
| import linkService from "../../services/link.js"; | import linkService from "../../services/link.js"; | ||||||
| import utils from "../../services/utils.js"; | import utils from "../../services/utils.js"; | ||||||
| import { t } from "../../services/i18n.js"; | import { t } from "../../services/i18n.js"; | ||||||
|  | import type { EventData } from "../../components/app_context.js"; | ||||||
| 
 | 
 | ||||||
| const TPL = ` | const TPL = ` | ||||||
| <div class="attachment-list note-detail-printable"> | <div class="attachment-list note-detail-printable"> | ||||||
| @@ -27,6 +28,10 @@ const TPL = ` | |||||||
| </div>`;
 | </div>`;
 | ||||||
| 
 | 
 | ||||||
| export default class AttachmentListTypeWidget extends TypeWidget { | export default class AttachmentListTypeWidget extends TypeWidget { | ||||||
|  |     $list!: JQuery<HTMLElement>; | ||||||
|  |     $linksWrapper!: JQuery<HTMLElement>; | ||||||
|  |     renderedAttachmentIds!: Set<string>; | ||||||
|  | 
 | ||||||
|     static getType() { |     static getType() { | ||||||
|         return "attachmentList"; |         return "attachmentList"; | ||||||
|     } |     } | ||||||
| @@ -39,7 +44,10 @@ export default class AttachmentListTypeWidget extends TypeWidget { | |||||||
|         super.doRender(); |         super.doRender(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async doRefresh(note) { |     async doRefresh(note: Parameters<TypeWidget["doRefresh"]>[0]) { | ||||||
|  |         // TriliumNextTODO: do we need to handle an undefined/null note?
 | ||||||
|  |         if (!note) return false; | ||||||
|  | 
 | ||||||
|         const $helpButton = $(` |         const $helpButton = $(` | ||||||
|             <button class="attachment-help-button icon-action bx bx-help-circle" |             <button class="attachment-help-button icon-action bx bx-help-circle" | ||||||
|                      type="button" data-help-page="attachments.html" |                      type="button" data-help-page="attachments.html" | ||||||
| @@ -56,7 +64,11 @@ export default class AttachmentListTypeWidget extends TypeWidget { | |||||||
|             $(`<div class="attachment-actions-toolbar">`).append( |             $(`<div class="attachment-actions-toolbar">`).append( | ||||||
|                 $('<button class="btn btn-sm">') |                 $('<button class="btn btn-sm">') | ||||||
|                     .text(t("attachment_list.upload_attachments")) |                     .text(t("attachment_list.upload_attachments")) | ||||||
|                     .on("click", () => this.triggerCommand("showUploadAttachmentsDialog", { noteId: this.noteId })), |                     .on("click", () => { | ||||||
|  |                         if (this.noteId) { | ||||||
|  |                             this.triggerCommand("showUploadAttachmentsDialog", { noteId: this.noteId }) | ||||||
|  |                         } | ||||||
|  |                     }), | ||||||
|                 $helpButton |                 $helpButton | ||||||
|             ) |             ) | ||||||
|         ); |         ); | ||||||
| @@ -83,9 +95,9 @@ export default class AttachmentListTypeWidget extends TypeWidget { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async entitiesReloadedEvent({ loadResults }) { |     async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) { | ||||||
|         // updates and deletions are handled by the detail, for new attachments the whole list has to be refreshed
 |         // updates and deletions are handled by the detail, for new attachments the whole list has to be refreshed
 | ||||||
|         const attachmentsAdded = loadResults.getAttachmentRows().some((att) => !this.renderedAttachmentIds.has(att.attachmentId)); |         const attachmentsAdded = loadResults.getAttachmentRows().some((att) => att.attachmentId && !this.renderedAttachmentIds.has(att.attachmentId)); | ||||||
| 
 | 
 | ||||||
|         if (attachmentsAdded) { |         if (attachmentsAdded) { | ||||||
|             this.refresh(); |             this.refresh(); | ||||||
		Reference in New Issue
	
	Block a user