mirror of https://github.com/vector-im/riot-web
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.
parent
aceb85130e
commit
e8f5131ed6
|
@ -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...";
|
||||
|
|
|
@ -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…
Reference in New Issue