Merge pull request #3423 from tchak/fix-upload-progress
Fix upload progress
This commit is contained in:
commit
6693c701ef
7 changed files with 171 additions and 67 deletions
|
@ -1,5 +1,5 @@
|
||||||
import { getJSON, debounce } from '@utils';
|
import { getJSON, debounce } from '@utils';
|
||||||
import { DirectUpload } from 'activestorage';
|
import Uploader from '../../shared/activestorage/uploader';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['state', 'index', 'item'],
|
props: ['state', 'index', 'item'],
|
||||||
|
@ -181,7 +181,12 @@ export default {
|
||||||
const file = input.files[0];
|
const file = input.files[0];
|
||||||
if (file) {
|
if (file) {
|
||||||
this.isUploading = true;
|
this.isUploading = true;
|
||||||
uploadFile(this.state.directUploadUrl, file).then(({ signed_id }) => {
|
const controller = new Uploader(
|
||||||
|
input,
|
||||||
|
file,
|
||||||
|
this.state.directUploadUrl
|
||||||
|
);
|
||||||
|
controller.start().then(signed_id => {
|
||||||
this.pieceJustificativeTemplate = signed_id;
|
this.pieceJustificativeTemplate = signed_id;
|
||||||
this.isUploading = false;
|
this.isUploading = false;
|
||||||
this.debouncedSave();
|
this.debouncedSave();
|
||||||
|
@ -247,17 +252,3 @@ const EXCLUDE_FROM_REPETITION = [
|
||||||
function castBoolean(value) {
|
function castBoolean(value) {
|
||||||
return value && value != 0;
|
return value && value != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function uploadFile(directUploadUrl, file) {
|
|
||||||
const upload = new DirectUpload(file, directUploadUrl);
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
upload.create((error, blob) => {
|
|
||||||
if (error) {
|
|
||||||
reject(error);
|
|
||||||
} else {
|
|
||||||
resolve(blob);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import Rails from 'rails-ujs';
|
||||||
import * as ActiveStorage from 'activestorage';
|
import * as ActiveStorage from 'activestorage';
|
||||||
import jQuery from 'jquery';
|
import jQuery from 'jquery';
|
||||||
|
|
||||||
import '../shared/activestorage/progress';
|
import '../shared/activestorage/ujs';
|
||||||
import '../shared/sentry';
|
import '../shared/sentry';
|
||||||
import '../shared/rails-ujs-fix';
|
import '../shared/rails-ujs-fix';
|
||||||
import '../shared/safari-11-file-xhr-workaround';
|
import '../shared/safari-11-file-xhr-workaround';
|
||||||
|
|
|
@ -5,7 +5,7 @@ import * as ActiveStorage from 'activestorage';
|
||||||
import Chartkick from 'chartkick';
|
import Chartkick from 'chartkick';
|
||||||
import Highcharts from 'highcharts';
|
import Highcharts from 'highcharts';
|
||||||
|
|
||||||
import '../shared/activestorage/progress';
|
import '../shared/activestorage/ujs';
|
||||||
import '../shared/sentry';
|
import '../shared/sentry';
|
||||||
import '../shared/rails-ujs-fix';
|
import '../shared/rails-ujs-fix';
|
||||||
import '../shared/safari-11-file-xhr-workaround';
|
import '../shared/safari-11-file-xhr-workaround';
|
||||||
|
|
92
app/javascript/shared/activestorage/progress-bar.js
Normal file
92
app/javascript/shared/activestorage/progress-bar.js
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
const PENDING_CLASS = 'direct-upload--pending';
|
||||||
|
const ERROR_CLASS = 'direct-upload--error';
|
||||||
|
const COMPLETE_CLASS = 'direct-upload--complete';
|
||||||
|
|
||||||
|
/**
|
||||||
|
ProgressBar is and utility class responsible for
|
||||||
|
rendering upload progress bar. It is used to handle
|
||||||
|
direct-upload form ujs events but also in the
|
||||||
|
Uploader delegate used with uploads on json api.
|
||||||
|
*/
|
||||||
|
export default class ProgressBar {
|
||||||
|
static init(input, id, file) {
|
||||||
|
clearErrors(input);
|
||||||
|
const html = this.render(id, file.name);
|
||||||
|
input.insertAdjacentHTML('beforebegin', html);
|
||||||
|
}
|
||||||
|
|
||||||
|
static start(id) {
|
||||||
|
const element = getDirectUploadElement(id);
|
||||||
|
|
||||||
|
element.classList.remove(PENDING_CLASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static progress(id, progress) {
|
||||||
|
const element = getDirectUploadProgressElement(id);
|
||||||
|
|
||||||
|
element.style.width = `${progress}%`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error(id, error) {
|
||||||
|
const element = getDirectUploadElement(id);
|
||||||
|
|
||||||
|
element.classList.add(ERROR_CLASS);
|
||||||
|
element.setAttribute('title', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static end(id) {
|
||||||
|
const element = getDirectUploadElement(id);
|
||||||
|
|
||||||
|
element.classList.add(COMPLETE_CLASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static render(id, filename) {
|
||||||
|
return `<div id="direct-upload-${id}" class="direct-upload ${PENDING_CLASS}">
|
||||||
|
<div class="direct-upload__progress" style="width: 0%"></div>
|
||||||
|
<span class="direct-upload__filename">${filename}</span>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(input, id, file) {
|
||||||
|
this.constructor.init(input, id, file);
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
this.constructor.start(this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
progress(progress) {
|
||||||
|
this.constructor.progress(this.id, progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
error(error) {
|
||||||
|
this.constructor.error(this.id, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
end() {
|
||||||
|
this.constructor.end(this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
const element = getDirectUploadElement(this.id);
|
||||||
|
element.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearErrors(input) {
|
||||||
|
const errorElements = input.parentElement.querySelectorAll(`.${ERROR_CLASS}`);
|
||||||
|
for (let element of errorElements) {
|
||||||
|
element.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDirectUploadElement(id) {
|
||||||
|
return document.getElementById(`direct-upload-${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDirectUploadProgressElement(id) {
|
||||||
|
return document.querySelector(
|
||||||
|
`#direct-upload-${id} .direct-upload__progress`
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,49 +0,0 @@
|
||||||
addEventListener('direct-upload:initialize', event => {
|
|
||||||
const target = event.target,
|
|
||||||
detail = event.detail,
|
|
||||||
id = detail.id,
|
|
||||||
file = detail.file;
|
|
||||||
|
|
||||||
target.insertAdjacentHTML(
|
|
||||||
'beforebegin',
|
|
||||||
'\n<div id="direct-upload-' +
|
|
||||||
id +
|
|
||||||
'" class="direct-upload direct-upload--pending">\n<div id="direct-upload-progress-' +
|
|
||||||
id +
|
|
||||||
'" class="direct-upload__progress" style="width: 0%"></div>\n<span class="direct-upload__filename">' +
|
|
||||||
file.name +
|
|
||||||
'</span>\n</div>\n'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
addEventListener('direct-upload:start', event => {
|
|
||||||
const id = event.detail.id,
|
|
||||||
element = document.getElementById('direct-upload-' + id);
|
|
||||||
|
|
||||||
element.classList.remove('direct-upload--pending');
|
|
||||||
});
|
|
||||||
|
|
||||||
addEventListener('direct-upload:progress', event => {
|
|
||||||
const id = event.detail.id,
|
|
||||||
progress = event.detail.progress,
|
|
||||||
progressElement = document.getElementById('direct-upload-progress-' + id);
|
|
||||||
|
|
||||||
progressElement.style.width = `${progress} %`;
|
|
||||||
});
|
|
||||||
|
|
||||||
addEventListener('direct-upload:error', event => {
|
|
||||||
event.preventDefault();
|
|
||||||
const id = event.detail.id,
|
|
||||||
error = event.detail.error,
|
|
||||||
element = document.getElementById('direct-upload-' + id);
|
|
||||||
|
|
||||||
element.classList.add('direct-upload--error');
|
|
||||||
element.setAttribute('title', error);
|
|
||||||
});
|
|
||||||
|
|
||||||
addEventListener('direct-upload:end', event => {
|
|
||||||
const id = event.detail.id,
|
|
||||||
element = document.getElementById('direct-upload-' + id);
|
|
||||||
|
|
||||||
element.classList.add('direct-upload--complete');
|
|
||||||
});
|
|
27
app/javascript/shared/activestorage/ujs.js
Normal file
27
app/javascript/shared/activestorage/ujs.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import ProgressBar from './progress-bar';
|
||||||
|
|
||||||
|
const INITIALIZE_EVENT = 'direct-upload:initialize';
|
||||||
|
const START_EVENT = 'direct-upload:start';
|
||||||
|
const PROGRESS_EVENT = 'direct-upload:progress';
|
||||||
|
const ERROR_EVENT = 'direct-upload:error';
|
||||||
|
const END_EVENT = 'direct-upload:end';
|
||||||
|
|
||||||
|
addEventListener(INITIALIZE_EVENT, ({ target, detail: { id, file } }) => {
|
||||||
|
ProgressBar.init(target, id, file);
|
||||||
|
});
|
||||||
|
|
||||||
|
addEventListener(START_EVENT, ({ detail: { id } }) => {
|
||||||
|
ProgressBar.start(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
addEventListener(PROGRESS_EVENT, ({ detail: { id, progress } }) => {
|
||||||
|
ProgressBar.progress(id, progress);
|
||||||
|
});
|
||||||
|
|
||||||
|
addEventListener(ERROR_EVENT, ({ detail: { id, error } }) => {
|
||||||
|
ProgressBar.error(id, error);
|
||||||
|
});
|
||||||
|
|
||||||
|
addEventListener(END_EVENT, ({ detail: { id } }) => {
|
||||||
|
ProgressBar.end(id);
|
||||||
|
});
|
43
app/javascript/shared/activestorage/uploader.js
Normal file
43
app/javascript/shared/activestorage/uploader.js
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import { DirectUpload } from 'activestorage';
|
||||||
|
import ProgressBar from './progress-bar';
|
||||||
|
|
||||||
|
/**
|
||||||
|
Uploader class is a delegate for DirectUpload instance
|
||||||
|
used to track lifecycle and progress of un upload.
|
||||||
|
*/
|
||||||
|
export default class Uploader {
|
||||||
|
constructor(input, file, directUploadUrl) {
|
||||||
|
this.directUpload = new DirectUpload(file, directUploadUrl, this);
|
||||||
|
this.progressBar = new ProgressBar(input, this.directUpload.id, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
this.progressBar.start();
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.directUpload.create((error, attributes) => {
|
||||||
|
if (error) {
|
||||||
|
this.progressBar.error(error);
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve(attributes.signed_id);
|
||||||
|
}
|
||||||
|
this.progressBar.end();
|
||||||
|
this.progressBar.destroy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadRequestDidProgress(event) {
|
||||||
|
const progress = (event.loaded / event.total) * 100;
|
||||||
|
if (progress) {
|
||||||
|
this.progressBar.progress(progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
directUploadWillStoreFileWithXHR(xhr) {
|
||||||
|
xhr.upload.addEventListener('progress', event =>
|
||||||
|
this.uploadRequestDidProgress(event)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue