summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhmhealey <harrisonmhealey@gmail.com>2015-08-10 12:05:45 -0400
committerhmhealey <harrisonmhealey@gmail.com>2015-08-10 14:04:23 -0400
commitc9a1bf2d336cc5718cdf327f37cfdf87dc0e2705 (patch)
treed90cdfebf96ac6a4557a8caaefe06888b7d9d6ba
parent4b74c873cc95973c5434988a41f5e06446a4e586 (diff)
downloadchat-c9a1bf2d336cc5718cdf327f37cfdf87dc0e2705.tar.gz
chat-c9a1bf2d336cc5718cdf327f37cfdf87dc0e2705.tar.bz2
chat-c9a1bf2d336cc5718cdf327f37cfdf87dc0e2705.zip
Changed post drafts to maintain a store a unique id for each file upload to fix issues with duplicate file names
-rw-r--r--api/file.go8
-rw-r--r--model/file.go1
-rw-r--r--web/react/components/create_comment.jsx30
-rw-r--r--web/react/components/create_post.jsx30
-rw-r--r--web/react/components/file_preview.jsx11
-rw-r--r--web/react/components/file_upload.jsx36
-rw-r--r--web/react/utils/utils.jsx21
7 files changed, 79 insertions, 58 deletions
diff --git a/api/file.go b/api/file.go
index bf1c59422..558f9357e 100644
--- a/api/file.go
+++ b/api/file.go
@@ -71,7 +71,9 @@ func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) {
files := m.File["files"]
resStruct := &model.FileUploadResponse{
- Filenames: []string{}}
+ Filenames: []string{},
+ ClientIds: []string{},
+ }
imageNameList := []string{}
imageDataList := [][]byte{}
@@ -113,6 +115,10 @@ func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) {
resStruct.Filenames = append(resStruct.Filenames, fileUrl)
}
+ for _, clientId := range props["client_ids"] {
+ resStruct.ClientIds = append(resStruct.ClientIds, clientId)
+ }
+
fireAndForgetHandleImages(imageNameList, imageDataList, c.Session.TeamId, channelId, c.Session.UserId)
w.Write([]byte(resStruct.ToJson()))
diff --git a/model/file.go b/model/file.go
index 7f5a3f916..3d38ddbd1 100644
--- a/model/file.go
+++ b/model/file.go
@@ -19,6 +19,7 @@ var (
type FileUploadResponse struct {
Filenames []string `json:"filenames"`
+ ClientIds []string `json:"client_ids"`
}
func FileUploadResponseFromJson(data io.Reader) *FileUploadResponse {
diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx
index 13ee6deb4..c16909b8b 100644
--- a/web/react/components/create_comment.jsx
+++ b/web/react/components/create_comment.jsx
@@ -95,7 +95,7 @@ module.exports = React.createClass({
$(".post-right__scroll").perfectScrollbar('update');
this.setState({messageText: messageText});
},
- handleUploadStart: function(filenames, channel_id) {
+ handleUploadStart: function(clientIds, channel_id) {
var draft = PostStore.getCommentDraft(this.props.rootId);
if (!draft) {
draft = {};
@@ -104,12 +104,12 @@ module.exports = React.createClass({
draft['previews'] = [];
}
- draft['uploadsInProgress'] = draft['uploadsInProgress'].concat(filenames);
+ draft['uploadsInProgress'] = draft['uploadsInProgress'].concat(clientIds);
PostStore.storeCommentDraft(this.props.rootId, draft);
this.setState({uploadsInProgress: draft['uploadsInProgress']});
},
- handleFileUploadComplete: function(filenames, channel_id) {
+ handleFileUploadComplete: function(filenames, clientIds, channel_id) {
var draft = PostStore.getCommentDraft(this.props.rootId);
if (!draft) {
draft = {};
@@ -119,18 +119,8 @@ module.exports = React.createClass({
}
// remove each finished file from uploads
- for (var i = 0; i < filenames.length; i++) {
- var filename = filenames[i];
-
- // filenames returned by the server include a path while stored uploads only have the actual file name
- var index = -1;
- for (var j = 0; j < draft['uploadsInProgress'].length; j++) {
- var upload = draft['uploadsInProgress'][j];
- if (upload.indexOf(filename, upload.length - filename.length)) {
- index = j;
- break;
- }
- }
+ for (var i = 0; i < clientIds.length; i++) {
+ var index = draft['uploadsInProgress'].indexOf(clientIds[i]);
if (index != -1) {
draft['uploadsInProgress'].splice(index, 1);
@@ -148,20 +138,20 @@ module.exports = React.createClass({
clearPreviews: function() {
this.setState({previews: []});
},
- removePreview: function(filename) {
+ removePreview: function(id) {
var previews = this.state.previews;
var uploadsInProgress = this.state.uploadsInProgress;
- // this can be either an uploaded file or an in progress upload that we need to remove
- var index = previews.indexOf(filename);
+ // id can either be the path of an uploaded file or the client id of an in progress upload
+ var index = previews.indexOf(id);
if (index !== -1) {
previews.splice(index, 1);
} else {
- index = uploadsInProgress.indexOf(filename);
+ index = uploadsInProgress.indexOf(id);
if (index !== -1) {
uploadsInProgress.splice(index, 1);
- this.refs.fileUpload.cancelUpload(filename);
+ this.refs.fileUpload.cancelUpload(id);
}
}
diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx
index b32b53749..a4107d4fc 100644
--- a/web/react/components/create_post.jsx
+++ b/web/react/components/create_post.jsx
@@ -128,7 +128,7 @@ module.exports = React.createClass({
$(".post-list-holder-by-time").css("height", height + "px");
$(window).trigger('resize');
},
- handleUploadStart: function(filenames, channel_id) {
+ handleUploadStart: function(clientIds, channel_id) {
var draft = PostStore.getDraft(channel_id);
if (!draft) {
draft = {};
@@ -137,12 +137,12 @@ module.exports = React.createClass({
draft['previews'] = [];
}
- draft['uploadsInProgress'] = draft['uploadsInProgress'].concat(filenames);
+ draft['uploadsInProgress'] = draft['uploadsInProgress'].concat(clientIds);
PostStore.storeDraft(channel_id, draft);
this.setState({uploadsInProgress: draft['uploadsInProgress']});
},
- handleFileUploadComplete: function(filenames, channel_id) {
+ handleFileUploadComplete: function(filenames, clientIds, channel_id) {
var draft = PostStore.getDraft(channel_id);
if (!draft) {
draft = {};
@@ -152,18 +152,8 @@ module.exports = React.createClass({
}
// remove each finished file from uploads
- for (var i = 0; i < filenames.length; i++) {
- var filename = filenames[i];
-
- // filenames returned by the server include a path while stored uploads only have the actual file name
- var index = -1;
- for (var j = 0; j < draft['uploadsInProgress'].length; j++) {
- var upload = draft['uploadsInProgress'][j];
- if (upload.indexOf(filename, upload.length - filename.length)) {
- index = j;
- break;
- }
- }
+ for (var i = 0; i < clientIds.length; i++) {
+ var index = draft['uploadsInProgress'].indexOf(clientIds[i]);
if (index != -1) {
draft['uploadsInProgress'].splice(index, 1);
@@ -178,20 +168,20 @@ module.exports = React.createClass({
handleUploadError: function(err) {
this.setState({ server_error: err });
},
- removePreview: function(filename) {
+ removePreview: function(id) {
var previews = this.state.previews;
var uploadsInProgress = this.state.uploadsInProgress;
- // this can be either an uploaded file or an in progress upload that we need to remove
- var index = previews.indexOf(filename);
+ // id can either be the path of an uploaded file or the client id of an in progress upload
+ var index = previews.indexOf(id);
if (index !== -1) {
previews.splice(index, 1);
} else {
- index = uploadsInProgress.indexOf(filename);
+ index = uploadsInProgress.indexOf(id);
if (index !== -1) {
uploadsInProgress.splice(index, 1);
- this.refs.fileUpload.cancelUpload(filename);
+ this.refs.fileUpload.cancelUpload(id);
}
}
diff --git a/web/react/components/file_preview.jsx b/web/react/components/file_preview.jsx
index 8218429fd..d1b2f734a 100644
--- a/web/react/components/file_preview.jsx
+++ b/web/react/components/file_preview.jsx
@@ -10,7 +10,12 @@ var Constants = require('../utils/constants.jsx');
module.exports = React.createClass({
handleRemove: function(e) {
var previewDiv = e.target.parentNode.parentNode;
- this.props.onRemove(previewDiv.getAttribute('data-filename'));
+
+ if (previewDiv.hasAttribute('data-filename')) {
+ this.props.onRemove(previewDiv.getAttribute('data-filename'));
+ } else if (previewDiv.hasAttribute('data-client-id')) {
+ this.props.onRemove(previewDiv.getAttribute('data-client-id'));
+ }
},
render: function() {
var previews = [];
@@ -43,9 +48,9 @@ module.exports = React.createClass({
}
}.bind(this));
- this.props.uploadsInProgress.forEach(function(filename) {
+ this.props.uploadsInProgress.forEach(function(clientId) {
previews.push(
- <div className="preview-div" data-filename={filename}>
+ <div className="preview-div" data-client-id={clientId}>
<img className="spinner" src="/static/images/load.gif"/>
<a className="remove-preview" onClick={this.handleRemove}><i className="glyphicon glyphicon-remove"/></a>
</div>
diff --git a/web/react/components/file_upload.jsx b/web/react/components/file_upload.jsx
index 11b3b3cee..4b8965dcb 100644
--- a/web/react/components/file_upload.jsx
+++ b/web/react/components/file_upload.jsx
@@ -39,19 +39,23 @@ module.exports = React.createClass({
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.
formData = new FormData();
formData.append('channel_id', channel_id);
formData.append('files', files[i], files[i].name);
+ formData.append('client_ids', clientId);
var request = client.uploadFile(formData,
function(data) {
parsedData = $.parseJSON(data);
- this.props.onFileUpload(parsedData['filenames'], channel_id);
+ this.props.onFileUpload(parsedData['filenames'], parsedData['client_ids'], channel_id);
var requests = this.state.requests;
- for (var i = 0; i < parsedData['filenames'].length; i++) {
- delete requests[utils.getFileName(parsedData['filenames'][i])];
+ for (var i = 0; i < parsedData['client_ids'].length; i++) {
+ delete requests[parsedData['client_ids'][i]];
}
this.setState({requests: requests});
}.bind(this),
@@ -61,10 +65,10 @@ module.exports = React.createClass({
);
var requests = this.state.requests;
- requests[files[i].name] = request;
+ requests[clientId] = request;
this.setState({requests: requests});
- this.props.onUploadStart([files[i].name], channel_id);
+ this.props.onUploadStart([clientId], channel_id);
}
// clear file input for all modern browsers
@@ -123,6 +127,9 @@ module.exports = React.createClass({
var channel_id = ChannelStore.getCurrentId();
+ // generate a unique id that can be used by other components to refer back to this file upload
+ var clientId = utils.generateId();
+
formData = new FormData();
formData.append('channel_id', channel_id);
var d = new Date();
@@ -130,15 +137,16 @@ module.exports = React.createClass({
var min = d.getMinutes() < 10 ? "0" + d.getMinutes() : String(d.getMinutes());
var name = "Image Pasted at "+d.getFullYear()+"-"+d.getMonth()+"-"+d.getDate()+" "+hour+"-"+min+"." + ext;
formData.append('files', file, name);
+ formData.append('client_ids', clientId);
- client.uploadFile(formData,
+ var request = client.uploadFile(formData,
function(data) {
parsedData = $.parseJSON(data);
- self.props.onFileUpload(parsedData['filenames'], channel_id);
+ self.props.onFileUpload(parsedData['filenames'], parsedData['client_ids'], channel_id);
var requests = self.state.requests;
- for (var i = 0; i < parsedData['filenames'].length; i++) {
- delete requests[utils.getFileName(parsedData['filenames'][i])];
+ for (var i = 0; i < parsedData['client_ids'].length; i++) {
+ delete requests[parsedData['client_ids'][i]];
}
self.setState({requests: requests});
},
@@ -148,23 +156,23 @@ module.exports = React.createClass({
);
var requests = self.state.requests;
- requests[files[i].name] = request;
+ requests[clientId] = request;
self.setState({requests: requests});
- self.props.onUploadStart([name], channel_id);
+ self.props.onUploadStart([clientId], channel_id);
}
}
}
});
},
- cancelUpload: function(filename) {
+ cancelUpload: function(clientId) {
var requests = this.state.requests;
- var request = requests[filename];
+ var request = requests[clientId];
if (request) {
request.abort();
- delete requests[filename];
+ delete requests[clientId];
this.setState({requests: requests});
}
},
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index a759cc579..2214b6239 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -913,3 +913,24 @@ module.exports.getFileName = function(path) {
var split = path.split('/');
return split[split.length - 1];
};
+
+// Generates a RFC-4122 version 4 compliant globally unique identifier.
+module.exports.generateId = function() {
+ // implementation taken from http://stackoverflow.com/a/2117523
+ var id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
+
+ id = id.replace(/[xy]/g, function(c) {
+ var r = Math.floor(Math.random() * 16);
+
+ var v;
+ if (c === 'x') {
+ v = r;
+ } else {
+ v = r & 0x3 | 0x8;
+ }
+
+ return v.toString(16);
+ });
+
+ return id;
+};