mirror of https://github.com/rails/rails
Dispatch direct-upload events on attachment uploads
When using Action Text's rich textarea, it's possible to attach files to the editor. Previously, that action didn't dispatch any events, which made it hard to react to the file uploads. For instance, if an upload failed, there was no way to notify the user about it, or remove the attachment from the editor. This commits adds new events - `direct-upload:start`, `direct-upload:progress`, and `direct-upload:end` - similar to how Active Storage's direct uploads work. Closes #37793 Supersedes #37794 Co-authored-by: Brad Rees <github@bradleyrees.com>
This commit is contained in:
parent
10924d3c96
commit
8f0b9117b7
|
@ -1,3 +1,15 @@
|
|||
* Dispatch direct-upload events on attachment uploads
|
||||
|
||||
When using Action Text's rich textarea, it's possible to attach files to the
|
||||
editor. Previously, that action didn't dispatch any events, which made it hard
|
||||
to react to the file uploads. For instance, if an upload failed, there was no
|
||||
way to notify the user about it, or remove the attachment from the editor.
|
||||
|
||||
This commits adds new events - `direct-upload:start`, `direct-upload:progress`,
|
||||
and `direct-upload:end` - similar to how Active Storage's direct uploads work.
|
||||
|
||||
*Matheus Richard*, *Brad Rees*
|
||||
|
||||
* Add `store_if_blank` option to `has_rich_text`
|
||||
|
||||
Pass `store_if_blank: false` to not create `ActionText::RichText` records when saving with a blank attribute, such as from an optional form parameter.
|
||||
|
|
|
@ -853,25 +853,47 @@ class AttachmentUpload {
|
|||
}
|
||||
start() {
|
||||
this.directUpload.create(this.directUploadDidComplete.bind(this));
|
||||
this.dispatch("start");
|
||||
}
|
||||
directUploadWillStoreFileWithXHR(xhr) {
|
||||
xhr.upload.addEventListener("progress", (event => {
|
||||
const progress = event.loaded / event.total * 100;
|
||||
this.attachment.setUploadProgress(progress);
|
||||
if (progress) {
|
||||
this.dispatch("progress", {
|
||||
progress: progress
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
directUploadDidComplete(error, attributes) {
|
||||
if (error) {
|
||||
throw new Error(`Direct upload failed: ${error}`);
|
||||
this.dispatchError(error);
|
||||
} else {
|
||||
this.attachment.setAttributes({
|
||||
sgid: attributes.attachable_sgid,
|
||||
url: this.createBlobUrl(attributes.signed_id, attributes.filename)
|
||||
});
|
||||
this.dispatch("end");
|
||||
}
|
||||
this.attachment.setAttributes({
|
||||
sgid: attributes.attachable_sgid,
|
||||
url: this.createBlobUrl(attributes.signed_id, attributes.filename)
|
||||
});
|
||||
}
|
||||
createBlobUrl(signedId, filename) {
|
||||
return this.blobUrlTemplate.replace(":signed_id", signedId).replace(":filename", encodeURIComponent(filename));
|
||||
}
|
||||
dispatch(name, detail = {}) {
|
||||
detail.attachment = this.attachment;
|
||||
return dispatchEvent(this.element, `direct-upload:${name}`, {
|
||||
detail: detail
|
||||
});
|
||||
}
|
||||
dispatchError(error) {
|
||||
const event = this.dispatch("error", {
|
||||
error: error
|
||||
});
|
||||
if (!event.defaultPrevented) {
|
||||
alert(error);
|
||||
}
|
||||
}
|
||||
get directUploadUrl() {
|
||||
return this.element.dataset.directUploadUrl;
|
||||
}
|
||||
|
|
|
@ -826,25 +826,47 @@
|
|||
}
|
||||
start() {
|
||||
this.directUpload.create(this.directUploadDidComplete.bind(this));
|
||||
this.dispatch("start");
|
||||
}
|
||||
directUploadWillStoreFileWithXHR(xhr) {
|
||||
xhr.upload.addEventListener("progress", (event => {
|
||||
const progress = event.loaded / event.total * 100;
|
||||
this.attachment.setUploadProgress(progress);
|
||||
if (progress) {
|
||||
this.dispatch("progress", {
|
||||
progress: progress
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
directUploadDidComplete(error, attributes) {
|
||||
if (error) {
|
||||
throw new Error(`Direct upload failed: ${error}`);
|
||||
this.dispatchError(error);
|
||||
} else {
|
||||
this.attachment.setAttributes({
|
||||
sgid: attributes.attachable_sgid,
|
||||
url: this.createBlobUrl(attributes.signed_id, attributes.filename)
|
||||
});
|
||||
this.dispatch("end");
|
||||
}
|
||||
this.attachment.setAttributes({
|
||||
sgid: attributes.attachable_sgid,
|
||||
url: this.createBlobUrl(attributes.signed_id, attributes.filename)
|
||||
});
|
||||
}
|
||||
createBlobUrl(signedId, filename) {
|
||||
return this.blobUrlTemplate.replace(":signed_id", signedId).replace(":filename", encodeURIComponent(filename));
|
||||
}
|
||||
dispatch(name, detail = {}) {
|
||||
detail.attachment = this.attachment;
|
||||
return dispatchEvent(this.element, `direct-upload:${name}`, {
|
||||
detail: detail
|
||||
});
|
||||
}
|
||||
dispatchError(error) {
|
||||
const event = this.dispatch("error", {
|
||||
error: error
|
||||
});
|
||||
if (!event.defaultPrevented) {
|
||||
alert(error);
|
||||
}
|
||||
}
|
||||
get directUploadUrl() {
|
||||
return this.element.dataset.directUploadUrl;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { DirectUpload } from "@rails/activestorage"
|
||||
import { DirectUpload, dispatchEvent } from "@rails/activestorage"
|
||||
|
||||
export class AttachmentUpload {
|
||||
constructor(attachment, element) {
|
||||
|
@ -9,24 +9,29 @@ export class AttachmentUpload {
|
|||
|
||||
start() {
|
||||
this.directUpload.create(this.directUploadDidComplete.bind(this))
|
||||
this.dispatch("start")
|
||||
}
|
||||
|
||||
directUploadWillStoreFileWithXHR(xhr) {
|
||||
xhr.upload.addEventListener("progress", event => {
|
||||
const progress = event.loaded / event.total * 100
|
||||
this.attachment.setUploadProgress(progress)
|
||||
if (progress) {
|
||||
this.dispatch("progress", { progress: progress })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
directUploadDidComplete(error, attributes) {
|
||||
if (error) {
|
||||
throw new Error(`Direct upload failed: ${error}`)
|
||||
this.dispatchError(error)
|
||||
} else {
|
||||
this.attachment.setAttributes({
|
||||
sgid: attributes.attachable_sgid,
|
||||
url: this.createBlobUrl(attributes.signed_id, attributes.filename)
|
||||
})
|
||||
this.dispatch("end")
|
||||
}
|
||||
|
||||
this.attachment.setAttributes({
|
||||
sgid: attributes.attachable_sgid,
|
||||
url: this.createBlobUrl(attributes.signed_id, attributes.filename)
|
||||
})
|
||||
}
|
||||
|
||||
createBlobUrl(signedId, filename) {
|
||||
|
@ -35,6 +40,18 @@ export class AttachmentUpload {
|
|||
.replace(":filename", encodeURIComponent(filename))
|
||||
}
|
||||
|
||||
dispatch(name, detail = {}) {
|
||||
detail.attachment = this.attachment
|
||||
return dispatchEvent(this.element, `direct-upload:${name}`, { detail })
|
||||
}
|
||||
|
||||
dispatchError(error) {
|
||||
const event = this.dispatch("error", { error })
|
||||
if (!event.defaultPrevented) {
|
||||
alert(error);
|
||||
}
|
||||
}
|
||||
|
||||
get directUploadUrl() {
|
||||
return this.element.dataset.directUploadUrl
|
||||
}
|
||||
|
|
|
@ -845,4 +845,4 @@ function autostart() {
|
|||
|
||||
setTimeout(autostart, 1);
|
||||
|
||||
export { DirectUpload, DirectUploadController, DirectUploadsController, start };
|
||||
export { DirectUpload, DirectUploadController, DirectUploadsController, dispatchEvent, start };
|
||||
|
|
|
@ -822,6 +822,7 @@
|
|||
exports.DirectUpload = DirectUpload;
|
||||
exports.DirectUploadController = DirectUploadController;
|
||||
exports.DirectUploadsController = DirectUploadsController;
|
||||
exports.dispatchEvent = dispatchEvent;
|
||||
exports.start = start;
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
|
|
|
@ -2,7 +2,8 @@ import { start } from "./ujs"
|
|||
import { DirectUpload } from "./direct_upload"
|
||||
import { DirectUploadController } from "./direct_upload_controller"
|
||||
import { DirectUploadsController } from "./direct_uploads_controller"
|
||||
export { start, DirectUpload, DirectUploadController, DirectUploadsController }
|
||||
import { dispatchEvent } from "./helpers"
|
||||
export { start, DirectUpload, DirectUploadController, DirectUploadsController, dispatchEvent }
|
||||
|
||||
function autostart() {
|
||||
if (window.ActiveStorage) {
|
||||
|
|
|
@ -233,7 +233,6 @@ To customize the HTML rendered for embedded images and other attachments (known
|
|||
as blobs), edit the `app/views/active_storage/blobs/_blob.html.erb` template
|
||||
created by the installer:
|
||||
|
||||
|
||||
```html+erb
|
||||
<%# app/views/active_storage/blobs/_blob.html.erb %>
|
||||
<figure class="attachment attachment--<%= blob.representable? ? "preview" : "file" %> attachment--<%= blob.filename.extension %>">
|
||||
|
@ -270,6 +269,14 @@ encounter when working with Action Text and Active Storage is that images do not
|
|||
render correctly in the editor. This is usually due to the `libvips` dependency
|
||||
not being installed.
|
||||
|
||||
#### Attachment Direct Upload JavaScript Events
|
||||
|
||||
| Event name | Event target | Event data (`event.detail`) | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `direct-upload:start` | `<input>` | `{id, file}` | A direct upload is starting. |
|
||||
| `direct-upload:progress` | `<input>` | `{id, file, progress}` | As requests to store files progress. |
|
||||
| `direct-upload:error` | `<input>` | `{id, file, error}` | An error occurred. An `alert` will display unless this event is canceled. |
|
||||
| `direct-upload:end` | `<input>` | `{id, file}` | A direct upload has ended. |
|
||||
|
||||
### Signed GlobalID
|
||||
|
||||
|
|
Loading…
Reference in New Issue