mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-27 18:12:29 +08: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:
commit
937a314260
@ -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">
|
||||||
@ -16,11 +19,11 @@ const TPL = `
|
|||||||
width: 35px;
|
width: 35px;
|
||||||
height: 35px;
|
height: 35px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.attachment-actions .dropdown-menu {
|
.attachment-actions .dropdown-menu {
|
||||||
width: 20em;
|
width: 20em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.attachment-actions .dropdown-item .bx {
|
.attachment-actions .dropdown-item .bx {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 3px;
|
top: 3px;
|
||||||
@ -35,7 +38,7 @@ const TPL = `
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<button type="button" data-bs-toggle="dropdown" aria-haspopup="true"
|
<button type="button" data-bs-toggle="dropdown" aria-haspopup="true"
|
||||||
aria-expanded="false" class="icon-action icon-action-always-border bx bx-dots-vertical-rounded"
|
aria-expanded="false" class="icon-action icon-action-always-border bx bx-dots-vertical-rounded"
|
||||||
style="position: relative; top: 3px;"></button>
|
style="position: relative; top: 3px;"></button>
|
||||||
|
|
||||||
@ -43,17 +46,17 @@ const TPL = `
|
|||||||
|
|
||||||
<li data-trigger-command="openAttachment" class="dropdown-item"
|
<li data-trigger-command="openAttachment" class="dropdown-item"
|
||||||
title="${t("attachments_actions.open_externally_title")}"><span class="bx bx-file-find"></span> ${t("attachments_actions.open_externally")}</li>
|
title="${t("attachments_actions.open_externally_title")}"><span class="bx bx-file-find"></span> ${t("attachments_actions.open_externally")}</li>
|
||||||
|
|
||||||
<li data-trigger-command="openAttachmentCustom" class="dropdown-item"
|
<li data-trigger-command="openAttachmentCustom" class="dropdown-item"
|
||||||
title="${t("attachments_actions.open_custom_title")}"><span class="bx bx-customize"></span> ${t("attachments_actions.open_custom")}</li>
|
title="${t("attachments_actions.open_custom_title")}"><span class="bx bx-customize"></span> ${t("attachments_actions.open_custom")}</li>
|
||||||
|
|
||||||
<li data-trigger-command="downloadAttachment" class="dropdown-item">
|
<li data-trigger-command="downloadAttachment" class="dropdown-item">
|
||||||
<span class="bx bx-download"></span> ${t("attachments_actions.download")}</li>
|
<span class="bx bx-download"></span> ${t("attachments_actions.download")}</li>
|
||||||
|
|
||||||
<li data-trigger-command="copyAttachmentLinkToClipboard" class="dropdown-item"><span class="bx bx-link">
|
<li data-trigger-command="copyAttachmentLinkToClipboard" class="dropdown-item"><span class="bx bx-link">
|
||||||
</span> ${t("attachments_actions.copy_link_to_clipboard")}</li>
|
</span> ${t("attachments_actions.copy_link_to_clipboard")}</li>
|
||||||
|
|
||||||
|
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
|
|
||||||
|
|
||||||
@ -68,18 +71,23 @@ const TPL = `
|
|||||||
|
|
||||||
|
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
|
|
||||||
|
|
||||||
<li data-trigger-command="convertAttachmentIntoNote" class="dropdown-item"><span class="bx bx-note">
|
<li data-trigger-command="convertAttachmentIntoNote" class="dropdown-item"><span class="bx bx-note">
|
||||||
</span> ${t("attachments_actions.convert_attachment_into_note")}</li>
|
</span> ${t("attachments_actions.convert_attachment_into_note")}</li>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<input type="file" class="attachment-upload-new-revision-input" style="display: none">
|
<input type="file" class="attachment-upload-new-revision-input" style="display: none">
|
||||||
</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,20 +100,21 @@ 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"));
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -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();
|
Loading…
x
Reference in New Issue
Block a user