mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-05-05 08:40:11 +00:00
Dropzone: Swapped fetch for XHR for progress tracking
This commit is contained in:
parent
23915c3b1a
commit
36116a45d4
2 changed files with 61 additions and 12 deletions
resources/js
|
@ -56,18 +56,17 @@ export class Dropzone extends Component {
|
||||||
* @return {Upload}
|
* @return {Upload}
|
||||||
*/
|
*/
|
||||||
createUploadFromFile(file) {
|
createUploadFromFile(file) {
|
||||||
const {dom, status} = this.createDomForFile(file);
|
const {dom, status, progress} = this.createDomForFile(file);
|
||||||
this.container.append(dom);
|
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 = {
|
const upload = {
|
||||||
file,
|
file,
|
||||||
dom,
|
dom,
|
||||||
|
updateProgress(percentComplete) {
|
||||||
|
console.log(`progress: ${percentComplete}%`);
|
||||||
|
progress.textContent = `${percentComplete}%`;
|
||||||
|
progress.style.width = `${percentComplete}%`;
|
||||||
|
},
|
||||||
markError(message) {
|
markError(message) {
|
||||||
status.setAttribute('data-status', 'error');
|
status.setAttribute('data-status', 'error');
|
||||||
status.textContent = message;
|
status.textContent = message;
|
||||||
|
@ -78,15 +77,43 @@ export class Dropzone extends Component {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
uploadPromise.then(returnData => {
|
this.startXhrForUpload(upload);
|
||||||
upload.markSuccess(returnData.statusText);
|
|
||||||
}).catch(err => {
|
|
||||||
upload.markError(err?.data?.message || err.message);
|
|
||||||
});
|
|
||||||
|
|
||||||
return 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
|
* @param {File} file
|
||||||
* @return {{image: Element, dom: Element, progress: Element, label: Element, status: Element}}
|
* @return {{image: Element, dom: Element, progress: Element, label: Element, status: Element}}
|
||||||
|
@ -121,6 +148,7 @@ export class Dropzone extends Component {
|
||||||
* @typedef Upload
|
* @typedef Upload
|
||||||
* @property {File} file
|
* @property {File} file
|
||||||
* @property {Element} dom
|
* @property {Element} dom
|
||||||
|
* @property {function(Number)} updateProgress
|
||||||
* @property {function(String)} markError
|
* @property {function(String)} markError
|
||||||
* @property {function(String)} markSuccess
|
* @property {function(String)} markSuccess
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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
|
* Create a new HTTP request, setting the required CSRF information
|
||||||
* to communicate with the back-end. Parses & formats the response.
|
* to communicate with the back-end. Parses & formats the response.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue