diff --git a/app/assets/javascripts/discourse-common/package.json b/app/assets/javascripts/discourse-common/package.json index ce6bb534eea..9f39f2e1adc 100644 --- a/app/assets/javascripts/discourse-common/package.json +++ b/app/assets/javascripts/discourse-common/package.json @@ -19,7 +19,13 @@ "ember-cli-htmlbars": "^4.2.0", "ember-auto-import": "^1.5.3", "handlebars": "^4.7.0", - "truth-helpers": "^1.0.0" + "truth-helpers": "^1.0.0", + "@uppy/aws-s3": "^2.0.4", + "@uppy/aws-s3-multipart": "^2.1.0", + "@uppy/core": "^2.1.0", + "@uppy/drop-target": "^1.1.0", + "@uppy/utils": "^4.0.3", + "@uppy/xhr-upload": "^2.0.4" }, "devDependencies": { "@ember/optional-features": "^1.1.0", diff --git a/app/assets/javascripts/wizard-shims.js b/app/assets/javascripts/wizard-shims.js index d7bc1715a8d..fcfaa05c041 100644 --- a/app/assets/javascripts/wizard-shims.js +++ b/app/assets/javascripts/wizard-shims.js @@ -4,3 +4,40 @@ define("@popperjs/core", ["exports"], function (__exports__) { __exports__.defaultModifiers = window.Popper.defaultModifiers; __exports__.popperGenerator = window.Popper.popperGenerator; }); + +define("@uppy/core", ["exports"], function (__exports__) { + __exports__.default = window.Uppy.Core; + __exports__.BasePlugin = window.Uppy.Core.BasePlugin; +}); + +define("@uppy/aws-s3", ["exports"], function (__exports__) { + __exports__.default = window.Uppy.AwsS3; +}); + +define("@uppy/aws-s3-multipart", ["exports"], function (__exports__) { + __exports__.default = window.Uppy.AwsS3Multipart; +}); + +define("@uppy/xhr-upload", ["exports"], function (__exports__) { + __exports__.default = window.Uppy.XHRUpload; +}); + +define("@uppy/drop-target", ["exports"], function (__exports__) { + __exports__.default = window.Uppy.DropTarget; +}); + +define("@uppy/utils/lib/delay", ["exports"], function (__exports__) { + __exports__.default = window.Uppy.Utils.delay; +}); + +define("@uppy/utils/lib/EventTracker", ["exports"], function (__exports__) { + __exports__.default = window.Uppy.Utils.EventTracker; +}); + +define("@uppy/utils/lib/AbortController", ["exports"], function (__exports__) { + __exports__.AbortController = + window.Uppy.Utils.AbortControllerLib.AbortController; + __exports__.AbortSignal = window.Uppy.Utils.AbortControllerLib.AbortSignal; + __exports__.createAbortError = + window.Uppy.Utils.AbortControllerLib.createAbortError; +}); diff --git a/app/assets/javascripts/wizard-vendor.js b/app/assets/javascripts/wizard-vendor.js index 1c515750deb..1818614a197 100644 --- a/app/assets/javascripts/wizard-vendor.js +++ b/app/assets/javascripts/wizard-vendor.js @@ -2,6 +2,7 @@ //= require template_include.js //= require jquery.ui.widget.js //= require jquery.fileupload.js +//= require uppy.js //= require bootstrap-modal.js //= require bootbox.js //= require virtual-dom diff --git a/app/assets/javascripts/wizard/components/wizard-field-image.js b/app/assets/javascripts/wizard/components/wizard-field-image.js index 783e203e79f..c4ff8328b5c 100644 --- a/app/assets/javascripts/wizard/components/wizard-field-image.js +++ b/app/assets/javascripts/wizard/components/wizard-field-image.js @@ -1,10 +1,14 @@ import Component from "@ember/component"; +import { warn } from "@ember/debug"; import I18n from "I18n"; import { dasherize } from "@ember/string"; import discourseComputed from "discourse-common/utils/decorators"; import { getOwner } from "discourse-common/lib/get-owner"; import { getToken } from "wizard/lib/ajax"; import getUrl from "discourse-common/lib/get-url"; +import Uppy from "@uppy/core"; +import DropTarget from "@uppy/drop-target"; +import XHRUpload from "@uppy/xhr-upload"; export default Component.extend({ classNames: ["wizard-image-row"], @@ -19,37 +23,63 @@ export default Component.extend({ didInsertElement() { this._super(...arguments); + this.setupUploads(); + }, - const $upload = $(this.element); - + setupUploads() { const id = this.get("field.id"); - - $upload.fileupload({ - url: getUrl("/uploads.json"), - formData: { - synchronous: true, - type: `wizard_${id}`, - authenticity_token: getToken(), - }, - dataType: "json", - dropZone: $upload, + this._uppyInstance = new Uppy({ + id: `wizard-field-image-${id}`, + meta: { upload_type: `wizard_${id}` }, + autoProceed: true, }); - $upload.on("fileuploadsubmit", () => this.set("uploading", true)); + this._uppyInstance.use(XHRUpload, { + endpoint: getUrl("/uploads.json"), + headers: { + "X-CSRF-Token": getToken(), + }, + }); - $upload.on("fileuploaddone", (e, response) => { - this.set("field.value", response.result.url); + this._uppyInstance.use(DropTarget, { target: this.element }); + + this._uppyInstance.on("upload", () => { + this.set("uploading", true); + }); + + this._uppyInstance.on("upload-success", (file, response) => { + this.set("field.value", response.body.url); this.set("uploading", false); }); - $upload.on("fileuploadfail", (e, response) => { + this._uppyInstance.on("upload-error", (file, error, response) => { let message = I18n.t("wizard.upload_error"); - if (response.jqXHR.responseJSON && response.jqXHR.responseJSON.errors) { - message = response.jqXHR.responseJSON.errors.join("\n"); + if (response.body.errors) { + message = response.body.errors.join("\n"); } window.bootbox.alert(message); this.set("uploading", false); }); + + this.element + .querySelector(".wizard-hidden-upload-field") + .addEventListener("change", (event) => { + const files = Array.from(event.target.files); + files.forEach((file) => { + try { + this._uppyInstance.addFile({ + source: `${this.id} file input`, + name: file.name, + type: file.type, + data: file, + }); + } catch (err) { + warn(`error adding files to uppy: ${err}`, { + id: "discourse.upload.uppy-add-files-error", + }); + } + }); + }); }, });