Merge remote-tracking branch 'origin/develop' into develop

This commit is contained in:
Weblate 2018-12-04 00:31:21 +00:00
commit 35ef2cde61
4 changed files with 102 additions and 16 deletions

View file

@ -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});
}
});
}

View file

@ -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}
/>;
}

View file

@ -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<files.length; i++) {
fileList.push(<li key={i}>
<TintableSvg key={i} src="img/files.svg" width="16" height="16" /> { files[i].name || _t('Attachment') }
</li>);
const fileAcceptedOrError = this.props.uploadAllowed(files[i]);
if (fileAcceptedOrError === true) {
acceptedFiles.push(<li key={i}>
<TintableSvg key={i} src="img/files.svg" width="16" height="16" /> { files[i].name || _t('Attachment') }
</li>);
fileList.push(files[i]);
} else {
failedFiles.push(<li key={i}>
<TintableSvg key={i} src="img/files.svg" width="16" height="16" /> { files[i].name || _t('Attachment') } <p>{ _t('Reason') + ": " + fileAcceptedOrError}</p>
</li>);
}
}
const isQuoting = Boolean(RoomViewStore.getQuotingEvent());
@ -161,23 +173,47 @@ export default class MessageComposer extends React.Component {
}</p>;
}
const acceptedFilesPart = acceptedFiles.length === 0 ? null : (
<div>
<p>{ _t('Are you sure you want to upload the following files?') }</p>
<ul style={{listStyle: 'none', textAlign: 'left'}}>
{ acceptedFiles }
</ul>
</div>
);
const failedFilesPart = failedFiles.length === 0 ? null : (
<div>
<p>{ _t('The following files cannot be uploaded:') }</p>
<ul style={{listStyle: 'none', textAlign: 'left'}}>
{ failedFiles }
</ul>
</div>
);
let buttonText;
if (acceptedFiles.length > 0 && failedFiles.length > 0) {
buttonText = "Upload selected"
} else if (failedFiles.length > 0) {
buttonText = "Close"
}
Modal.createTrackedDialog('Upload Files confirmation', '', QuestionDialog, {
title: _t('Upload Files'),
description: (
<div>
<p>{ _t('Are you sure you want to upload the following files?') }</p>
<ul style={{listStyle: 'none', textAlign: 'left'}}>
{ fileList }
</ul>
{ acceptedFilesPart }
{ failedFilesPart }
{ replyToWarning }
</div>
),
hasCancelButton: acceptedFiles.length > 0,
button: buttonText,
onFinished: (shouldUpload) => {
if (shouldUpload) {
// MessageComposer shouldn't have to rely on its parent passing in a callback to upload a file
if (files) {
for (let i=0; i<files.length; i++) {
this.props.uploadFile(files[i]);
if (fileList) {
for (let i=0; i<fileList.length; i++) {
this.props.uploadFile(fileList[i]);
}
}
}
@ -459,6 +495,9 @@ MessageComposer.propTypes = {
// callback when a file to upload is chosen
uploadFile: PropTypes.func.isRequired,
// function to test whether a file should be allowed to be uploaded.
uploadAllowed: PropTypes.func.isRequired,
// string representing the current room app drawer state
showApps: PropTypes.bool,
showApps: PropTypes.bool
};

View file

@ -1351,5 +1351,12 @@
"Import": "Import",
"Failed to set direct chat tag": "Failed to set direct chat tag",
"Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room",
"Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room"
"Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room",
"File is too big. Maximum file size is %(fileSize)s": "File is too big. Maximum file size is %(fileSize)s",
"Reason": "Reason",
"The following files cannot be uploaded:": "The following files cannot be uploaded:",
"You've previously used Riot on %(host)s with lazy loading of members enabled. In this version lazy loading is disabled. As the local cache is not compatible between these two settings, Riot needs to resync your account.": "You've previously used Riot on %(host)s with lazy loading of members enabled. In this version lazy loading is disabled. As the local cache is not compatible between these two settings, Riot needs to resync your account.",
"If the other version of Riot is still open in another tab, please close it as using Riot on the same host with both lazy loading enabled and disabled simultaneously will cause issues.": "If the other version of Riot is still open in another tab, please close it as using Riot on the same host with both lazy loading enabled and disabled simultaneously will cause issues.",
"Incompatible local cache": "Incompatible local cache",
"Clear cache and resync": "Clear cache and resync"
}