diff options
Diffstat (limited to 'web/react/components/file_upload.jsx')
-rw-r--r-- | web/react/components/file_upload.jsx | 230 |
1 files changed, 104 insertions, 126 deletions
diff --git a/web/react/components/file_upload.jsx b/web/react/components/file_upload.jsx index e77982559..534f0136e 100644 --- a/web/react/components/file_upload.jsx +++ b/web/react/components/file_upload.jsx @@ -6,71 +6,64 @@ var Constants = require('../utils/constants.jsx'); var ChannelStore = require('../stores/channel_store.jsx'); var utils = require('../utils/utils.jsx'); -module.exports = React.createClass({ - displayName: 'FileUpload', - propTypes: { - onUploadError: React.PropTypes.func, - getFileCount: React.PropTypes.func, - onFileUpload: React.PropTypes.func, - onUploadStart: React.PropTypes.func, - channelId: React.PropTypes.string, - postType: React.PropTypes.string - }, - getInitialState: function() { - return {requests: {}}; - }, - handleChange: function() { - var element = $(this.refs.fileInput.getDOMNode()); - var files = element.prop('files'); +export default class FileUpload extends React.Component { + constructor(props) { + super(props); - var channelId = this.props.channelId || ChannelStore.getCurrentId(); + this.uploadFiles = this.uploadFiles.bind(this); + this.handleChange = this.handleChange.bind(this); + this.handleDrop = this.handleDrop.bind(this); - this.props.onUploadError(null); + this.state = { + requests: {} + }; + } - // This looks redundant, but must be done this way due to - // setState being an asynchronous call - var numFiles = 0; - for (var i = 0; i < files.length; i++) { - if (files[i].size <= Constants.MAX_FILE_SIZE) { - numFiles++; - } + fileUploadSuccess(channelId, data) { + var parsedData = $.parseJSON(data); + this.props.onFileUpload(parsedData.filenames, parsedData.client_ids, channelId); + + var requests = this.state.requests; + for (var j = 0; j < parsedData.client_ids.length; j++) { + delete requests[parsedData.client_ids[j]]; } + this.setState({requests: requests}); + } - var numToUpload = Math.min(Constants.MAX_UPLOAD_FILES - this.props.getFileCount(channelId), numFiles); + fileUploadFail(clientId, err) { + this.props.onUploadError(err, clientId); + } - if (numFiles > numToUpload) { - this.props.onUploadError('Uploads limited to ' + Constants.MAX_UPLOAD_FILES + ' files maximum. Please use additional posts for more files.'); - } + uploadFiles(files) { + // clear any existing errors + this.props.onUploadError(null); + + var channelId = this.props.channelId || ChannelStore.getCurrentId(); + + var uploadsRemaining = Constants.MAX_UPLOAD_FILES - this.props.getFileCount(channelId); + var numUploads = 0; + + // keep track of how many files have been too large + var tooLargeFiles = []; - for (var i = 0; i < files.length && i < numToUpload; i++) { + for (let i = 0; i < files.length && numUploads < uploadsRemaining; i++) { if (files[i].size > Constants.MAX_FILE_SIZE) { - this.props.onUploadError('Files must be no more than ' + Constants.MAX_FILE_SIZE / 1000000 + ' MB'); + tooLargeFiles.push(files[i]); continue; } - // generate a unique id that can be used by other components to refer back to this file upload + // generate a unique id that can be used by other components to refer back to this upload var clientId = utils.generateId(); - // Prepare data to be uploaded. + // prepare data to be uploaded var formData = new FormData(); formData.append('channel_id', channelId); formData.append('files', files[i], files[i].name); formData.append('client_ids', clientId); var request = client.uploadFile(formData, - function(data) { - var parsedData = $.parseJSON(data); - this.props.onFileUpload(parsedData.filenames, parsedData.client_ids, channelId); - - var requests = this.state.requests; - for (var j = 0; j < parsedData.client_ids.length; j++) { - delete requests[parsedData.client_ids[j]]; - } - this.setState({requests: requests}); - }.bind(this), - function(err) { - this.props.onUploadError(err, clientId); - }.bind(this) + this.fileUploadSuccess.bind(this, channelId), + this.fileUploadFail.bind(this, clientId) ); var requests = this.state.requests; @@ -78,8 +71,26 @@ module.exports = React.createClass({ this.setState({requests: requests}); this.props.onUploadStart([clientId], channelId); + + numUploads += 1; } + if (files.length > uploadsRemaining) { + this.props.onUploadError(`Uploads limited to ${Constants.MAX_UPLOAD_FILES} files maximum. Please use additional posts for more files.`); + } else if (tooLargeFiles.length > 1) { + var tooLargeFilenames = tooLargeFiles.map((file) => file.name).join(', '); + + this.props.onUploadError(`Files above ${Constants.MAX_FILE_SIZE / 1000000}MB could not be uploaded: ${tooLargeFilenames}`); + } else if (tooLargeFiles.length > 0) { + this.props.onUploadError(`File above ${Constants.MAX_FILE_SIZE / 1000000}MB could not be uploaded: ${tooLargeFiles[0].name}`); + } + } + + handleChange() { + var element = $(React.findDOMNode(this.refs.fileInput)); + + this.uploadFiles(element.prop('files')); + // clear file input for all modern browsers try { element[0].value = ''; @@ -87,97 +98,56 @@ module.exports = React.createClass({ element[0].type = 'text'; element[0].type = 'file'; } - } catch(e) {} - }, - handleDrop: function(e) { + } catch(e) { + // Do nothing + } + } + + handleDrop(e) { this.props.onUploadError(null); var files = e.originalEvent.dataTransfer.files; - var channelId = this.props.channelId || ChannelStore.getCurrentId(); if (typeof files !== 'string' && files.length) { - var numFiles = files.length; - - var numToUpload = Math.min(Constants.MAX_UPLOAD_FILES - this.props.getFileCount(channelId), numFiles); - - if (numFiles > numToUpload) { - this.props.onUploadError('Uploads limited to ' + Constants.MAX_UPLOAD_FILES + ' files maximum. Please use additional posts for more files.'); - } - - for (var i = 0; i < files.length && i < numToUpload; i++) { - if (files[i].size > Constants.MAX_FILE_SIZE) { - this.props.onUploadError('Files must be no more than ' + Constants.MAX_FILE_SIZE / 1000000 + ' MB'); - continue; - } - - // generate a unique id that can be used by other components to refer back to this file upload - var clientId = utils.generateId(); - - // Prepare data to be uploaded. - var formData = new FormData(); - formData.append('channel_id', channelId); - formData.append('files', files[i], files[i].name); - formData.append('client_ids', clientId); - - var request = client.uploadFile(formData, - function(data) { - var parsedData = $.parseJSON(data); - this.props.onFileUpload(parsedData.filenames, parsedData.client_ids, channelId); - - var requests = this.state.requests; - for (var j = 0; j < parsedData.client_ids.length; j++) { - delete requests[parsedData.client_ids[j]]; - } - this.setState({requests: requests}); - }.bind(this), - function(err) { - this.props.onUploadError(err, clientId); - }.bind(this) - ); - - var requests = this.state.requests; - requests[clientId] = request; - this.setState({requests: requests}); - - this.props.onUploadStart([clientId], channelId); - } + this.uploadFiles(files); } else { this.props.onUploadError('Invalid file upload', -1); } - }, - componentDidMount: function() { - var inputDiv = this.refs.input.getDOMNode(); + } + + componentDidMount() { + var inputDiv = React.findDOMNode(this.refs.input); var self = this; if (this.props.postType === 'post') { $('.row.main').dragster({ - enter: function() { + enter() { $('.center-file-overlay').removeClass('hidden'); }, - leave: function() { + leave() { $('.center-file-overlay').addClass('hidden'); }, - drop: function(dragsterEvent, e) { + drop(dragsterEvent, e) { $('.center-file-overlay').addClass('hidden'); self.handleDrop(e); } }); } else if (this.props.postType === 'comment') { $('.post-right__container').dragster({ - enter: function() { + enter() { $('.right-file-overlay').removeClass('hidden'); }, - leave: function() { + leave() { $('.right-file-overlay').addClass('hidden'); }, - drop: function(dragsterEvent, e) { + drop(dragsterEvent, e) { $('.right-file-overlay').addClass('hidden'); self.handleDrop(e); } }); } - document.addEventListener('paste', function(e) { + document.addEventListener('paste', function handlePaste(e) { var textarea = $(inputDiv.parentNode.parentNode).find('.custom-textarea')[0]; if (textarea !== e.target && !$.contains(textarea, e.target)) { @@ -191,7 +161,7 @@ module.exports = React.createClass({ var items = e.clipboardData.items; var numItems = 0; if (items) { - for (var i = 0; i < items.length; i++) { + for (let i = 0; i < items.length; i++) { if (items[i].type.indexOf('image') !== -1) { var testExt = items[i].type.split('/')[1].toLowerCase(); @@ -245,19 +215,8 @@ module.exports = React.createClass({ formData.append('client_ids', clientId); var request = client.uploadFile(formData, - function(data) { - var parsedData = $.parseJSON(data); - self.props.onFileUpload(parsedData.filenames, parsedData.client_ids, channelId); - - var requests = self.state.requests; - for (var j = 0; j < parsedData.client_ids.length; j++) { - delete requests[parsedData.client_ids[j]]; - } - self.setState({requests: requests}); - }, - function(err) { - self.props.onUploadError(err, clientId); - } + self.fileUploadSuccess.bind(self, channelId), + self.fileUploadFail.bind(self, clientId) ); var requests = self.state.requests; @@ -269,8 +228,9 @@ module.exports = React.createClass({ } } }); - }, - cancelUpload: function(clientId) { + } + + cancelUpload(clientId) { var requests = this.state.requests; var request = requests[clientId]; @@ -280,15 +240,33 @@ module.exports = React.createClass({ delete requests[clientId]; this.setState({requests: requests}); } - }, - render: function() { + } + + render() { return ( - <span ref='input' className='btn btn-file'> + <span + ref='input' + className='btn btn-file' + > <span> <i className='glyphicon glyphicon-paperclip' /> </span> - <input ref='fileInput' type='file' onChange={this.handleChange} multiple/> + <input + ref='fileInput' + type='file' + onChange={this.handleChange} + multiple='true' + /> </span> ); } -}); +} + +FileUpload.propTypes = { + onUploadError: React.PropTypes.func, + getFileCount: React.PropTypes.func, + onFileUpload: React.PropTypes.func, + onUploadStart: React.PropTypes.func, + channelId: React.PropTypes.string, + postType: React.PropTypes.string +}; |