toggle-corp/react-store

View on GitHub
utils/upload/Uploader.js

Summary

Maintainability
A
0 mins
Test Coverage
const defaultProgressFn = () => { /* console.warn('No progress callback defined'); */ };
const defaultSuccessFn = () => { console.warn('No success callback defined'); };
const defaultFailureFn = () => { console.warn('No failure callback defined'); };
const defaultFatalFn = () => { console.warn('No fatal callback defined'); };
const defaultAbortFn = () => { console.warn('No abort callback defined'); };
const defaultPreLoadFn = () => { /* console.warn('No preload callback defined'); */ };
const defaultPostLoadFn = () => { /* console.warn('No postload callback defined'); */ };

export default class Uploader {
    constructor(
        file, url, params = {},
        progress = defaultProgressFn, success = defaultSuccessFn,
        failure = defaultFailureFn, fatal = defaultFatalFn,
        abort = defaultAbortFn, preLoad = defaultPreLoadFn, postLoad = defaultPostLoadFn,
    ) {
        this.file = file;
        this.uploadUrl = url;
        this.params = params;
        this.progress = progress;
        this.success = (...attrs) => { postLoad(); success(...attrs); };
        this.failure = (...attrs) => { postLoad(); failure(...attrs); };
        this.fatal = (...attrs) => { postLoad(); fatal(...attrs); };
        this.abort = (...attrs) => { postLoad(); abort(...attrs); };
        this.preLoad = preLoad;
        this.postLoad = postLoad;

        // If the uploader is working or not
        this.uploading = false;
        this.progressPercent = 0;

        this.xhr = this.createNativeUploader();
    }

    // PRIVATE
    createNativeUploader = () => {
        const xhr = new XMLHttpRequest();
        xhr.upload.onprogress = (e) => {
            if (e.lengthComputable) {
                const progressPercent = Math.round((e.loaded * 100) / e.total);
                this.progressPercent = progressPercent;
            }
            // callback
            this.progress(this.progressPercent, e);
        };

        xhr.onabort = () => {
            this.uploading = false;
            this.progressPercent = 0;

            // callback
            this.abort();
        };

        xhr.onerror = () => {
            this.uploading = false;
            this.progressPercent = 0;

            let response;
            try {
                response = JSON.parse(xhr.response);
            } catch (err) {
                // callback
                this.fatal({ errorMessage: 'Error while parsing json', errorCode: null }, xhr.status);
                return;
            }
            // callback
            this.fatal(response, xhr.status);
        };

        xhr.onload = () => {
            const okay = Math.floor(xhr.status / 100) === 2;

            this.uploading = false;
            this.progressPercent = okay ? 100 : 0;

            let response;
            try {
                response = JSON.parse(xhr.response);
            } catch (err) {
                // callback
                this.fatal({ errorMessage: 'Error while parsing json', errorCode: null }, xhr.status);
                return;
            }

            console.log(`Recieving ${this.uploadUrl}`, response);

            if (okay) {
                // callback
                this.success(response, xhr.status);
            } else {
                // callback
                this.failure(response, xhr.status);
            }
        };

        return xhr;
    }

    start = () => {
        if (this.uploading) {
            console.error('Uploader already started');
            return;
        }
        this.uploading = true;

        // callback
        this.preLoad();

        this.xhr.open('POST', this.uploadUrl);

        const formData = new FormData();
        formData.append('file', this.file);
        formData.append('title', this.file.name);

        const parameters = typeof this.params === 'function' ? this.params() : this.params;
        if (parameters.withCredentials) {
            this.xhr.withCredentials = true;
        }

        // DEBUG:
        console.log(`Fetching ${this.uploadUrl}`, parameters);

        const headerKeys = Object.keys(parameters.headers);
        headerKeys.forEach((key) => {
            this.xhr.setRequestHeader(key, parameters.headers[key]);
        });

        const bodyKeys = Object.keys(parameters.body);
        bodyKeys.forEach((key) => {
            formData.append(key, parameters.body[key]);
        });

        this.xhr.send(formData);
    }

    stop = () => {
        if (this.uploading) {
            this.xhr.abort();
        }
    }
}