Merge pull request #5 from rgarmsen2295/mm-316

MM-316 Allows users to drag and drop files from their computer to the center pane or RHS to upload them
Этот коммит содержится в:
asaadmahmoodspin
2015-08-13 20:31:19 +05:00
родитель 6d9e715e26 14c121dc0c
Коммит 1a6d67b0cd
12 изменённых файлов: 293 добавлений и 17 удалений

Просмотреть файл

@@ -222,7 +222,9 @@ module.exports = React.createClass({
getFileCount={this.getFileCount}
onUploadStart={this.handleUploadStart}
onFileUpload={this.handleFileUploadComplete}
onUploadError={this.handleUploadError} />
onUploadError={this.handleUploadError}
postType='comment'
channelId={this.props.channelId} />
</div>
<MsgTyping channelId={this.props.channelId} parentId={this.props.rootId} />
<div className={postFooterClassName}>

Просмотреть файл

@@ -262,7 +262,9 @@ module.exports = React.createClass({
getFileCount={this.getFileCount}
onUploadStart={this.handleUploadStart}
onFileUpload={this.handleFileUploadComplete}
onUploadError={this.handleUploadError} />
onUploadError={this.handleUploadError}
postType='post'
channelId='' />
</div>
<div className={postFooterClassName}>
{postError}

Просмотреть файл

@@ -21,7 +21,7 @@ module.exports = React.createClass({
var element = $(this.refs.fileInput.getDOMNode());
var files = element.prop('files');
var channelId = ChannelStore.getCurrentId();
var channelId = this.props.channelId || ChannelStore.getCurrentId();
this.props.onUploadError(null);
@@ -87,10 +87,101 @@ module.exports = React.createClass({
}
} catch(e) {}
},
handleDrop: function(e) {
this.props.onUploadError(null);
var files = e.originalEvent.dataTransfer.files;
if (!files.length) {
files = e.originalEvent.dataTransfer.getData('URL');
}
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 i = 0; i < parsedData['client_ids'].length; i++) {
delete requests[parsedData['client_ids'][i]];
}
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);
}
}
},
componentDidMount: function() {
var inputDiv = this.refs.input.getDOMNode();
var self = this;
if (this.props.postType === 'post') {
$('.app__content').dragster({
enter: function(dragsterEvent, e) {
$('.center-file-overlay').removeClass('invisible');
$('.center-file-overlay').addClass('visible');
},
leave: function(dragsterEvent, e) {
$('.center-file-overlay').removeClass('visible');
$('.center-file-overlay').addClass('invisible');
},
drop: function(dragsterEvent, e) {
$('.center-file-overlay').removeClass('visible');
$('.center-file-overlay').addClass('invisible');
self.handleDrop(e);
}
});
} else if (this.props.postType === 'comment') {
$('.post-right__container').dragster({
enter: function(dragsterEvent, e) {
$('.right-file-overlay').removeClass('invisible');
$('.right-file-overlay').addClass('visible');
},
leave: function(dragsterEvent, e) {
$('.right-file-overlay').removeClass('visible');
$('.right-file-overlay').addClass('invisible');
},
drop: function(dragsterEvent, e) {
$('.right-file-overlay').removeClass('visible');
$('.right-file-overlay').addClass('invisible');
self.handleDrop(e);
}
});
}
document.addEventListener('paste', function(e) {
var textarea = $(inputDiv.parentNode.parentNode).find('.custom-textarea')[0];
@@ -133,14 +224,13 @@ module.exports = React.createClass({
continue;
}
var channelId = ChannelStore.getCurrentId();
var channelId = this.props.channelId || ChannelStore.getCurrentId();
// generate a unique id that can be used by other components to refer back to this file upload
var clientId = utils.generateId();
var formData = new FormData();
formData.append('channel_id', channelId);
var d = new Date();
var hour;
if (d.getHours() < 10) {

Просмотреть файл

@@ -463,11 +463,17 @@ module.exports = React.createClass({
}
return (
<div ref="postlist" className="post-list-holder-by-time">
<div className="post-list__table">
<div className="post-list__content">
{ more_messages }
{ postCtls }
<div>
<div className='center-file-overlay invisible'>
<span className='center-file-help-text'>Drop a file to upload it.</span>
<img className='center-file-help-picture' />
</div>
<div ref="postlist" className="post-list-holder-by-time">
<div className="post-list__table">
<div className="post-list__content">
{ more_messages }
{ postCtls }
</div>
</div>
</div>
</div>

Просмотреть файл

@@ -296,6 +296,10 @@ module.exports = React.createClass({
return (
<div className="post-right__container">
<div className='right-file-overlay invisible'>
<span className='right-file-help-text'>Drop a file to upload it.</span>
<img className='right-file-help-picture' />
</div>
<div className="search-bar__container sidebar--right__search-header">{searchForm}</div>
<div className="sidebar-right__body">
<RhsHeaderPost fromSearch={this.props.fromSearch} isMentionSearch={this.props.isMentionSearch} />

Просмотреть файл

@@ -194,11 +194,12 @@
border-right: 1px solid #ddd;
vertical-align: center;
// helper to center the image icon in the preview window
.file-details__preview-helper {
height: 100%;
display: inline-block;
vertical-align: middle;
}
}
// helper to center the image icon in the preview window
.file-details__preview-helper {
height: 100%;
display: inline-block;
vertical-align: middle;
}
}
}

Просмотреть файл

@@ -107,6 +107,29 @@ body.ios {
}
#post-list {
.center-file-overlay {
top: 0px;
height: 100%;
width: 59.7%;
position: fixed;
z-index: 2147483646;
-moz-box-shadow: 0px 0px 3px #8a8a8a;
-webkit-box-shadow: 0px 0px 3px #8a8a8a;
box-shadow: 0px 0px 3px #8a8a8a;
.invisible {
visibility: hidden;
}
.visible {
visibility: visible;
}
.center-file-help-text {
}
.center-file-help-picture {
}
}
.post-list-holder-by-time {
background: #fff;
overflow-y: scroll;

Просмотреть файл

@@ -1,5 +1,28 @@
.post-right__container {
.right-file-overlay {
height: 100%;
width: 100%;
position: fixed;
z-index: 2147483646;
-moz-box-shadow: 0px 0px 3px #8a8a8a;
-webkit-box-shadow: 0px 0px 3px #8a8a8a;
box-shadow: 0px 0px 3px #8a8a8a;
.invisible {
visibility: hidden;
}
.visible {
visibility: visible;
}
.right-file-help-text {
}
.right-file-help-picture {
}
}
.post-right-root-message {
padding: 1em 1em 0;
}

21
web/static/js/jquery-dragster/LICENSE Обычный файл
Просмотреть файл

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Jan Martin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

17
web/static/js/jquery-dragster/README.md Обычный файл
Просмотреть файл

@@ -0,0 +1,17 @@
Include [jquery.dragster.js](https://rawgithub.com/catmanjan/jquery-dragster/master/jquery.dragster.js) in page.
Works in IE.
```javascript
$('.element').dragster({
enter: function (dragsterEvent, event) {
$(this).addClass('hover');
},
leave: function (dragsterEvent, event) {
$(this).removeClass('hover');
},
drop: function (dragsterEvent, event) {
$(this).removeClass('hover');
}
});
```

Просмотреть файл

@@ -0,0 +1,85 @@
// 1.0.3
/*
The MIT License (MIT)
Copyright (c) 2015 Jan Martin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
(function ($) {
$.fn.dragster = function (options) {
var settings = $.extend({
enter: $.noop,
leave: $.noop,
over: $.noop,
drop: $.noop
}, options);
return this.each(function () {
var first = false,
second = false,
$this = $(this);
$this.on({
dragenter: function (event) {
if (first) {
second = true;
return;
} else {
first = true;
$this.trigger('dragster:enter', event);
}
event.preventDefault();
},
dragleave: function (event) {
if (second) {
second = false;
} else if (first) {
first = false;
}
if (!first && !second) {
$this.trigger('dragster:leave', event);
}
event.preventDefault();
},
dragover: function (event) {
$this.trigger('dragster:over', event);
event.preventDefault();
},
drop: function (event) {
if (second) {
second = false;
} else if (first) {
first = false;
}
if (!first && !second) {
$this.trigger('dragster:drop', event);
}
event.preventDefault();
},
'dragster:enter': settings.enter,
'dragster:leave': settings.leave,
'dragster:over': settings.over,
'dragster:drop': settings.drop
});
});
};
}(jQuery));

Просмотреть файл

@@ -32,6 +32,8 @@
<script src="/static/js/perfect-scrollbar-0.6.3.jquery.js"></script>
<script src="/static/js/jquery-dragster/jquery.dragster.js"></script>
<script type="text/javascript" src="https://www.google.com/jsapi?autoload={'modules':[{'name':'visualization','version':'1','packages':['annotationchart']}]}"></script>
<script type="text/javascript" src="https://cloudfront.loggly.com/js/loggly.tracker.js" async></script>