uppy
Version:
Almost as cute as a Puppy :dog:
1,041 lines (842 loc) • 30.5 kB
JavaScript
'use strict';
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var Utils = require('../core/Utils');
var Translator = require('../core/Translator');
var UppySocket = require('./UppySocket');
var ee = require('namespace-emitter');
var cuid = require('cuid');
var throttle = require('lodash.throttle');
var prettyBytes = require('prettier-bytes');
var match = require('mime-match'
// const deepFreeze = require('deep-freeze-strict')
/**
* Main Uppy core
*
* @param {object} opts general options, like locales, to show modal or not to show
*/
);
var Uppy = function () {
function Uppy(opts) {
_classCallCheck(this, Uppy);
var defaultLocale = {
strings: {
youCanOnlyUploadX: {
0: 'You can only upload %{smart_count} file',
1: 'You can only upload %{smart_count} files'
},
youHaveToAtLeastSelectX: {
0: 'You have to select at least %{smart_count} file',
1: 'You have to select at least %{smart_count} files'
},
exceedsSize: 'This file exceeds maximum allowed size of',
youCanOnlyUploadFileTypes: 'You can only upload:',
uppyServerError: 'Connection with Uppy Server failed'
}
// set default options
};var defaultOptions = {
id: 'uppy',
autoProceed: true,
debug: false,
restrictions: {
maxFileSize: false,
maxNumberOfFiles: false,
minNumberOfFiles: false,
allowedFileTypes: false
},
meta: {},
onBeforeFileAdded: function onBeforeFileAdded(currentFile, files) {
return Promise.resolve();
},
onBeforeUpload: function onBeforeUpload(files, done) {
return Promise.resolve();
},
locale: defaultLocale
// Merge default options with the ones set by user
};this.opts = _extends({}, defaultOptions, opts
// // Dictates in what order different plugin types are ran:
// this.types = [ 'presetter', 'orchestrator', 'progressindicator',
// 'acquirer', 'modifier', 'uploader', 'presenter', 'debugger']
);this.locale = _extends({}, defaultLocale, this.opts.locale);
this.locale.strings = _extends({}, defaultLocale.strings, this.opts.locale.strings
// i18n
);this.translator = new Translator({ locale: this.locale });
this.i18n = this.translator.translate.bind(this.translator
// Container for different types of plugins
);this.plugins = {};
// @TODO maybe bindall
this.translator = new Translator({ locale: this.opts.locale });
this.i18n = this.translator.translate.bind(this.translator);
this.getState = this.getState.bind(this);
this.updateMeta = this.updateMeta.bind(this);
this.initSocket = this.initSocket.bind(this);
this.log = this.log.bind(this);
this.info = this.info.bind(this);
this.hideInfo = this.hideInfo.bind(this);
this.addFile = this.addFile.bind(this);
this.removeFile = this.removeFile.bind(this);
this.calculateProgress = this.calculateProgress.bind(this);
this.resetProgress = this.resetProgress.bind(this
// this.bus = this.emitter = ee()
);this.emitter = ee();
this.on = this.emitter.on.bind(this.emitter);
this.off = this.emitter.off.bind(this.emitter);
this.once = this.emitter.once.bind(this.emitter);
this.emit = this.emitter.emit.bind(this.emitter);
this.preProcessors = [];
this.uploaders = [];
this.postProcessors = [];
this.state = {
files: {},
capabilities: {
resumableUploads: false
},
totalProgress: 0,
meta: _extends({}, this.opts.meta),
info: {
isHidden: true,
type: 'info',
message: ''
}
// for debugging and testing
};this.updateNum = 0;
if (this.opts.debug) {
global.UppyState = this.state;
global.uppyLog = '';
// global.UppyAddFile = this.addFile.bind(this)
global._uppy = this;
}
}
/**
* Iterate on all plugins and run `update` on them. Called each time state changes
*
*/
Uppy.prototype.updateAll = function updateAll(state) {
this.iteratePlugins(function (plugin) {
plugin.update(state);
});
};
/**
* Updates state
*
* @param {patch} object
*/
Uppy.prototype.setState = function setState(patch) {
var prevState = _extends({}, this.state);
var nextState = _extends({}, this.state, patch);
this.state = nextState;
this.emit('core:state-update', prevState, nextState, patch);
this.updateAll(this.state);
};
/**
* Returns current state
*
*/
Uppy.prototype.getState = function getState() {
// use deepFreeze for debugging
// return deepFreeze(this.state)
return this.state;
};
Uppy.prototype.reset = function reset() {
this.emit('core:pause-all');
this.emit('core:cancel-all');
this.setState({
totalProgress: 0
});
};
Uppy.prototype.resetProgress = function resetProgress() {
var defaultProgress = {
percentage: 0,
bytesUploaded: 0,
uploadComplete: false,
uploadStarted: false
};
var files = _extends({}, this.state.files);
var updatedFiles = {};
Object.keys(files).forEach(function (fileID) {
var updatedFile = _extends({}, files[fileID]);
updatedFile.progress = _extends({}, updatedFile.progress, defaultProgress);
updatedFiles[fileID] = updatedFile;
});
this.setState({
files: updatedFiles,
totalProgress: 0
}
// TODO Document on the website
);this.emit('core:reset-progress');
};
Uppy.prototype.addPreProcessor = function addPreProcessor(fn) {
this.preProcessors.push(fn);
};
Uppy.prototype.removePreProcessor = function removePreProcessor(fn) {
var i = this.preProcessors.indexOf(fn);
if (i !== -1) {
this.preProcessors.splice(i, 1);
}
};
Uppy.prototype.addPostProcessor = function addPostProcessor(fn) {
this.postProcessors.push(fn);
};
Uppy.prototype.removePostProcessor = function removePostProcessor(fn) {
var i = this.postProcessors.indexOf(fn);
if (i !== -1) {
this.postProcessors.splice(i, 1);
}
};
Uppy.prototype.addUploader = function addUploader(fn) {
this.uploaders.push(fn);
};
Uppy.prototype.removeUploader = function removeUploader(fn) {
var i = this.uploaders.indexOf(fn);
if (i !== -1) {
this.uploaders.splice(i, 1);
}
};
Uppy.prototype.setMeta = function setMeta(data) {
var newMeta = _extends({}, this.getState().meta, data);
this.log('Adding metadata:');
this.log(data);
this.setState({ meta: newMeta });
};
Uppy.prototype.updateMeta = function updateMeta(data, fileID) {
var updatedFiles = _extends({}, this.getState().files);
var newMeta = _extends({}, updatedFiles[fileID].meta, data);
updatedFiles[fileID] = _extends({}, updatedFiles[fileID], {
meta: newMeta
});
this.setState({ files: updatedFiles });
};
/**
* Check if minNumberOfFiles restriction is reached before uploading
*
* @return {boolean}
* @private
*/
Uppy.prototype.checkMinNumberOfFiles = function checkMinNumberOfFiles() {
var minNumberOfFiles = this.opts.restrictions.minNumberOfFiles;
if (Object.keys(this.state.files).length < minNumberOfFiles) {
this.info('' + this.i18n('youHaveToAtLeastSelectX', { smart_count: minNumberOfFiles }), 'error', 5000);
return false;
}
return true;
};
/**
* Check if file passes a set of restrictions set in options: maxFileSize,
* maxNumberOfFiles and allowedFileTypes
*
* @param {object} file object to check
* @return {boolean}
* @private
*/
Uppy.prototype.checkRestrictions = function checkRestrictions(file) {
var _opts$restrictions = this.opts.restrictions,
maxFileSize = _opts$restrictions.maxFileSize,
maxNumberOfFiles = _opts$restrictions.maxNumberOfFiles,
allowedFileTypes = _opts$restrictions.allowedFileTypes;
if (maxNumberOfFiles) {
if (Object.keys(this.state.files).length + 1 > maxNumberOfFiles) {
this.info('' + this.i18n('youCanOnlyUploadX', { smart_count: maxNumberOfFiles }), 'error', 5000);
return false;
}
}
if (allowedFileTypes) {
var isCorrectFileType = allowedFileTypes.filter(match(file.type.mime)).length > 0;
if (!isCorrectFileType) {
var allowedFileTypesString = allowedFileTypes.join(', ');
this.info(this.i18n('youCanOnlyUploadFileTypes') + ' ' + allowedFileTypesString, 'error', 5000);
return false;
}
}
if (maxFileSize) {
if (file.data.size > maxFileSize) {
this.info(this.i18n('exceedsSize') + ' ' + prettyBytes(maxFileSize), 'error', 5000);
return false;
}
}
return true;
};
/**
* Add a new file to `state.files`. This will run `onBeforeFileAdded`,
* try to guess file type in a clever way, check file against restrictions,
* and start an upload if `autoProceed === true`.
*
* @param {object} file object to add
*/
Uppy.prototype.addFile = function addFile(file) {
var _this = this;
// Wrap this in a Promise `.then()` handler so errors will reject the Promise
// instead of throwing.
var beforeFileAdded = Promise.resolve().then(function () {
return _this.opts.onBeforeFileAdded(file, _this.getState().files);
});
return beforeFileAdded.catch(function (err) {
_this.info(err, 'error', 5000);
return Promise.reject('onBeforeFileAdded: ' + err);
}).then(function () {
return Utils.getFileType(file).then(function (fileType) {
var updatedFiles = _extends({}, _this.state.files);
var fileName = file.name || 'noname';
var fileExtension = Utils.getFileNameAndExtension(fileName)[1];
var isRemote = file.isRemote || false;
var fileID = Utils.generateFileID(file);
var fileTypeGeneral = fileType[0];
var fileTypeSpecific = fileType[1];
var newFile = {
source: file.source || '',
id: fileID,
name: fileName,
extension: fileExtension || '',
meta: _extends({}, { name: fileName }, _this.getState().meta),
type: {
general: fileTypeGeneral,
specific: fileTypeSpecific,
mime: fileType.join('/')
},
data: file.data,
progress: {
percentage: 0,
bytesUploaded: 0,
bytesTotal: file.data.size || 0,
uploadComplete: false,
uploadStarted: false
},
size: file.data.size || 'N/A',
isRemote: isRemote,
remote: file.remote || '',
preview: file.preview
};
var isFileAllowed = _this.checkRestrictions(newFile);
if (!isFileAllowed) return Promise.reject('File not allowed');
updatedFiles[fileID] = newFile;
_this.setState({ files: updatedFiles });
_this.emit('core:file-added', newFile);
_this.log('Added file: ' + fileName + ', ' + fileID + ', mime type: ' + fileType);
if (_this.opts.autoProceed && !_this.scheduledAutoProceed) {
_this.scheduledAutoProceed = setTimeout(function () {
_this.scheduledAutoProceed = null;
_this.upload().catch(function (err) {
console.error(err.stack || err.message || err);
});
}, 4);
}
});
});
};
/**
* Get a file object.
*
* @param {string} fileID The ID of the file object to return.
*/
Uppy.prototype.getFile = function getFile(fileID) {
return this.getState().files[fileID];
};
/**
* Generate a preview image for the given file, if possible.
*/
Uppy.prototype.generatePreview = function generatePreview(file) {
var _this2 = this;
if (Utils.isPreviewSupported(file.type.specific) && !file.isRemote) {
Utils.createThumbnail(file, 200).then(function (thumbnail) {
_this2.setPreviewURL(file.id, thumbnail);
}).catch(function (err) {
console.warn(err.stack || err.message);
});
}
};
/**
* Set the preview URL for a file.
*/
Uppy.prototype.setPreviewURL = function setPreviewURL(fileID, preview) {
var _extends2;
var files = this.state.files;
this.setState({
files: _extends({}, files, (_extends2 = {}, _extends2[fileID] = _extends({}, files[fileID], {
preview: preview
}), _extends2))
});
};
Uppy.prototype.removeFile = function removeFile(fileID) {
var updatedFiles = _extends({}, this.getState().files);
var removedFile = updatedFiles[fileID];
delete updatedFiles[fileID];
this.setState({ files: updatedFiles });
this.calculateTotalProgress();
this.emit('core:file-removed', fileID
// Clean up object URLs.
);if (removedFile.preview && Utils.isObjectURL(removedFile.preview)) {
URL.revokeObjectURL(removedFile.preview);
}
this.log('Removed file: ' + fileID);
};
Uppy.prototype.calculateProgress = function calculateProgress(data) {
var fileID = data.id;
var updatedFiles = _extends({}, this.getState().files
// skip progress event for a file that’s been removed
);if (!updatedFiles[fileID]) {
this.log('Trying to set progress for a file that’s not with us anymore: ', fileID);
return;
}
var updatedFile = _extends({}, updatedFiles[fileID], _extends({}, {
progress: _extends({}, updatedFiles[fileID].progress, {
bytesUploaded: data.bytesUploaded,
bytesTotal: data.bytesTotal,
percentage: Math.floor((data.bytesUploaded / data.bytesTotal * 100).toFixed(2))
})
}));
updatedFiles[data.id] = updatedFile;
this.setState({
files: updatedFiles
});
this.calculateTotalProgress();
};
Uppy.prototype.calculateTotalProgress = function calculateTotalProgress() {
// calculate total progress, using the number of files currently uploading,
// multiplied by 100 and the summ of individual progress of each file
var files = _extends({}, this.getState().files);
var inProgress = Object.keys(files).filter(function (file) {
return files[file].progress.uploadStarted;
});
var progressMax = inProgress.length * 100;
var progressAll = 0;
inProgress.forEach(function (file) {
progressAll = progressAll + files[file].progress.percentage;
});
var totalProgress = progressMax === 0 ? 0 : Math.floor((progressAll * 100 / progressMax).toFixed(2));
this.setState({
totalProgress: totalProgress
});
};
/**
* Registers listeners for all global actions, like:
* `file-add`, `file-remove`, `upload-progress`, `reset`
*
*/
Uppy.prototype.actions = function actions() {
var _this3 = this;
// this.bus.on('*', (payload) => {
// console.log('emitted: ', this.event)
// console.log('with payload: ', payload)
// })
// stress-test re-rendering
// setInterval(() => {
// this.setState({bla: 'bla'})
// }, 20)
this.on('core:error', function (error) {
_this3.setState({ error: error });
});
this.on('core:upload-error', function (fileID, error) {
var fileName = _this3.state.files[fileID].name;
var message = 'Failed to upload ' + fileName;
if ((typeof error === 'undefined' ? 'undefined' : _typeof(error)) === 'object' && error.message) {
message = message + ': ' + error.message;
}
_this3.info(message, 'error', 5000);
});
this.on('core:upload', function () {
_this3.setState({ error: null });
});
this.on('core:file-add', function (data) {
_this3.addFile(data);
});
this.on('core:file-added', function (file) {
_this3.generatePreview(file);
}
// `remove-file` removes a file from `state.files`, for example when
// a user decides not to upload particular file and clicks a button to remove it
);this.on('core:file-remove', function (fileID) {
_this3.removeFile(fileID);
});
this.on('core:cancel-all', function () {
// let updatedFiles = this.getState().files
// updatedFiles = {}
_this3.setState({ files: {} });
});
this.on('core:upload-started', function (fileID, upload) {
var updatedFiles = _extends({}, _this3.getState().files);
var updatedFile = _extends({}, updatedFiles[fileID], _extends({}, {
progress: _extends({}, updatedFiles[fileID].progress, {
uploadStarted: Date.now()
})
}));
updatedFiles[fileID] = updatedFile;
_this3.setState({ files: updatedFiles });
}
// upload progress events can occur frequently, especially when you have a good
// connection to the remote server. Therefore, we are throtteling them to
// prevent accessive function calls.
// see also: https://github.com/tus/tus-js-client/commit/9940f27b2361fd7e10ba58b09b60d82422183bbb
);var throttledCalculateProgress = throttle(this.calculateProgress, 100, { leading: true, trailing: false });
this.on('core:upload-progress', function (data) {
// this.calculateProgress(data)
throttledCalculateProgress(data);
});
this.on('core:upload-success', function (fileID, uploadResp, uploadURL) {
var updatedFiles = _extends({}, _this3.getState().files);
var updatedFile = _extends({}, updatedFiles[fileID], {
progress: _extends({}, updatedFiles[fileID].progress, {
uploadComplete: true,
// good or bad idea? setting the percentage to 100 if upload is successful,
// so that if we lost some progress events on the way, its still marked “compete”?
percentage: 100
}),
uploadURL: uploadURL
});
updatedFiles[fileID] = updatedFile;
_this3.setState({
files: updatedFiles
});
_this3.calculateTotalProgress();
});
this.on('core:update-meta', function (data, fileID) {
_this3.updateMeta(data, fileID);
});
this.on('core:preprocess-progress', function (fileID, progress) {
var files = _extends({}, _this3.getState().files);
files[fileID] = _extends({}, files[fileID], {
progress: _extends({}, files[fileID].progress, {
preprocess: progress
})
});
_this3.setState({ files: files });
});
this.on('core:preprocess-complete', function (fileID) {
var files = _extends({}, _this3.getState().files);
files[fileID] = _extends({}, files[fileID], {
progress: _extends({}, files[fileID].progress)
});
delete files[fileID].progress.preprocess;
_this3.setState({ files: files });
});
this.on('core:postprocess-progress', function (fileID, progress) {
var files = _extends({}, _this3.getState().files);
files[fileID] = _extends({}, files[fileID], {
progress: _extends({}, files[fileID].progress, {
postprocess: progress
})
});
_this3.setState({ files: files });
});
this.on('core:postprocess-complete', function (fileID) {
var files = _extends({}, _this3.getState().files);
files[fileID] = _extends({}, files[fileID], {
progress: _extends({}, files[fileID].progress)
});
delete files[fileID].progress.postprocess;
// TODO should we set some kind of `fullyComplete` property on the file object
// so it's easier to see that the file is upload…fully complete…rather than
// what we have to do now (`uploadComplete && !postprocess`)
_this3.setState({ files: files });
}
// show informer if offline
);if (typeof window !== 'undefined') {
window.addEventListener('online', function () {
return _this3.updateOnlineStatus();
});
window.addEventListener('offline', function () {
return _this3.updateOnlineStatus();
});
setTimeout(function () {
return _this3.updateOnlineStatus();
}, 3000);
}
};
Uppy.prototype.updateOnlineStatus = function updateOnlineStatus() {
var online = typeof window.navigator.onLine !== 'undefined' ? window.navigator.onLine : true;
if (!online) {
this.emit('is-offline');
this.info('No internet connection', 'error', 0);
this.wasOffline = true;
} else {
this.emit('is-online');
if (this.wasOffline) {
this.emit('back-online');
this.info('Connected!', 'success', 3000);
this.wasOffline = false;
}
}
};
Uppy.prototype.getID = function getID() {
return this.opts.id;
};
/**
* Registers a plugin with Core
*
* @param {Class} Plugin object
* @param {Object} options object that will be passed to Plugin later
* @return {Object} self for chaining
*/
Uppy.prototype.use = function use(Plugin, opts) {
if (typeof Plugin !== 'function') {
var msg = 'Expected a plugin class, but got ' + (Plugin === null ? 'null' : typeof Plugin === 'undefined' ? 'undefined' : _typeof(Plugin)) + '.' + ' Please verify that the plugin was imported and spelled correctly.';
throw new TypeError(msg);
}
// Instantiate
var plugin = new Plugin(this, opts);
var pluginId = plugin.id;
this.plugins[plugin.type] = this.plugins[plugin.type] || [];
if (!pluginId) {
throw new Error('Your plugin must have an id');
}
if (!plugin.type) {
throw new Error('Your plugin must have a type');
}
var existsPluginAlready = this.getPlugin(pluginId);
if (existsPluginAlready) {
var _msg = 'Already found a plugin named \'' + existsPluginAlready.id + '\'.\n Tried to use: \'' + pluginId + '\'.\n Uppy is currently limited to running one of every plugin.\n Share your use case with us over at\n https://github.com/transloadit/uppy/issues/\n if you want us to reconsider.';
throw new Error(_msg);
}
this.plugins[plugin.type].push(plugin);
plugin.install();
return this;
};
/**
* Find one Plugin by name
*
* @param string name description
*/
Uppy.prototype.getPlugin = function getPlugin(name) {
var foundPlugin = false;
this.iteratePlugins(function (plugin) {
var pluginName = plugin.id;
if (pluginName === name) {
foundPlugin = plugin;
return false;
}
});
return foundPlugin;
};
/**
* Iterate through all `use`d plugins
*
* @param function method description
*/
Uppy.prototype.iteratePlugins = function iteratePlugins(method) {
var _this4 = this;
Object.keys(this.plugins).forEach(function (pluginType) {
_this4.plugins[pluginType].forEach(method);
});
};
/**
* Uninstall and remove a plugin.
*
* @param {Plugin} instance The plugin instance to remove.
*/
Uppy.prototype.removePlugin = function removePlugin(instance) {
var list = this.plugins[instance.type];
if (instance.uninstall) {
instance.uninstall();
}
var index = list.indexOf(instance);
if (index !== -1) {
list.splice(index, 1);
}
};
/**
* Uninstall all plugins and close down this Uppy instance.
*/
Uppy.prototype.close = function close() {
this.reset();
this.iteratePlugins(function (plugin) {
plugin.uninstall();
});
if (this.socket) {
this.socket.close();
}
};
/**
* Set info message in `state.info`, so that UI plugins like `Informer`
* can display the message
*
* @param {string} msg Message to be displayed by the informer
*/
Uppy.prototype.info = function info(message, type, duration) {
var isComplexMessage = (typeof message === 'undefined' ? 'undefined' : _typeof(message)) === 'object';
this.setState({
info: {
isHidden: false,
type: type || 'info',
message: isComplexMessage ? message.message : message,
details: isComplexMessage ? message.details : null
}
});
this.emit('core:info-visible');
window.clearTimeout(this.infoTimeoutID);
if (duration === 0) {
this.infoTimeoutID = undefined;
return;
}
// hide the informer after `duration` milliseconds
this.infoTimeoutID = setTimeout(this.hideInfo, duration);
};
Uppy.prototype.hideInfo = function hideInfo() {
var newInfo = _extends({}, this.state.info, {
isHidden: true
});
this.setState({
info: newInfo
});
this.emit('core:info-hidden');
};
/**
* Logs stuff to console, only if `debug` is set to true. Silent in production.
*
* @return {String|Object} to log
*/
Uppy.prototype.log = function log(msg, type) {
if (!this.opts.debug) {
return;
}
if (type === 'error') {
console.error('LOG: ' + msg);
return;
}
if (msg === '' + msg) {
console.log('LOG: ' + msg);
} else {
console.dir(msg);
}
global.uppyLog = global.uppyLog + '\n' + 'DEBUG LOG: ' + msg;
};
Uppy.prototype.initSocket = function initSocket(opts) {
if (!this.socket) {
this.socket = new UppySocket(opts);
}
return this.socket;
};
/**
* Initializes actions, installs all plugins (by iterating on them and calling `install`), sets options
*
*/
Uppy.prototype.run = function run() {
this.log('Core is run, initializing actions...');
this.actions();
return this;
};
/**
* Restore an upload by its ID.
*/
Uppy.prototype.restore = function restore(uploadID) {
this.log('Core: attempting to restore upload "' + uploadID + '"');
if (!this.state.currentUploads[uploadID]) {
this.removeUpload(uploadID);
return Promise.reject(new Error('Nonexistent upload'));
}
return this.runUpload(uploadID);
};
/**
* Create an upload for a bunch of files.
*
* @param {Array<string>} fileIDs File IDs to include in this upload.
* @return {string} ID of this upload.
*/
Uppy.prototype.createUpload = function createUpload(fileIDs) {
var _extends3;
var uploadID = cuid();
this.emit('core:upload', {
id: uploadID,
fileIDs: fileIDs
});
this.setState({
currentUploads: _extends({}, this.state.currentUploads, (_extends3 = {}, _extends3[uploadID] = {
fileIDs: fileIDs,
step: 0
}, _extends3))
});
return uploadID;
};
/**
* Remove an upload, eg. if it has been canceled or completed.
*
* @param {string} uploadID The ID of the upload.
*/
Uppy.prototype.removeUpload = function removeUpload(uploadID) {
var currentUploads = _extends({}, this.state.currentUploads);
delete currentUploads[uploadID];
this.setState({
currentUploads: currentUploads
});
};
/**
* Run an upload. This picks up where it left off in case the upload is being restored.
*
* @private
*/
Uppy.prototype.runUpload = function runUpload(uploadID) {
var _this5 = this;
var uploadData = this.state.currentUploads[uploadID];
var fileIDs = uploadData.fileIDs;
var restoreStep = uploadData.step;
var steps = [].concat(this.preProcessors, this.uploaders, this.postProcessors);
var lastStep = Promise.resolve();
steps.forEach(function (fn, step) {
// Skip this step if we are restoring and have already completed this step before.
if (step < restoreStep) {
return;
}
lastStep = lastStep.then(function () {
var _extends4;
var currentUpload = _extends({}, _this5.state.currentUploads[uploadID], {
step: step
});
_this5.setState({
currentUploads: _extends({}, _this5.state.currentUploads, (_extends4 = {}, _extends4[uploadID] = currentUpload, _extends4))
}
// TODO give this the `currentUpload` object as its only parameter maybe?
// Otherwise when more metadata may be added to the upload this would keep getting more parameters
);return fn(fileIDs, uploadID);
});
}
// Not returning the `catch`ed promise, because we still want to return a rejected
// promise from this method if the upload failed.
);lastStep.catch(function (err) {
_this5.emit('core:error', err);
_this5.removeUpload(uploadID);
});
return lastStep.then(function () {
_this5.emit('core:success', fileIDs);
_this5.removeUpload(uploadID);
});
};
/**
* Start an upload for all the files that are not currently being uploaded.
*
* @return {Promise}
*/
Uppy.prototype.upload = function upload(forceUpload) {
var _this6 = this;
var isMinNumberOfFilesReached = this.checkMinNumberOfFiles();
if (!isMinNumberOfFilesReached) {
return Promise.reject('Minimum number of files has not been reached');
}
var beforeUpload = Promise.resolve().then(function () {
return _this6.opts.onBeforeUpload(_this6.state.files);
});
return beforeUpload.catch(function (err) {
_this6.info(err, 'error', 5000);
return Promise.reject('onBeforeUpload: ' + err);
}).then(function () {
var waitingFileIDs = [];
Object.keys(_this6.state.files).forEach(function (fileID) {
var file = _this6.getFile(fileID
// TODO: replace files[file].isRemote with some logic
//
// filter files that are now yet being uploaded / haven’t been uploaded
// and remote too
);if (forceUpload) {
_this6.resetProgress();
waitingFileIDs.push(file.id);
} else if (!file.progress.uploadStarted || file.isRemote) {
waitingFileIDs.push(file.id);
}
});
var uploadID = _this6.createUpload(waitingFileIDs);
return _this6.runUpload(uploadID);
});
};
return Uppy;
}();
module.exports = function (opts) {
return new Uppy(opts);
};
//# sourceMappingURL=Core.js.map