Files
Trilium/apps/client/src/widgets/buttons/attachments_actions.ts

191 lines
7.9 KiB
TypeScript
Raw Normal View History

import { t } from "../../services/i18n.js";
2023-03-24 10:57:32 +01:00
import BasicWidget from "../basic_widget.js";
2023-03-30 23:48:26 +02:00
import server from "../../services/server.js";
2023-04-01 13:58:53 +02:00
import dialogService from "../../services/dialog.js";
2023-04-01 23:55:04 +02:00
import toastService from "../../services/toast.js";
import ws from "../../services/ws.js";
import appContext from "../../components/app_context.js";
2023-05-03 10:23:20 +02:00
import openService from "../../services/open.js";
2023-09-07 06:33:51 +00:00
import utils from "../../services/utils.js";
import { Dropdown } from "bootstrap";
2025-03-02 20:47:57 +01:00
import type attachmentsApiRoute from "../../../../routes/api/attachments.js";
import type FAttachment from "../../entities/fattachment.js";
import type AttachmentDetailWidget from "../attachment_detail.js";
2023-03-24 10:57:32 +01:00
const TPL = /*html*/`
2023-03-24 10:57:32 +01:00
<div class="dropdown attachment-actions">
<style>
.attachment-actions {
width: 35px;
height: 35px;
}
2023-03-24 10:57:32 +01:00
.attachment-actions .dropdown-menu {
2023-04-01 23:55:04 +02:00
width: 20em;
2023-03-24 10:57:32 +01:00
}
2024-11-19 22:21:33 +08:00
.attachment-actions .dropdown-item .bx {
position: relative;
top: 3px;
font-size: 120%;
margin-right: 5px;
}
2023-03-24 10:57:32 +01:00
.attachment-actions .dropdown-item[disabled], .attachment-actions .dropdown-item[disabled]:hover {
color: var(--muted-text-color) !important;
background-color: transparent !important;
pointer-events: none; /* makes it unclickable */
}
</style>
<button type="button" data-bs-toggle="dropdown" aria-haspopup="true"
2023-06-14 00:28:59 +02:00
aria-expanded="false" class="icon-action icon-action-always-border bx bx-dots-vertical-rounded"
2023-06-30 12:14:58 +02:00
style="position: relative; top: 3px;"></button>
2023-03-24 10:57:32 +01:00
<div class="dropdown-menu dropdown-menu-right">
2024-11-20 00:11:54 +02:00
2024-11-19 22:21:33 +08:00
<li data-trigger-command="openAttachment" class="dropdown-item"
2025-01-09 18:07:02 +02:00
title="${t("attachments_actions.open_externally_title")}"><span class="bx bx-file-find"></span> ${t("attachments_actions.open_externally")}</li>
2024-11-19 22:21:33 +08:00
<li data-trigger-command="openAttachmentCustom" class="dropdown-item"
2025-01-09 18:07:02 +02:00
title="${t("attachments_actions.open_custom_title")}"><span class="bx bx-customize"></span> ${t("attachments_actions.open_custom")}</li>
2024-11-19 22:21:33 +08:00
<li data-trigger-command="downloadAttachment" class="dropdown-item">
2025-01-09 18:07:02 +02:00
<span class="bx bx-download"></span> ${t("attachments_actions.download")}</li>
2024-11-20 00:11:54 +02:00
<li data-trigger-command="copyAttachmentLinkToClipboard" class="dropdown-item"><span class="bx bx-link">
2025-01-09 18:07:02 +02:00
</span> ${t("attachments_actions.copy_link_to_clipboard")}</li>
2024-11-20 00:11:54 +02:00
2024-11-20 00:11:54 +02:00
<div class="dropdown-divider"></div>
2024-11-19 22:21:33 +08:00
<li data-trigger-command="uploadNewAttachmentRevision" class="dropdown-item"><span class="bx bx-upload">
2025-01-09 18:07:02 +02:00
</span> ${t("attachments_actions.upload_new_revision")}</li>
2024-11-20 00:11:54 +02:00
<li data-trigger-command="renameAttachment" class="dropdown-item">
2025-01-09 18:07:02 +02:00
<span class="bx bx-rename"></span> ${t("attachments_actions.rename_attachment")}</li>
2024-11-20 00:11:54 +02:00
<li data-trigger-command="deleteAttachment" class="dropdown-item">
2025-01-09 18:07:02 +02:00
<span class="bx bx-trash destructive-action-icon"></span> ${t("attachments_actions.delete_attachment")}</li>
2024-11-20 00:11:54 +02:00
<div class="dropdown-divider"></div>
2024-11-20 00:11:54 +02:00
2024-11-19 22:21:33 +08:00
<li data-trigger-command="convertAttachmentIntoNote" class="dropdown-item"><span class="bx bx-note">
2025-01-09 18:07:02 +02:00
</span> ${t("attachments_actions.convert_attachment_into_note")}</li>
2023-03-24 10:57:32 +01:00
</div>
2023-05-03 22:49:24 +02:00
<input type="file" class="attachment-upload-new-revision-input" style="display: none">
2023-03-24 10:57:32 +01:00
</div>`;
export default class AttachmentActionsWidget extends BasicWidget {
$uploadNewRevisionInput!: JQuery<HTMLInputElement>;
attachment: FAttachment;
isFullDetail: boolean;
dropdown!: Dropdown;
constructor(attachment: FAttachment, isFullDetail: boolean) {
2023-03-24 10:57:32 +01:00
super();
this.attachment = attachment;
this.isFullDetail = isFullDetail;
2023-03-24 10:57:32 +01:00
}
2023-05-03 10:23:20 +02:00
get attachmentId() {
return this.attachment.attachmentId;
}
2023-03-24 10:57:32 +01:00
doRender() {
this.$widget = $(TPL);
this.dropdown = Dropdown.getOrCreateInstance(this.$widget.find("[data-bs-toggle='dropdown']")[0]);
2025-01-09 18:07:02 +02:00
this.$widget.on("click", ".dropdown-item", () => this.dropdown.toggle());
2023-05-03 22:49:24 +02:00
this.$uploadNewRevisionInput = this.$widget.find(".attachment-upload-new-revision-input");
2025-01-09 18:07:02 +02:00
this.$uploadNewRevisionInput.on("change", async () => {
2025-03-02 20:47:57 +01:00
const fileToUpload = this.$uploadNewRevisionInput[0].files?.item(0); // copy to allow reset below
this.$uploadNewRevisionInput.val("");
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"));
}
2023-05-03 22:49:24 +02:00
}
});
const isElectron = utils.isElectron();
if (!this.isFullDetail) {
const $openAttachmentButton = this.$widget.find("[data-trigger-command='openAttachment']");
2025-01-09 18:07:02 +02:00
$openAttachmentButton.addClass("disabled").append($('<span class="bx bx-info-circle disabled-tooltip" />').attr("title", t("attachments_actions.open_externally_detail_page")));
if (isElectron) {
const $openAttachmentCustomButton = this.$widget.find("[data-trigger-command='openAttachmentCustom']");
2025-01-09 18:07:02 +02:00
$openAttachmentCustomButton.addClass("disabled").append($('<span class="bx bx-info-circle disabled-tooltip" />').attr("title", t("attachments_actions.open_externally_detail_page")));
}
2023-09-07 06:33:51 +00:00
}
if (!isElectron) {
2023-09-07 06:33:51 +00:00
const $openAttachmentCustomButton = this.$widget.find("[data-trigger-command='openAttachmentCustom']");
2025-01-09 18:07:02 +02:00
$openAttachmentCustomButton.addClass("disabled").append($('<span class="bx bx-info-circle disabled-tooltip" />').attr("title", t("attachments_actions.open_custom_client_only")));
}
2023-05-03 10:23:20 +02:00
}
async openAttachmentCommand() {
await openService.openAttachmentExternally(this.attachmentId, this.attachment.mime);
}
2023-09-07 06:33:51 +00:00
async openAttachmentCustomCommand() {
await openService.openAttachmentCustom(this.attachmentId, this.attachment.mime);
}
2023-05-03 10:23:20 +02:00
async downloadAttachmentCommand() {
await openService.downloadAttachment(this.attachmentId);
}
2023-05-03 22:49:24 +02:00
async uploadNewAttachmentRevisionCommand() {
2025-01-09 18:07:02 +02:00
this.$uploadNewRevisionInput.trigger("click");
2023-05-03 22:49:24 +02:00
}
2023-05-26 10:36:05 +02:00
async copyAttachmentLinkToClipboardCommand() {
if (this.parent && "copyAttachmentLinkToClipboard" in this.parent) {
(this.parent as AttachmentDetailWidget).copyAttachmentLinkToClipboard();
}
2023-05-03 10:23:20 +02:00
}
2023-03-30 23:48:26 +02:00
async deleteAttachmentCommand() {
2025-01-09 18:07:02 +02:00
if (!(await dialogService.confirm(t("attachments_actions.delete_confirm", { title: this.attachment.title })))) {
return;
2023-04-01 23:55:04 +02:00
}
2023-05-03 10:23:20 +02:00
await server.remove(`attachments/${this.attachmentId}`);
2025-01-09 18:07:02 +02:00
toastService.showMessage(t("attachments_actions.delete_success", { title: this.attachment.title }));
2023-04-01 23:55:04 +02:00
}
async convertAttachmentIntoNoteCommand() {
2025-01-09 18:07:02 +02:00
if (!(await dialogService.confirm(t("attachments_actions.convert_confirm", { title: this.attachment.title })))) {
return;
2023-04-01 13:58:53 +02:00
}
const { note: newNote } = await server.post<ReturnType<typeof attachmentsApiRoute.convertAttachmentToNote>>(`attachments/${this.attachmentId}/convert-to-note`);
2025-01-09 18:07:02 +02:00
toastService.showMessage(t("attachments_actions.convert_success", { title: this.attachment.title }));
await ws.waitForMaxKnownEntityChangeId();
2025-03-03 21:02:18 +01:00
await appContext.tabManager.getActiveContext()?.setNote(newNote.noteId);
2023-03-24 10:57:32 +01:00
}
2023-06-14 00:28:59 +02:00
async renameAttachmentCommand() {
const attachmentTitle = await dialogService.prompt({
2025-01-09 18:07:02 +02:00
title: t("attachments_actions.rename_attachment"),
message: t("attachments_actions.enter_new_name"),
2023-06-14 00:28:59 +02:00
defaultValue: this.attachment.title
});
if (!attachmentTitle?.trim()) {
return;
}
await server.put(`attachments/${this.attachmentId}/rename`, { title: attachmentTitle });
2023-06-14 00:28:59 +02:00
}
2023-03-24 10:57:32 +01:00
}