From 310cd513d88e3670c7008682ae4b35414084a17d Mon Sep 17 00:00:00 2001 From: Jarek Radosz Date: Wed, 12 Feb 2025 23:31:42 +0100 Subject: [PATCH] DEV: Convert UppyImageUploader to gjs (#31310) --- .../addon/components/site-settings/upload.hbs | 2 + .../app/components/uppy-image-uploader.gjs | 216 ++++++++++++++++++ .../app/components/uppy-image-uploader.hbs | 65 ------ .../app/components/uppy-image-uploader.js | 157 ------------- .../form-kit/components/fk/control/image.gjs | 3 +- .../app/templates/preferences/profile.hbs | 10 + .../tests/helpers/form-kit-assertions.js | 4 +- ...r-test.js => uppy-image-uploader-test.gjs} | 47 ++-- 8 files changed, 255 insertions(+), 249 deletions(-) create mode 100644 app/assets/javascripts/discourse/app/components/uppy-image-uploader.gjs delete mode 100644 app/assets/javascripts/discourse/app/components/uppy-image-uploader.hbs delete mode 100644 app/assets/javascripts/discourse/app/components/uppy-image-uploader.js rename app/assets/javascripts/discourse/tests/integration/components/{uppy-image-uploader-test.js => uppy-image-uploader-test.gjs} (72%) diff --git a/app/assets/javascripts/admin/addon/components/site-settings/upload.hbs b/app/assets/javascripts/admin/addon/components/site-settings/upload.hbs index cad0a33a03d..c421c746bdb 100644 --- a/app/assets/javascripts/admin/addon/components/site-settings/upload.hbs +++ b/app/assets/javascripts/admin/addon/components/site-settings/upload.hbs @@ -1,6 +1,8 @@ ({ + target: document.querySelector( + `#${this.args.id} .uploaded-image-preview` + ), + }), + uploadDone: (upload) => { + this.imageFilesize = upload.human_filesize; + this.imageFilename = upload.original_filename; + this.imageWidth = upload.width; + this.imageHeight = upload.height; + + this.args.onUploadDone(upload); + }, + }); + + applyLightbox = modifier((element) => lightbox(element, this.siteSettings)); + + willDestroy() { + super.willDestroy(...arguments); + $.magnificPopup?.instance.close(); + } + + get disabled() { + return ( + this.notAllowed || + this.uppyUpload?.uploading || + this.uppyUpload?.processing + ); + } + + get computedId() { + // without a fallback ID this will not be accessible + return this.args.id ? `${this.args.id}__input` : `${guidFor(this)}__input`; + } + + get disabledReason() { + if (this.disabled && this.notAllowed) { + return i18n("post.errors.no_uploads_authorized"); + } + } + + get notAllowed() { + return !authorizesOneOrMoreExtensions( + this.currentUser?.staff, + this.siteSettings + ); + } + + get showingPlaceholder() { + return !this.args.imageUrl && this.args.placeholderUrl; + } + + get placeholderStyle() { + if (isEmpty(this.args.placeholderUrl)) { + return htmlSafe(""); + } + return htmlSafe(`background-image: url(${this.args.placeholderUrl})`); + } + + get imageCdnUrl() { + if (isEmpty(this.args.imageUrl)) { + return htmlSafe(""); + } + + return getURLWithCDN(this.args.imageUrl); + } + + get backgroundStyle() { + return htmlSafe(`background-image: url(${this.imageCdnUrl})`); + } + + get imageBaseName() { + if (!isEmpty(this.args.imageUrl)) { + return this.args.imageUrl.split("/").slice(-1)[0]; + } + } + + @action + toggleLightbox() { + const lightboxElement = document.querySelector( + `#${this.args.id} a.lightbox` + ); + + if (lightboxElement) { + $(lightboxElement).magnificPopup("open"); + } + } + + @action + handleKeyboardActivation(event) { + if (event.key === "Enter" || event.key === " ") { + event.preventDefault(); // avoid space scrolling the page + const input = document.getElementById(this.computedId); + if (input && !this.disabled) { + input.click(); + } + } + } + + +} diff --git a/app/assets/javascripts/discourse/app/components/uppy-image-uploader.hbs b/app/assets/javascripts/discourse/app/components/uppy-image-uploader.hbs deleted file mode 100644 index 39c51a7da7f..00000000000 --- a/app/assets/javascripts/discourse/app/components/uppy-image-uploader.hbs +++ /dev/null @@ -1,65 +0,0 @@ -
- {{#if this.showingPlaceholder}} -
- {{/if}} -
- - - {{#if this.imageUrl}} - - - {{/if}} - - {{i18n - "upload_selector.uploading" - }} - {{this.uppyUpload.uploadProgress}}% - {{i18n - "upload_selector.processing" - }} -
- - {{#if this.imageUrl}} - - -
- - {{this.imageWidth}}x{{this.imageHeight}} - {{this.imageFilesize}} - -
-
- {{/if}} -
\ No newline at end of file diff --git a/app/assets/javascripts/discourse/app/components/uppy-image-uploader.js b/app/assets/javascripts/discourse/app/components/uppy-image-uploader.js deleted file mode 100644 index dde24ff5625..00000000000 --- a/app/assets/javascripts/discourse/app/components/uppy-image-uploader.js +++ /dev/null @@ -1,157 +0,0 @@ -import Component from "@ember/component"; -import { action } from "@ember/object"; -import { or } from "@ember/object/computed"; -import { guidFor } from "@ember/object/internals"; -import { getOwner } from "@ember/owner"; -import { next } from "@ember/runloop"; -import { htmlSafe } from "@ember/template"; -import { isEmpty } from "@ember/utils"; -import { classNames } from "@ember-decorators/component"; -import { on } from "@ember-decorators/object"; -import $ from "jquery"; -import discourseComputed from "discourse/lib/decorators"; -import { getURLWithCDN } from "discourse/lib/get-url"; -import lightbox from "discourse/lib/lightbox"; -import { authorizesOneOrMoreExtensions } from "discourse/lib/uploads"; -import UppyUpload from "discourse/lib/uppy/uppy-upload"; -import { i18n } from "discourse-i18n"; - -@classNames("image-uploader") -export default class UppyImageUploader extends Component { - @or("notAllowed", "uppyUpload.uploading", "uppyUpload.processing") disabled; - - uppyUpload = null; - - @on("init") - setupUppyUpload() { - // The uppyUpload configuration depends on arguments. In classic components like - // this one, the arguments are not available during field initialization, so we have to - // defer until init(). When this component is glimmer-ified in future, this can be turned - // into a simple field initializer. - this.uppyUpload = new UppyUpload(getOwner(this), { - id: this.id, - type: this.type, - additionalParams: this.additionalParams, - validateUploadedFilesOptions: { imagesOnly: true }, - uploadDropTargetOptions: () => ({ - target: document.querySelector(`#${this.id} .uploaded-image-preview`), - }), - uploadDone: (upload) => { - this.setProperties({ - imageFilesize: upload.human_filesize, - imageFilename: upload.original_filename, - imageWidth: upload.width, - imageHeight: upload.height, - }); - - // the value of the property used for imageUrl should be set - // in this callback. this should be done in cases where imageUrl - // is bound to a computed property of the parent component. - if (this.onUploadDone) { - this.onUploadDone(upload); - } else { - this.set("imageUrl", upload.url); - } - }, - }); - } - - @discourseComputed("id") - computedId(id) { - // without a fallback ID this will not be accessible - return id ? `${id}__input` : `${guidFor(this)}__input`; - } - - @discourseComputed("disabled", "notAllowed") - disabledReason(disabled, notAllowed) { - if (disabled && notAllowed) { - return i18n("post.errors.no_uploads_authorized"); - } - } - - @discourseComputed( - "currentUser.staff", - "siteSettings.{authorized_extensions,authorized_extensions_for_staff}" - ) - notAllowed() { - return !authorizesOneOrMoreExtensions( - this.currentUser?.staff, - this.siteSettings - ); - } - - @discourseComputed("imageUrl", "placeholderUrl") - showingPlaceholder(imageUrl, placeholderUrl) { - return !imageUrl && placeholderUrl; - } - - @discourseComputed("placeholderUrl") - placeholderStyle(url) { - if (isEmpty(url)) { - return htmlSafe(""); - } - return htmlSafe(`background-image: url(${url})`); - } - - @discourseComputed("imageUrl") - imageCDNURL(url) { - if (isEmpty(url)) { - return htmlSafe(""); - } - - return getURLWithCDN(url); - } - - @discourseComputed("imageCDNURL") - backgroundStyle(url) { - return htmlSafe(`background-image: url(${url})`); - } - - @discourseComputed("imageUrl") - imageBaseName(imageUrl) { - if (isEmpty(imageUrl)) { - return; - } - return imageUrl.split("/").slice(-1)[0]; - } - - @on("didRender") - _applyLightbox() { - next(() => lightbox(this.element, this.siteSettings)); - } - - @on("willDestroyElement") - _closeOnRemoval() { - if ($.magnificPopup?.instance) { - $.magnificPopup.instance.close(); - } - } - - @action - toggleLightbox() { - $(this.element.querySelector("a.lightbox"))?.magnificPopup("open"); - } - - @action - trash() { - // the value of the property used for imageUrl should be cleared - // in this callback. this should be done in cases where imageUrl - // is bound to a computed property of the parent component. - if (this.onUploadDeleted) { - this.onUploadDeleted(); - } else { - this.setProperties({ imageUrl: null }); - } - } - - @action - handleKeyboardActivation(event) { - if (event.key === "Enter" || event.key === " ") { - event.preventDefault(); // avoid space scrolling the page - const input = document.getElementById(this.computedId); - if (input && !this.disabled) { - input.click(); - } - } - } -} diff --git a/app/assets/javascripts/discourse/app/form-kit/components/fk/control/image.gjs b/app/assets/javascripts/discourse/app/form-kit/components/fk/control/image.gjs index fc7662ff277..751e6ffdbf5 100644 --- a/app/assets/javascripts/discourse/app/form-kit/components/fk/control/image.gjs +++ b/app/assets/javascripts/discourse/app/form-kit/components/fk/control/image.gjs @@ -1,5 +1,4 @@ import Component from "@glimmer/component"; -import { concat } from "@ember/helper"; import { action } from "@ember/object"; import { isBlank } from "@ember/utils"; import UppyImageUploader from "discourse/components/uppy-image-uploader"; @@ -23,7 +22,7 @@ export default class FKControlImage extends Component {