diff --git a/src/ContentMessages.js b/src/ContentMessages.js
index fd21977108..f2bbdfafe5 100644
--- a/src/ContentMessages.js
+++ b/src/ContentMessages.js
@@ -377,9 +377,9 @@ class ContentMessages {
}
}
if (error) {
- dis.dispatch({action: 'upload_failed', upload: upload});
+ dis.dispatch({action: 'upload_failed', upload, error});
} else {
- dis.dispatch({action: 'upload_finished', upload: upload});
+ dis.dispatch({action: 'upload_finished', upload});
}
});
}
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index dd57bd7636..934031e98d 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -27,6 +27,7 @@ const React = require("react");
const ReactDOM = require("react-dom");
import PropTypes from 'prop-types';
import Promise from 'bluebird';
+import filesize from 'filesize';
const classNames = require("classnames");
import { _t } from '../../languageHandler';
@@ -101,6 +102,10 @@ module.exports = React.createClass({
roomLoading: true,
peekLoading: false,
shouldPeek: true,
+
+ // Media limits for uploading.
+ mediaConfig: undefined,
+
// used to trigger a rerender in TimelinePanel once the members are loaded,
// so RR are rendered again (now with the members available), ...
membersLoaded: !llMembers,
@@ -156,7 +161,7 @@ module.exports = React.createClass({
MatrixClientPeg.get().on("RoomState.members", this.onRoomStateMember);
MatrixClientPeg.get().on("Room.myMembership", this.onMyMembership);
MatrixClientPeg.get().on("accountData", this.onAccountData);
-
+ this._fetchMediaConfig();
// Start listening for RoomViewStore updates
this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate);
this._onRoomViewStoreUpdate(true);
@@ -164,6 +169,27 @@ module.exports = React.createClass({
WidgetEchoStore.on('update', this._onWidgetEchoStoreUpdate);
},
+ _fetchMediaConfig: function(invalidateCache: boolean = false) {
+ /// NOTE: Using global here so we don't make repeated requests for the
+ /// config every time we swap room.
+ if(global.mediaConfig !== undefined && !invalidateCache) {
+ this.setState({mediaConfig: global.mediaConfig});
+ return;
+ }
+ console.log("[Media Config] Fetching");
+ MatrixClientPeg.get().getMediaConfig().then((config) => {
+ console.log("[Media Config] Fetched config:", config);
+ return config;
+ }).catch(() => {
+ // Media repo can't or won't report limits, so provide an empty object (no limits).
+ console.log("[Media Config] Could not fetch config, so not limiting uploads.");
+ return {};
+ }).then((config) => {
+ global.mediaConfig = config;
+ this.setState({mediaConfig: config});
+ });
+ },
+
_onRoomViewStoreUpdate: function(initial) {
if (this.unmounted) {
return;
@@ -499,6 +525,10 @@ module.exports = React.createClass({
break;
case 'notifier_enabled':
case 'upload_failed':
+ // 413: File was too big or upset the server in some way.
+ if(payload.error.http_status === 413) {
+ this._fetchMediaConfig(true);
+ }
case 'upload_started':
case 'upload_finished':
this.forceUpdate();
@@ -931,6 +961,15 @@ module.exports = React.createClass({
this.setState({ draggingFile: false });
},
+ isFileUploadAllowed(file) {
+ if (this.state.mediaConfig !== undefined &&
+ this.state.mediaConfig["m.upload.size"] !== undefined &&
+ file.size > this.state.mediaConfig["m.upload.size"]) {
+ return _t("File is too big. Maximum file size is %(fileSize)s", {fileSize: filesize(this.state.mediaConfig["m.upload.size"])});
+ }
+ return true;
+ },
+
uploadFile: async function(file) {
dis.dispatch({action: 'focus_composer'});
@@ -1687,6 +1726,7 @@ module.exports = React.createClass({
callState={this.state.callState}
disabled={this.props.disabled}
showApps={this.state.showApps}
+ uploadAllowed={this.isFileUploadAllowed}
/>;
}
diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js
index 8df6a76836..3fa0f888df 100644
--- a/src/components/views/rooms/MessageComposer.js
+++ b/src/components/views/rooms/MessageComposer.js
@@ -139,7 +139,8 @@ export default class MessageComposer extends React.Component {
}
onUploadFileSelected(files) {
- this.uploadFiles(files.target.files);
+ const tfiles = files.target.files;
+ this.uploadFiles(tfiles);
}
uploadFiles(files) {
@@ -147,10 +148,21 @@ export default class MessageComposer extends React.Component {
const TintableSvg = sdk.getComponent("elements.TintableSvg");
const fileList = [];
+ const acceptedFiles = [];
+ const failedFiles = [];
+
for (let i=0; i { _t('Reason') + ": " + fileAcceptedOrError}
{ _t('Are you sure you want to upload the following files?') }
+{ _t('The following files cannot be uploaded:') }
+{ _t('Are you sure you want to upload the following files?') }
-