Make file uploads cancelable, make them not go weird if you change rooms, error if they fail and move upload status bar out into its own component.
This commit is contained in:
parent
aceb85130e
commit
e8f5131ed6
4 changed files with 196 additions and 72 deletions
|
@ -18,6 +18,10 @@ limitations under the License.
|
|||
|
||||
var q = require('q');
|
||||
var extend = require('./extend');
|
||||
var dis = require('./dispatcher');
|
||||
var MatrixClientPeg = require('./MatrixClientPeg');
|
||||
var sdk = require('./index');
|
||||
var Modal = require('./Modal');
|
||||
|
||||
function infoForImageFile(imageFile) {
|
||||
var deferred = q.defer();
|
||||
|
@ -48,39 +52,102 @@ function infoForImageFile(imageFile) {
|
|||
return deferred.promise;
|
||||
}
|
||||
|
||||
function sendContentToRoom(file, roomId, matrixClient) {
|
||||
var content = {
|
||||
body: file.name,
|
||||
info: {
|
||||
size: file.size,
|
||||
class ContentMessages {
|
||||
constructor() {
|
||||
this.inprogress = [];
|
||||
this.nextId = 0;
|
||||
}
|
||||
|
||||
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
|
||||
if (file.type) {
|
||||
content.info.mimetype = file.type;
|
||||
}
|
||||
|
||||
var def = q.defer();
|
||||
if (file.type.indexOf('image/') == 0) {
|
||||
content.msgtype = 'm.image';
|
||||
infoForImageFile(file).then(function(imageInfo) {
|
||||
extend(content.info, imageInfo);
|
||||
var def = q.defer();
|
||||
if (file.type.indexOf('image/') == 0) {
|
||||
content.msgtype = 'm.image';
|
||||
infoForImageFile(file).then(function(imageInfo) {
|
||||
extend(content.info, imageInfo);
|
||||
def.resolve();
|
||||
});
|
||||
} else {
|
||||
content.msgtype = 'm.file';
|
||||
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});
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Upload Failed",
|
||||
description: "The file '"+upload.fileName+"' failed to upload."
|
||||
});
|
||||
}).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() {
|
||||
return matrixClient.uploadContent(file);
|
||||
}).then(function(url) {
|
||||
content.url = url;
|
||||
return matrixClient.sendMessage(roomId, content);
|
||||
});
|
||||
getCurrentUploads() {
|
||||
return this.inprogress;
|
||||
}
|
||||
|
||||
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 = {
|
||||
sendContentToRoom: sendContentToRoom
|
||||
};
|
||||
if (global.mx_ContentMessage === undefined) {
|
||||
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.MatrixChat'] = require('./components/structures/MatrixChat');
|
||||
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['views.avatars.MemberAvatar'] = require('./components/views/avatars/MemberAvatar');
|
||||
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 q = require("q");
|
||||
var classNames = require("classnames");
|
||||
var filesize = require('filesize');
|
||||
var Matrix = require("matrix-js-sdk");
|
||||
|
||||
var MatrixClientPeg = require("../../MatrixClientPeg");
|
||||
|
@ -107,6 +106,9 @@ module.exports = React.createClass({
|
|||
this.forceUpdate();
|
||||
break;
|
||||
case 'notifier_enabled':
|
||||
case 'upload_failed':
|
||||
case 'upload_started':
|
||||
case 'upload_finished':
|
||||
this.forceUpdate();
|
||||
break;
|
||||
case 'call_state':
|
||||
|
@ -408,30 +410,10 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
uploadFile: function(file) {
|
||||
this.setState({
|
||||
upload: {
|
||||
fileName: file.name,
|
||||
uploadedBytes: 0,
|
||||
totalBytes: file.size
|
||||
}
|
||||
});
|
||||
var self = this;
|
||||
ContentMessages.sendContentToRoom(
|
||||
file, this.props.roomId, MatrixClientPeg.get()
|
||||
).progress(function(ev) {
|
||||
//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) {
|
||||
).done(undefined, function(error) {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to upload file",
|
||||
|
@ -894,28 +876,9 @@ module.exports = React.createClass({
|
|||
// fileName: "testing_fooble.jpg",
|
||||
// }
|
||||
|
||||
if (this.state.upload) {
|
||||
var innerProgressStyle = {
|
||||
width: ((this.state.upload.uploadedBytes / this.state.upload.totalBytes) * 100) + '%'
|
||||
};
|
||||
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>
|
||||
);
|
||||
if (ContentMessages.getCurrentUploads().length > 0) {
|
||||
var UploadBar = sdk.getComponent('structures.UploadBar');
|
||||
statusBar = <UploadBar room={this.state.room} />
|
||||
} else {
|
||||
var typingString = this.getWhoIsTypingString();
|
||||
//typingString = "Testing 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