Dropzone: Swapped fetch for XHR for progress tracking

This commit is contained in:
Dan Brown 2023-04-24 18:18:08 +01:00
parent 23915c3b1a
commit 36116a45d4
No known key found for this signature in database
GPG key ID: 46D9F943C24A2EF9
2 changed files with 61 additions and 12 deletions

View file

@ -56,18 +56,17 @@ export class Dropzone extends Component {
* @return {Upload}
*/
createUploadFromFile(file) {
const {dom, status} = this.createDomForFile(file);
const {dom, status, progress} = this.createDomForFile(file);
this.container.append(dom);
const formData = new FormData();
formData.append('file', file, file.name);
// TODO - Change to XMLHTTPRequest so we can track progress.
const uploadPromise = window.$http.post(this.url, formData);
const upload = {
file,
dom,
updateProgress(percentComplete) {
console.log(`progress: ${percentComplete}%`);
progress.textContent = `${percentComplete}%`;
progress.style.width = `${percentComplete}%`;
},
markError(message) {
status.setAttribute('data-status', 'error');
status.textContent = message;
@ -78,15 +77,43 @@ export class Dropzone extends Component {
},
};
uploadPromise.then(returnData => {
upload.markSuccess(returnData.statusText);
}).catch(err => {
upload.markError(err?.data?.message || err.message);
});
this.startXhrForUpload(upload);
return upload;
}
/**
* @param {Upload} upload
*/
startXhrForUpload(upload) {
const formData = new FormData();
formData.append('file', upload.file, upload.file.name);
const req = window.$http.createXMLHttpRequest('POST', this.url, {
error() {
upload.markError('Upload failed'); // TODO - Update text
},
readystatechange() {
if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
upload.markSuccess('Finished upload!');
} else if (this.readyState === XMLHttpRequest.DONE && this.status >= 400) {
const content = this.responseText;
const data = content.startsWith('{') ? JSON.parse(content) : {message: content};
const message = data?.message || content;
upload.markError(message);
}
},
});
req.upload.addEventListener('progress', evt => {
const percent = Math.min(Math.ceil((evt.loaded / evt.total) * 100), 100);
upload.updateProgress(percent);
});
req.setRequestHeader('Accept', 'application/json');
req.send(formData);
}
/**
* @param {File} file
* @return {{image: Element, dom: Element, progress: Element, label: Element, status: Element}}
@ -121,6 +148,7 @@ export class Dropzone extends Component {
* @typedef Upload
* @property {File} file
* @property {Element} dom
* @property {function(Number)} updateProgress
* @property {function(String)} markError
* @property {function(String)} markSuccess
*/

View file

@ -45,6 +45,27 @@ export class HttpError extends Error {
}
/**
* @param {String} method
* @param {String} url
* @param {Object} events
* @return {XMLHttpRequest}
*/
export function createXMLHttpRequest(method, url, events = {}) {
const csrfToken = document.querySelector('meta[name=token]').getAttribute('content');
const req = new XMLHttpRequest();
for (const [eventName, callback] of Object.entries(events)) {
req.addEventListener(eventName, callback.bind(req));
}
req.open(method, url);
req.withCredentials = true;
req.setRequestHeader('X-CSRF-TOKEN', csrfToken);
return req;
}
/**
* Create a new HTTP request, setting the required CSRF information
* to communicate with the back-end. Parses & formats the response.