From 2a40d6bb7e7f56203d285cf21c423f9008c8187c Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 5 Aug 2025 23:03:38 +0300 Subject: [PATCH] feat(react/dialogs): port upload attachments --- .../src/translations/cn/translation.json | 2 +- .../src/translations/de/translation.json | 2 +- .../src/translations/en/translation.json | 2 +- .../src/translations/es/translation.json | 2 +- .../src/translations/fr/translation.json | 2 +- .../src/translations/ro/translation.json | 2 +- .../src/translations/sr/translation.json | 2 +- .../src/translations/tw/translation.json | 2 +- .../src/widgets/dialogs/upload_attachments.ts | 120 ------------------ .../widgets/dialogs/upload_attachments.tsx | 79 ++++++++++++ apps/client/src/widgets/react/Button.tsx | 4 +- .../src/widgets/react/FormFileUpload.tsx | 13 ++ 12 files changed, 103 insertions(+), 129 deletions(-) delete mode 100644 apps/client/src/widgets/dialogs/upload_attachments.ts create mode 100644 apps/client/src/widgets/dialogs/upload_attachments.tsx create mode 100644 apps/client/src/widgets/react/FormFileUpload.tsx diff --git a/apps/client/src/translations/cn/translation.json b/apps/client/src/translations/cn/translation.json index a3a85325b..cd1b86123 100644 --- a/apps/client/src/translations/cn/translation.json +++ b/apps/client/src/translations/cn/translation.json @@ -310,7 +310,7 @@ "upload_attachments_to_note": "上传附件到笔记", "close": "关闭", "choose_files": "选择文件", - "files_will_be_uploaded": "文件将作为附件上传到", + "files_will_be_uploaded": "文件将作为附件上传到 {{noteTitle}}", "options": "选项", "shrink_images": "缩小图片", "upload": "上传", diff --git a/apps/client/src/translations/de/translation.json b/apps/client/src/translations/de/translation.json index 431dd0027..5cb7dfa07 100644 --- a/apps/client/src/translations/de/translation.json +++ b/apps/client/src/translations/de/translation.json @@ -307,7 +307,7 @@ "upload_attachments_to_note": "Lade Anhänge zur Notiz hoch", "close": "Schließen", "choose_files": "Wähle Dateien aus", - "files_will_be_uploaded": "Dateien werden als Anhänge in hochgeladen", + "files_will_be_uploaded": "Dateien werden als Anhänge in hochgeladen {{noteTitle}}", "options": "Optionen", "shrink_images": "Bilder verkleinern", "upload": "Hochladen", diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 491addf3c..4060745ab 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -316,7 +316,7 @@ "upload_attachments_to_note": "Upload attachments to note", "close": "Close", "choose_files": "Choose files", - "files_will_be_uploaded": "Files will be uploaded as attachments into", + "files_will_be_uploaded": "Files will be uploaded as attachments into {{noteTitle}}", "options": "Options", "shrink_images": "Shrink images", "upload": "Upload", diff --git a/apps/client/src/translations/es/translation.json b/apps/client/src/translations/es/translation.json index 91095325f..6043ea6bc 100644 --- a/apps/client/src/translations/es/translation.json +++ b/apps/client/src/translations/es/translation.json @@ -312,7 +312,7 @@ "upload_attachments_to_note": "Cargar archivos adjuntos a nota", "close": "Cerrar", "choose_files": "Elija los archivos", - "files_will_be_uploaded": "Los archivos se cargarán como archivos adjuntos en", + "files_will_be_uploaded": "Los archivos se cargarán como archivos adjuntos en {{noteTitle}}", "options": "Opciones", "shrink_images": "Reducir imágenes", "upload": "Subir", diff --git a/apps/client/src/translations/fr/translation.json b/apps/client/src/translations/fr/translation.json index 0add71da1..53836b014 100644 --- a/apps/client/src/translations/fr/translation.json +++ b/apps/client/src/translations/fr/translation.json @@ -307,7 +307,7 @@ "upload_attachments_to_note": "Téléverser des pièces jointes à la note", "close": "Fermer", "choose_files": "Choisir des fichiers", - "files_will_be_uploaded": "Les fichiers seront téléversés sous forme de pièces jointes dans", + "files_will_be_uploaded": "Les fichiers seront téléversés sous forme de pièces jointes dans {{noteTitle}}", "options": "Options", "shrink_images": "Réduire les images", "upload": "Téléverser", diff --git a/apps/client/src/translations/ro/translation.json b/apps/client/src/translations/ro/translation.json index 2b1ba6db2..e4d6288ba 100644 --- a/apps/client/src/translations/ro/translation.json +++ b/apps/client/src/translations/ro/translation.json @@ -1309,7 +1309,7 @@ }, "upload_attachments": { "choose_files": "Selectați fișierele", - "files_will_be_uploaded": "Fișierele vor fi încărcate ca atașamente în", + "files_will_be_uploaded": "Fișierele vor fi încărcate ca atașamente în {{noteTitle}}", "options": "Opțuni", "shrink_images": "Micșorează imaginile", "tooltip": "Dacă această opțiune este bifată, Trilium va încerca micșorarea imaginilor încărcate prin scalarea și optimizarea lor, aspect ce va putea afecta calitatea imaginilor. Dacă nu este bifată, imaginile vor fi încărcate fără nicio schimbare.", diff --git a/apps/client/src/translations/sr/translation.json b/apps/client/src/translations/sr/translation.json index 1e5d8741c..c6eeb8bff 100644 --- a/apps/client/src/translations/sr/translation.json +++ b/apps/client/src/translations/sr/translation.json @@ -315,7 +315,7 @@ "upload_attachments_to_note": "Otpremite priloge uz belešku", "close": "Zatvori", "choose_files": "Izaberite datoteke", - "files_will_be_uploaded": "Datoteke će biti otpremljene kao prilozi u", + "files_will_be_uploaded": "Datoteke će biti otpremljene kao prilozi u {{noteTitle}}", "options": "Opcije", "shrink_images": "Smanji slike", "upload": "Otpremi", diff --git a/apps/client/src/translations/tw/translation.json b/apps/client/src/translations/tw/translation.json index 50daa3905..4140719b7 100644 --- a/apps/client/src/translations/tw/translation.json +++ b/apps/client/src/translations/tw/translation.json @@ -281,7 +281,7 @@ "upload_attachments": { "upload_attachments_to_note": "上傳附件到筆記", "choose_files": "選擇文件", - "files_will_be_uploaded": "文件將作為附件上傳到", + "files_will_be_uploaded": "文件將作為附件上傳到 {{noteTitle}}", "options": "選項", "shrink_images": "縮小圖片", "upload": "上傳", diff --git a/apps/client/src/widgets/dialogs/upload_attachments.ts b/apps/client/src/widgets/dialogs/upload_attachments.ts deleted file mode 100644 index 9b056db3e..000000000 --- a/apps/client/src/widgets/dialogs/upload_attachments.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { t } from "../../services/i18n.js"; -import { escapeQuotes } from "../../services/utils.js"; -import treeService from "../../services/tree.js"; -import importService from "../../services/import.js"; -import options from "../../services/options.js"; -import BasicWidget from "../basic_widget.js"; -import { Modal, Tooltip } from "bootstrap"; -import type { EventData } from "../../components/app_context.js"; -import { openDialog } from "../../services/dialog.js"; - -const TPL = /*html*/` -`; - -export default class UploadAttachmentsDialog extends BasicWidget { - - private parentNoteId: string | null; - private modal!: bootstrap.Modal; - private $form!: JQuery; - private $noteTitle!: JQuery; - private $fileUploadInput!: JQuery; - private $uploadButton!: JQuery; - private $shrinkImagesCheckbox!: JQuery; - - constructor() { - super(); - - this.parentNoteId = null; - } - - doRender() { - this.$widget = $(TPL); - this.modal = Modal.getOrCreateInstance(this.$widget[0]); - - this.$form = this.$widget.find(".upload-attachment-form"); - this.$noteTitle = this.$widget.find(".upload-attachment-note-title"); - this.$fileUploadInput = this.$widget.find(".upload-attachment-file-upload-input"); - this.$uploadButton = this.$widget.find(".upload-attachment-button"); - this.$shrinkImagesCheckbox = this.$widget.find(".shrink-images-checkbox"); - - this.$form.on("submit", () => { - // disabling so that import is not triggered again. - this.$uploadButton.attr("disabled", "disabled"); - if (this.parentNoteId) { - this.uploadAttachments(this.parentNoteId); - } - return false; - }); - - this.$fileUploadInput.on("change", () => { - if (this.$fileUploadInput.val()) { - this.$uploadButton.removeAttr("disabled"); - } else { - this.$uploadButton.attr("disabled", "disabled"); - } - }); - - Tooltip.getOrCreateInstance(this.$widget.find('[data-bs-toggle="tooltip"]')[0], { - html: true - }); - } - - async showUploadAttachmentsDialogEvent({ noteId }: EventData<"showUploadAttachmentsDialog">) { - this.parentNoteId = noteId; - - this.$fileUploadInput.val("").trigger("change"); // to trigger upload button disabling listener below - this.$shrinkImagesCheckbox.prop("checked", options.is("compressImages")); - - this.$noteTitle.text(await treeService.getNoteTitle(this.parentNoteId)); - - openDialog(this.$widget); - } - - async uploadAttachments(parentNoteId: string) { - const files = Array.from(this.$fileUploadInput[0].files ?? []); // shallow copy since we're resetting the upload button below - - function boolToString($el: JQuery): "true" | "false" { - return ($el.is(":checked") ? "true" : "false"); - } - - const options = { - shrinkImages: boolToString(this.$shrinkImagesCheckbox) - }; - - this.modal.hide(); - - await importService.uploadFiles("attachments", parentNoteId, files, options); - } -} diff --git a/apps/client/src/widgets/dialogs/upload_attachments.tsx b/apps/client/src/widgets/dialogs/upload_attachments.tsx new file mode 100644 index 000000000..b0500c864 --- /dev/null +++ b/apps/client/src/widgets/dialogs/upload_attachments.tsx @@ -0,0 +1,79 @@ +import { useEffect, useState } from "preact/compat"; +import { closeActiveDialog, openDialog } from "../../services/dialog"; +import { t } from "../../services/i18n"; +import Button from "../react/Button"; +import FormCheckbox from "../react/FormCheckbox"; +import FormFileUpload from "../react/FormFileUpload"; +import FormGroup from "../react/FormGroup"; +import Modal from "../react/Modal"; +import ReactBasicWidget from "../react/ReactBasicWidget"; +import options from "../../services/options"; +import importService from "../../services/import.js"; +import { EventData } from "../../components/app_context"; +import tree from "../../services/tree"; + +interface UploadAttachmentsDialogProps { + parentNoteId?: string; +} + +function UploadAttachmentsDialogComponent({ parentNoteId }: UploadAttachmentsDialogProps) { + const [ files, setFiles ] = useState(null); + const [ shrinkImages, setShrinkImages ] = useState(options.is("compressImages")); + const [ isUploading, setIsUploading ] = useState(false); + const [ description, setDescription ] = useState(undefined); + + if (parentNoteId) { + useEffect(() => { + tree.getNoteTitle(parentNoteId).then((noteTitle) => + setDescription(t("upload_attachments.files_will_be_uploaded", { noteTitle }))); + }, [parentNoteId]); + } + + return (parentNoteId && + } + onSubmit={async () => { + if (!files) { + return; + } + + setIsUploading(true); + const filesCopy = Array.from(files); + await importService.uploadFiles("attachments", parentNoteId, filesCopy, { shrinkImages }); + setIsUploading(false); + closeActiveDialog(); + }} + > + + + + + + + + + ); +} + +export default class UploadAttachmentsDialog extends ReactBasicWidget { + + private props: UploadAttachmentsDialogProps = {}; + + get component() { + return ; + } + + showUploadAttachmentsDialogEvent({ noteId }: EventData<"showUploadAttachmentsDialog">) { + this.props = { parentNoteId: noteId }; + this.doRender(); + openDialog(this.$widget); + } + +} diff --git a/apps/client/src/widgets/react/Button.tsx b/apps/client/src/widgets/react/Button.tsx index dc4e61dcd..dcf62f9f3 100644 --- a/apps/client/src/widgets/react/Button.tsx +++ b/apps/client/src/widgets/react/Button.tsx @@ -11,9 +11,10 @@ interface ButtonProps { /** Called when the button is clicked. If not set, the button will submit the form (if any). */ onClick?: () => void; primary?: boolean; + disabled?: boolean; } -export default function Button({ buttonRef: _buttonRef, className, text, onClick, keyboardShortcut, icon, primary }: ButtonProps) { +export default function Button({ buttonRef: _buttonRef, className, text, onClick, keyboardShortcut, icon, primary, disabled }: ButtonProps) { const classes: string[] = ["btn"]; if (primary) { classes.push("btn-primary"); @@ -33,6 +34,7 @@ export default function Button({ buttonRef: _buttonRef, className, text, onClick type={onClick ? "button" : "submit"} onClick={onClick} ref={buttonRef} + disabled={disabled} > {icon && } {text} {keyboardShortcut && ( diff --git a/apps/client/src/widgets/react/FormFileUpload.tsx b/apps/client/src/widgets/react/FormFileUpload.tsx new file mode 100644 index 000000000..c2f53a5a9 --- /dev/null +++ b/apps/client/src/widgets/react/FormFileUpload.tsx @@ -0,0 +1,13 @@ +interface FormFileUploadProps { + onChange: (files: FileList | null) => void; + multiple?: boolean; +} + +export default function FormFileUpload({ onChange, multiple }: FormFileUploadProps) { + return ( + + ) +} \ No newline at end of file