javascript: move auto-upload attachment to the Uploader class

Rationale:

- It makes more sense to handle the progress bar updates in a single class;
- This will allow us to unify the error handling.
This commit is contained in:
Pierre de La Morinerie 2020-04-15 14:20:31 +00:00
parent 256362efd3
commit d8f3b86b0e
2 changed files with 57 additions and 59 deletions

View file

@ -1,6 +1,5 @@
import Uploader from '../../shared/activestorage/uploader';
import ProgressBar from '../../shared/activestorage/progress-bar';
import { ajax, show, hide, toggle } from '@utils';
import { show, hide, toggle } from '@utils';
// Given a file input in a champ with a selected file, upload a file,
// then attach it to the dossier.
@ -11,27 +10,19 @@ export default class AutoUploadController {
constructor(input, file) {
this.input = input;
this.file = file;
this.uploader = new Uploader(
input,
file,
input.dataset.directUploadUrl,
input.dataset.autoAttachUrl
);
}
async start() {
try {
this._begin();
// Sanity checks
const autoAttachUrl = this.input.dataset.autoAttachUrl;
if (!autoAttachUrl) {
throw new Error('Lattribut "data-auto-attach-url" est manquant');
}
// Upload the file (using Direct Upload)
let blobSignedId = await this._upload();
// Attach the blob to the champ
// (The request responds with Javascript, which displays the attachment HTML fragment).
await this._attach(blobSignedId, autoAttachUrl);
// Everything good: clear the original file input value
this.input.value = null;
await this.uploader.start();
this._succeeded();
} catch (error) {
this._failed(error);
throw error;
@ -45,35 +36,8 @@ export default class AutoUploadController {
this._hideErrorMessage();
}
async _upload() {
const uploader = new Uploader(
this.input,
this.file,
this.input.dataset.directUploadUrl
);
return await uploader.start();
}
async _attach(blobSignedId, autoAttachUrl) {
// Now that the upload is done, display a new progress bar
// to show that the attachment request is still pending.
const progressBar = new ProgressBar(
this.input,
`${this.input.id}-progress-bar`,
this.file
);
progressBar.progress(100);
progressBar.end();
const attachmentRequest = {
url: autoAttachUrl,
type: 'PUT',
data: `blob_signed_id=${blobSignedId}`
};
await ajax(attachmentRequest);
// The progress bar has been destroyed by the attachment HTML fragment that replaced the input,
// so no further cleanup is needed.
_succeeded() {
this.input.value = null;
}
_failed(error) {
@ -81,12 +45,10 @@ export default class AutoUploadController {
return;
}
let progressBar = this.input.parentElement.querySelector('.direct-upload');
if (progressBar) {
progressBar.remove();
}
this.uploader.progressBar.destroy();
this._displayErrorMessage(error);
let message = this._messageFromError(error);
this._displayErrorMessage(message);
}
_done() {
@ -126,11 +88,10 @@ export default class AutoUploadController {
}
}
_displayErrorMessage(error) {
_displayErrorMessage(message) {
let errorNode = this.input.parentElement.querySelector('.attachment-error');
if (errorNode) {
show(errorNode);
let message = this._messageFromError(error);
errorNode.querySelector('.attachment-error-title').textContent =
message.title || '';
errorNode.querySelector('.attachment-error-description').textContent =

View file

@ -1,4 +1,5 @@
import { DirectUpload } from '@rails/activestorage';
import { ajax } from '@utils';
import ProgressBar from './progress-bar';
import errorFromDirectUploadMessage from './errors';
@ -7,29 +8,65 @@ import errorFromDirectUploadMessage from './errors';
used to track lifecycle and progress of an upload.
*/
export default class Uploader {
constructor(input, file, directUploadUrl) {
constructor(input, file, directUploadUrl, autoAttachUrl) {
this.directUpload = new DirectUpload(file, directUploadUrl, this);
this.progressBar = new ProgressBar(input, this.directUpload.id, file);
this.autoAttachUrl = autoAttachUrl;
}
start() {
/**
Upload (and optionally attach) the file.
Returns the blob signed id on success.
*/
async start() {
this.progressBar.start();
try {
let blobSignedId = await this._upload();
if (this.autoAttachUrl) {
await this._attach(blobSignedId);
}
this.progressBar.end();
this.progressBar.destroy();
return blobSignedId;
} catch (error) {
this.progressBar.error(error.message);
throw error;
}
}
/**
Upload the file using the DirectUpload instance, and return the blob signed_id.
*/
async _upload() {
return new Promise((resolve, reject) => {
this.directUpload.create((errorMsg, attributes) => {
if (errorMsg) {
this.progressBar.error(errorMsg);
let error = errorFromDirectUploadMessage(errorMsg);
reject(error);
} else {
resolve(attributes.signed_id);
}
this.progressBar.end();
this.progressBar.destroy();
});
});
}
/**
Attach the file by sending a POST request to the autoAttachUrl.
*/
async _attach(blobSignedId) {
const attachmentRequest = {
url: this.autoAttachUrl,
type: 'PUT',
data: `blob_signed_id=${blobSignedId}`
};
await ajax(attachmentRequest);
}
uploadRequestDidProgress(event) {
const progress = (event.loaded / event.total) * 100;
if (progress) {