From 31170744d1ee650a75d5ccf9526c8c806a42fc94 Mon Sep 17 00:00:00 2001 From: Panagiotis Papadopoulos Date: Sat, 22 Feb 2025 16:46:23 +0100 Subject: [PATCH 01/15] chore(ts): allow link.createLink notePath type to accept undefined it can also accept undefined -> it is even handled in that first if block. change required for upcoming port of attachment_*.js files --- src/public/app/services/link.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/public/app/services/link.ts b/src/public/app/services/link.ts index 472f7d37a..9b3c779bb 100644 --- a/src/public/app/services/link.ts +++ b/src/public/app/services/link.ts @@ -70,7 +70,7 @@ interface CreateLinkOptions { viewScope?: ViewScope; } -async function createLink(notePath: string, options: CreateLinkOptions = {}) { +async function createLink(notePath: string | undefined, options: CreateLinkOptions = {}) { if (!notePath || !notePath.trim()) { logError("Missing note path"); From 70756fe7955a1ecbaf37d95b672ca510c9435ca7 Mon Sep 17 00:00:00 2001 From: Panagiotis Papadopoulos Date: Sat, 22 Feb 2025 16:58:55 +0100 Subject: [PATCH 02/15] chore(ts): start port of type_widgets/attachment_detail --- .../{attachment_detail.js => attachment_detail.ts} | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) rename src/public/app/widgets/type_widgets/{attachment_detail.js => attachment_detail.ts} (84%) diff --git a/src/public/app/widgets/type_widgets/attachment_detail.js b/src/public/app/widgets/type_widgets/attachment_detail.ts similarity index 84% rename from src/public/app/widgets/type_widgets/attachment_detail.js rename to src/public/app/widgets/type_widgets/attachment_detail.ts index 108c5da2e..5fc8e4201 100644 --- a/src/public/app/widgets/type_widgets/attachment_detail.js +++ b/src/public/app/widgets/type_widgets/attachment_detail.ts @@ -4,6 +4,8 @@ import linkService from "../../services/link.js"; import froca from "../../services/froca.js"; import utils from "../../services/utils.js"; import { t } from "../../services/i18n.js"; +import type FNote from "../../entities/fnote.js"; +import type { EventData } from "../../components/app_context.js"; const TPL = `
@@ -32,6 +34,9 @@ const TPL = `
`; export default class AttachmentDetailTypeWidget extends TypeWidget { + $wrapper!: JQuery; + $linksWrapper!: JQuery; + static getType() { return "attachmentDetail"; } @@ -44,7 +49,7 @@ export default class AttachmentDetailTypeWidget extends TypeWidget { super.doRender(); } - async doRefresh(note) { + async doRefresh(note: Parameters[0]) { this.$wrapper.empty(); this.children = []; @@ -69,7 +74,7 @@ export default class AttachmentDetailTypeWidget extends TypeWidget { $helpButton ); - const attachment = await froca.getAttachment(this.attachmentId, true); + const attachment = (this.attachmentId) ? await froca.getAttachment(this.attachmentId, true) : null; if (!attachment) { this.$wrapper.html("" + t("attachment_detail.attachment_deleted") + ""); @@ -82,7 +87,7 @@ export default class AttachmentDetailTypeWidget extends TypeWidget { this.$wrapper.append(attachmentDetailWidget.render()); } - async entitiesReloadedEvent({ loadResults }) { + async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) { const attachmentRow = loadResults.getAttachmentRows().find((att) => att.attachmentId === this.attachmentId); if (attachmentRow?.isDeleted) { @@ -91,6 +96,6 @@ export default class AttachmentDetailTypeWidget extends TypeWidget { } get attachmentId() { - return this.noteContext.viewScope.attachmentId; + return this?.noteContext?.viewScope?.attachmentId; } } From bf15192b252158bb0d6b9dc6bb364bb3dd3bb086 Mon Sep 17 00:00:00 2001 From: Panagiotis Papadopoulos Date: Sat, 22 Feb 2025 17:39:30 +0100 Subject: [PATCH 03/15] chore(ts): start port of widgets/buttons/attachment_actions --- ...ents_actions.js => attachments_actions.ts} | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) rename src/public/app/widgets/buttons/{attachments_actions.js => attachments_actions.ts} (86%) diff --git a/src/public/app/widgets/buttons/attachments_actions.js b/src/public/app/widgets/buttons/attachments_actions.ts similarity index 86% rename from src/public/app/widgets/buttons/attachments_actions.js rename to src/public/app/widgets/buttons/attachments_actions.ts index 5ee51b9d9..f25e9c376 100644 --- a/src/public/app/widgets/buttons/attachments_actions.js +++ b/src/public/app/widgets/buttons/attachments_actions.ts @@ -8,6 +8,8 @@ import appContext from "../../components/app_context.js"; import openService from "../../services/open.js"; import utils from "../../services/utils.js"; import { Dropdown } from "bootstrap"; +import type attachmentsApiRoute from "../../../../routes/api/attachments.js" +import type FAttachment from "../../entities/fattachment.js"; const TPL = ` `; export default class AttachmentActionsWidget extends BasicWidget { - constructor(attachment, isFullDetail) { + $uploadNewRevisionInput!: JQuery; + attachment: FAttachment; + isFullDetail: boolean; + dropdown!: Dropdown; + + constructor(attachment: FAttachment, isFullDetail: boolean) { super(); this.attachment = attachment; @@ -92,20 +99,21 @@ export default class AttachmentActionsWidget extends BasicWidget { doRender() { 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.$uploadNewRevisionInput = this.$widget.find(".attachment-upload-new-revision-input"); 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(""); - - const result = await server.upload(`attachments/${this.attachmentId}/file`, fileToUpload); - - if (result.uploaded) { - toastService.showMessage(t("attachments_actions.upload_success")); - } else { - toastService.showError(t("attachments_actions.upload_failed")); + if (fileToUpload) { + const result = await server.upload(`attachments/${this.attachmentId}/file`, fileToUpload); + if (result.uploaded) { + toastService.showMessage(t("attachments_actions.upload_success")); + } else { + toastService.showError(t("attachments_actions.upload_failed")); + } } }); @@ -122,6 +130,7 @@ export default class AttachmentActionsWidget extends BasicWidget { const $openAttachmentCustomButton = this.$widget.find("[data-trigger-command='openAttachmentCustom']"); $openAttachmentCustomButton.addClass("disabled").append($('').attr("title", t("attachments_actions.open_custom_client_only"))); } + } async openAttachmentCommand() { @@ -141,7 +150,8 @@ export default class AttachmentActionsWidget extends BasicWidget { } async copyAttachmentLinkToClipboardCommand() { - this.parent.copyAttachmentLinkToClipboard(); + //TriliumNextTODO: the parent here is AttachmentDetailWidget + this.parent?.copyAttachmentLinkToClipboard(); } async deleteAttachmentCommand() { @@ -158,7 +168,8 @@ export default class AttachmentActionsWidget extends BasicWidget { return; } - const { note: newNote } = await server.post(`attachments/${this.attachmentId}/convert-to-note`); + + const { note: newNote } = await server.post>(`attachments/${this.attachmentId}/convert-to-note`); toastService.showMessage(t("attachments_actions.convert_success", { title: this.attachment.title })); await ws.waitForMaxKnownEntityChangeId(); await appContext.tabManager.getActiveContext().setNote(newNote.noteId); From f6785f7980ae9422087dc6262b0c4883a8c60008 Mon Sep 17 00:00:00 2001 From: Panagiotis Papadopoulos Date: Sat, 22 Feb 2025 18:38:00 +0100 Subject: [PATCH 04/15] chore(ts): add missing isDeleted and deleteId types to AttachmentRow --- src/becca/entities/rows.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/becca/entities/rows.ts b/src/becca/entities/rows.ts index ba9189190..b57801e1e 100644 --- a/src/becca/entities/rows.ts +++ b/src/becca/entities/rows.ts @@ -1,4 +1,5 @@ // 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 { attachmentId?: string; @@ -12,6 +13,8 @@ export interface AttachmentRow { dateModified?: string; utcDateModified?: string; utcDateScheduledForErasureSince?: string; + isDeleted?: boolean; + deleteId?: string; contentLength?: number; content?: Buffer | string; } From 8f643c62e3cf336b8db4c9f22f82ce8b9e24b025 Mon Sep 17 00:00:00 2001 From: Panagiotis Papadopoulos Date: Sat, 22 Feb 2025 18:48:46 +0100 Subject: [PATCH 05/15] chore(ts): port of type_widgets/attachment_list --- ...{attachment_list.js => attachment_list.ts} | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) rename src/public/app/widgets/type_widgets/{attachment_list.js => attachment_list.ts} (79%) diff --git a/src/public/app/widgets/type_widgets/attachment_list.js b/src/public/app/widgets/type_widgets/attachment_list.ts similarity index 79% rename from src/public/app/widgets/type_widgets/attachment_list.js rename to src/public/app/widgets/type_widgets/attachment_list.ts index f6eadb97e..3018f784b 100644 --- a/src/public/app/widgets/type_widgets/attachment_list.js +++ b/src/public/app/widgets/type_widgets/attachment_list.ts @@ -3,6 +3,7 @@ import AttachmentDetailWidget from "../attachment_detail.js"; import linkService from "../../services/link.js"; import utils from "../../services/utils.js"; import { t } from "../../services/i18n.js"; +import type { EventData } from "../../components/app_context.js"; const TPL = `
@@ -27,6 +28,10 @@ const TPL = `
`; export default class AttachmentListTypeWidget extends TypeWidget { + $list!: JQuery; + $linksWrapper!: JQuery; + renderedAttachmentIds!: Set; + static getType() { return "attachmentList"; } @@ -39,7 +44,10 @@ export default class AttachmentListTypeWidget extends TypeWidget { super.doRender(); } - async doRefresh(note) { + async doRefresh(note: Parameters[0]) { + // TriliumNextTODO: do we need to handle an undefined/null note? + if (!note) return false; + const $helpButton = $(` @@ -45,17 +46,17 @@ const TPL = ` - + - + - + @@ -70,13 +71,13 @@ const TPL = ` - + - + - + `; @@ -150,10 +151,9 @@ export default class AttachmentActionsWidget extends BasicWidget { } async copyAttachmentLinkToClipboardCommand() { - //TriliumNextTODO: the parent here is AttachmentDetailWidget - //how can we pass that to the generic TypedComponent? - //@ts-ignore - TypedComponent - this.parent?.copyAttachmentLinkToClipboard(); + if (this.parent && "copyAttachmentLinkToClipboard" in this.parent) { + (this.parent as AttachmentDetailWidget).copyAttachmentLinkToClipboard(); + } } async deleteAttachmentCommand() {