Merge pull request #45 from matrix-org/file_uploads
Improve file uploads
This commit is contained in:
commit
c0391145e5
4 changed files with 202 additions and 72 deletions
|
@ -18,6 +18,10 @@ limitations under the License.
|
||||||
|
|
||||||
var q = require('q');
|
var q = require('q');
|
||||||
var extend = require('./extend');
|
var extend = require('./extend');
|
||||||
|
var dis = require('./dispatcher');
|
||||||
|
var MatrixClientPeg = require('./MatrixClientPeg');
|
||||||
|
var sdk = require('./index');
|
||||||
|
var Modal = require('./Modal');
|
||||||
|
|
||||||
function infoForImageFile(imageFile) {
|
function infoForImageFile(imageFile) {
|
||||||
var deferred = q.defer();
|
var deferred = q.defer();
|
||||||
|
@ -48,39 +52,108 @@ function infoForImageFile(imageFile) {
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendContentToRoom(file, roomId, matrixClient) {
|
class ContentMessages {
|
||||||
var content = {
|
constructor() {
|
||||||
body: file.name,
|
this.inprogress = [];
|
||||||
info: {
|
this.nextId = 0;
|
||||||
size: file.size,
|
}
|
||||||
|
|
||||||
|
sendContentToRoom(file, roomId, matrixClient) {
|
||||||
|
var content = {
|
||||||
|
body: file.name,
|
||||||
|
info: {
|
||||||
|
size: file.size,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// if we have a mime type for the file, add it to the message metadata
|
||||||
|
if (file.type) {
|
||||||
|
content.info.mimetype = file.type;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// if we have a mime type for the file, add it to the message metadata
|
var def = q.defer();
|
||||||
if (file.type) {
|
if (file.type.indexOf('image/') == 0) {
|
||||||
content.info.mimetype = file.type;
|
content.msgtype = 'm.image';
|
||||||
}
|
infoForImageFile(file).then(function(imageInfo) {
|
||||||
|
extend(content.info, imageInfo);
|
||||||
var def = q.defer();
|
def.resolve();
|
||||||
if (file.type.indexOf('image/') == 0) {
|
});
|
||||||
content.msgtype = 'm.image';
|
} else {
|
||||||
infoForImageFile(file).then(function(imageInfo) {
|
content.msgtype = 'm.file';
|
||||||
extend(content.info, imageInfo);
|
|
||||||
def.resolve();
|
def.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
var upload = {
|
||||||
|
fileName: file.name,
|
||||||
|
roomId: roomId,
|
||||||
|
total: 0,
|
||||||
|
loaded: 0
|
||||||
|
};
|
||||||
|
this.inprogress.push(upload);
|
||||||
|
dis.dispatch({action: 'upload_started'});
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
return def.promise.then(function() {
|
||||||
|
upload.promise = matrixClient.uploadContent(file);
|
||||||
|
return upload.promise;
|
||||||
|
}).progress(function(ev) {
|
||||||
|
if (ev) {
|
||||||
|
upload.total = ev.total;
|
||||||
|
upload.loaded = ev.loaded;
|
||||||
|
dis.dispatch({action: 'upload_progress', upload: upload});
|
||||||
|
}
|
||||||
|
}).then(function(url) {
|
||||||
|
dis.dispatch({action: 'upload_finished', upload: upload});
|
||||||
|
content.url = url;
|
||||||
|
return matrixClient.sendMessage(roomId, content);
|
||||||
|
}, function(err) {
|
||||||
|
dis.dispatch({action: 'upload_failed', upload: upload});
|
||||||
|
if (!upload.canceled) {
|
||||||
|
var desc = "The file '"+upload.fileName+"' failed to upload.";
|
||||||
|
if (err.http_status == 413) {
|
||||||
|
desc = "The file '"+upload.fileName+"' exceeds this home server's size limit for uploads";
|
||||||
|
}
|
||||||
|
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
|
Modal.createDialog(ErrorDialog, {
|
||||||
|
title: "Upload Failed",
|
||||||
|
description: desc
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).finally(function() {
|
||||||
|
var inprogressKeys = Object.keys(self.inprogress);
|
||||||
|
for (var i = 0; i < self.inprogress.length; ++i) {
|
||||||
|
var k = inprogressKeys[i];
|
||||||
|
if (self.inprogress[k].promise === upload.promise) {
|
||||||
|
self.inprogress.splice(k, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
content.msgtype = 'm.file';
|
|
||||||
def.resolve();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return def.promise.then(function() {
|
getCurrentUploads() {
|
||||||
return matrixClient.uploadContent(file);
|
return this.inprogress;
|
||||||
}).then(function(url) {
|
}
|
||||||
content.url = url;
|
|
||||||
return matrixClient.sendMessage(roomId, content);
|
cancelUpload(promise) {
|
||||||
});
|
var inprogressKeys = Object.keys(this.inprogress);
|
||||||
|
var upload;
|
||||||
|
for (var i = 0; i < this.inprogress.length; ++i) {
|
||||||
|
var k = inprogressKeys[i];
|
||||||
|
if (this.inprogress[k].promise === promise) {
|
||||||
|
upload = this.inprogress[k];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (upload) {
|
||||||
|
upload.canceled = true;
|
||||||
|
MatrixClientPeg.get().cancelUpload(upload.promise);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
if (global.mx_ContentMessage === undefined) {
|
||||||
sendContentToRoom: sendContentToRoom
|
global.mx_ContentMessage = new ContentMessages();
|
||||||
};
|
}
|
||||||
|
|
||||||
|
module.exports = global.mx_ContentMessage;
|
||||||
|
|
|
@ -28,6 +28,7 @@ module.exports.components['structures.login.PostRegistration'] = require('./comp
|
||||||
module.exports.components['structures.login.Registration'] = require('./components/structures/login/Registration');
|
module.exports.components['structures.login.Registration'] = require('./components/structures/login/Registration');
|
||||||
module.exports.components['structures.MatrixChat'] = require('./components/structures/MatrixChat');
|
module.exports.components['structures.MatrixChat'] = require('./components/structures/MatrixChat');
|
||||||
module.exports.components['structures.RoomView'] = require('./components/structures/RoomView');
|
module.exports.components['structures.RoomView'] = require('./components/structures/RoomView');
|
||||||
|
module.exports.components['structures.UploadBar'] = require('./components/structures/UploadBar');
|
||||||
module.exports.components['structures.UserSettings'] = require('./components/structures/UserSettings');
|
module.exports.components['structures.UserSettings'] = require('./components/structures/UserSettings');
|
||||||
module.exports.components['views.avatars.MemberAvatar'] = require('./components/views/avatars/MemberAvatar');
|
module.exports.components['views.avatars.MemberAvatar'] = require('./components/views/avatars/MemberAvatar');
|
||||||
module.exports.components['views.avatars.RoomAvatar'] = require('./components/views/avatars/RoomAvatar');
|
module.exports.components['views.avatars.RoomAvatar'] = require('./components/views/avatars/RoomAvatar');
|
||||||
|
|
|
@ -26,7 +26,6 @@ var ReactDOM = require("react-dom");
|
||||||
var GeminiScrollbar = require('react-gemini-scrollbar');
|
var GeminiScrollbar = require('react-gemini-scrollbar');
|
||||||
var q = require("q");
|
var q = require("q");
|
||||||
var classNames = require("classnames");
|
var classNames = require("classnames");
|
||||||
var filesize = require('filesize');
|
|
||||||
var Matrix = require("matrix-js-sdk");
|
var Matrix = require("matrix-js-sdk");
|
||||||
|
|
||||||
var MatrixClientPeg = require("../../MatrixClientPeg");
|
var MatrixClientPeg = require("../../MatrixClientPeg");
|
||||||
|
@ -107,6 +106,9 @@ module.exports = React.createClass({
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
break;
|
break;
|
||||||
case 'notifier_enabled':
|
case 'notifier_enabled':
|
||||||
|
case 'upload_failed':
|
||||||
|
case 'upload_started':
|
||||||
|
case 'upload_finished':
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
break;
|
break;
|
||||||
case 'call_state':
|
case 'call_state':
|
||||||
|
@ -408,30 +410,10 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
uploadFile: function(file) {
|
uploadFile: function(file) {
|
||||||
this.setState({
|
|
||||||
upload: {
|
|
||||||
fileName: file.name,
|
|
||||||
uploadedBytes: 0,
|
|
||||||
totalBytes: file.size
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var self = this;
|
var self = this;
|
||||||
ContentMessages.sendContentToRoom(
|
ContentMessages.sendContentToRoom(
|
||||||
file, this.props.roomId, MatrixClientPeg.get()
|
file, this.props.roomId, MatrixClientPeg.get()
|
||||||
).progress(function(ev) {
|
).done(undefined, function(error) {
|
||||||
//console.log("Upload: "+ev.loaded+" / "+ev.total);
|
|
||||||
self.setState({
|
|
||||||
upload: {
|
|
||||||
fileName: file.name,
|
|
||||||
uploadedBytes: ev.loaded,
|
|
||||||
totalBytes: ev.total
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).finally(function() {
|
|
||||||
self.setState({
|
|
||||||
upload: undefined
|
|
||||||
});
|
|
||||||
}).done(undefined, function(error) {
|
|
||||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
Modal.createDialog(ErrorDialog, {
|
Modal.createDialog(ErrorDialog, {
|
||||||
title: "Failed to upload file",
|
title: "Failed to upload file",
|
||||||
|
@ -894,28 +876,9 @@ module.exports = React.createClass({
|
||||||
// fileName: "testing_fooble.jpg",
|
// fileName: "testing_fooble.jpg",
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if (this.state.upload) {
|
if (ContentMessages.getCurrentUploads().length > 0) {
|
||||||
var innerProgressStyle = {
|
var UploadBar = sdk.getComponent('structures.UploadBar');
|
||||||
width: ((this.state.upload.uploadedBytes / this.state.upload.totalBytes) * 100) + '%'
|
statusBar = <UploadBar room={this.state.room} />
|
||||||
};
|
|
||||||
var uploadedSize = filesize(this.state.upload.uploadedBytes);
|
|
||||||
var totalSize = filesize(this.state.upload.totalBytes);
|
|
||||||
if (uploadedSize.replace(/^.* /,'') === totalSize.replace(/^.* /,'')) {
|
|
||||||
uploadedSize = uploadedSize.replace(/ .*/, '');
|
|
||||||
}
|
|
||||||
statusBar = (
|
|
||||||
<div className="mx_RoomView_uploadBar">
|
|
||||||
<div className="mx_RoomView_uploadProgressOuter">
|
|
||||||
<div className="mx_RoomView_uploadProgressInner" style={innerProgressStyle}></div>
|
|
||||||
</div>
|
|
||||||
<img className="mx_RoomView_uploadIcon" src="img/fileicon.png" width="17" height="22"/>
|
|
||||||
<img className="mx_RoomView_uploadCancel" src="img/cancel.png" width="18" height="18"/>
|
|
||||||
<div className="mx_RoomView_uploadBytes">
|
|
||||||
{ uploadedSize } / { totalSize }
|
|
||||||
</div>
|
|
||||||
<div className="mx_RoomView_uploadFilename">Uploading {this.state.upload.fileName}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
var typingString = this.getWhoIsTypingString();
|
var typingString = this.getWhoIsTypingString();
|
||||||
// typingString = "S͚͍̭̪̤͙̱͙̖̥͙̥̤̻̙͕͓͂̌ͬ͐̂k̜̝͎̰̥̻̼̂̌͛͗͊̅̒͂̊̍̍͌̈̈́͌̋̊ͬa͉̯͚̺̗̳̩ͪ̋̑͌̓̆̍̂̉̏̅̆ͧ̌̑v̲̲̪̝ͥ̌ͨͮͭ̊͆̾ͮ̍ͮ͑̚e̮̙͈̱̘͕̼̮͒ͩͨͫ̃͗̇ͩ͒ͣͦ͒̄̍͐ͣ̿ͥṘ̗̺͇̺̺͔̄́̊̓͊̍̃ͨ̚ā̼͎̘̟̼͎̜̪̪͚̋ͨͨͧ̓ͦͯͤ̄͆̋͂ͩ͌ͧͅt̙̙̹̗̦͖̞ͫͪ͑̑̅ͪ̃̚ͅ is typing...";
|
// typingString = "S͚͍̭̪̤͙̱͙̖̥͙̥̤̻̙͕͓͂̌ͬ͐̂k̜̝͎̰̥̻̼̂̌͛͗͊̅̒͂̊̍̍͌̈̈́͌̋̊ͬa͉̯͚̺̗̳̩ͪ̋̑͌̓̆̍̂̉̏̅̆ͧ̌̑v̲̲̪̝ͥ̌ͨͮͭ̊͆̾ͮ̍ͮ͑̚e̮̙͈̱̘͕̼̮͒ͩͨͫ̃͗̇ͩ͒ͣͦ͒̄̍͐ͣ̿ͥṘ̗̺͇̺̺͔̄́̊̓͊̍̃ͨ̚ā̼͎̘̟̼͎̜̪̪͚̋ͨͨͧ̓ͦͯͤ̄͆̋͂ͩ͌ͧͅt̙̙̹̗̦͖̞ͫͪ͑̑̅ͪ̃̚ͅ is typing...";
|
||||||
|
|
93
src/components/structures/UploadBar.js
Normal file
93
src/components/structures/UploadBar.js
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
Copyright 2015 OpenMarket Ltd
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var React = require('react');
|
||||||
|
var ContentMessages = require('../../ContentMessages');
|
||||||
|
var dis = require('../../dispatcher');
|
||||||
|
var filesize = require('filesize');
|
||||||
|
|
||||||
|
module.exports = React.createClass({displayName: 'UploadBar',
|
||||||
|
propTypes: {
|
||||||
|
room: React.PropTypes.object
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount: function() {
|
||||||
|
dis.register(this.onAction);
|
||||||
|
this.mounted = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount: function() {
|
||||||
|
this.mounted = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
onAction: function(payload) {
|
||||||
|
switch (payload.action) {
|
||||||
|
case 'upload_progress':
|
||||||
|
case 'upload_finished':
|
||||||
|
case 'upload_failed':
|
||||||
|
if (this.mounted) this.forceUpdate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var uploads = ContentMessages.getCurrentUploads();
|
||||||
|
if (uploads.length == 0) {
|
||||||
|
return <div />
|
||||||
|
}
|
||||||
|
|
||||||
|
var upload;
|
||||||
|
for (var i = 0; i < uploads.length; ++i) {
|
||||||
|
if (uploads[i].roomId == this.props.room.roomId) {
|
||||||
|
upload = uploads[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!upload) {
|
||||||
|
upload = uploads[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
var innerProgressStyle = {
|
||||||
|
width: ((upload.loaded / (upload.total || 1)) * 100) + '%'
|
||||||
|
};
|
||||||
|
var uploadedSize = filesize(upload.loaded);
|
||||||
|
var totalSize = filesize(upload.total);
|
||||||
|
if (uploadedSize.replace(/^.* /,'') === totalSize.replace(/^.* /,'')) {
|
||||||
|
uploadedSize = uploadedSize.replace(/ .*/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
var others;
|
||||||
|
if (uploads.length > 1) {
|
||||||
|
others = 'and '+(uploads.length - 1) + ' other' + (uploads.length > 2 ? 's' : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx_UploadBar">
|
||||||
|
<div className="mx_UploadBar_uploadProgressOuter">
|
||||||
|
<div className="mx_UploadBar_uploadProgressInner" style={innerProgressStyle}></div>
|
||||||
|
</div>
|
||||||
|
<img className="mx_UploadBar_uploadIcon" src="img/fileicon.png" width="17" height="22"/>
|
||||||
|
<img className="mx_UploadBar_uploadCancel" src="img/cancel.png" width="18" height="18"
|
||||||
|
onClick={function() { ContentMessages.cancelUpload(upload.promise); }}
|
||||||
|
/>
|
||||||
|
<div className="mx_UploadBar_uploadBytes">
|
||||||
|
{ uploadedSize } / { totalSize }
|
||||||
|
</div>
|
||||||
|
<div className="mx_UploadBar_uploadFilename">Uploading {upload.fileName}{others}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
Loading…
Add table
Add a link
Reference in a new issue