Merge branch 'develop' of https://github.com/matrix-org/matrix-react-sdk into rxl881/snapshot

This commit is contained in:
Richard Lewis 2018-02-23 15:37:33 +00:00
commit b2bf4d4709
219 changed files with 9309 additions and 3694 deletions

View file

@ -17,6 +17,7 @@ limitations under the License.
'use strict';
import React from 'react';
import PropTypes from 'prop-types';
import filesize from 'filesize';
import MatrixClientPeg from '../../../MatrixClientPeg';
import sdk from '../../../index';
@ -81,7 +82,7 @@ Tinter.registerTintable(updateTintedDownloadImage);
// downloaded. This limit does not seem to apply when the url is used as
// the source attribute of an image tag.
//
// Blob URLs are generated using window.URL.createObjectURL and unforuntately
// Blob URLs are generated using window.URL.createObjectURL and unfortunately
// for our purposes they inherit the origin of the page that created them.
// This means that any scripts that run when the URL is viewed will be able
// to access local storage.
@ -191,7 +192,7 @@ module.exports = React.createClass({
},
contextTypes: {
appConfig: React.PropTypes.object,
appConfig: PropTypes.object,
},
/**
@ -294,7 +295,7 @@ module.exports = React.createClass({
return (
<span className="mx_MFileBody" ref="body">
<div className="mx_MImageBody_download">
<div className="mx_MFileBody_download">
<a href="javascript:void(0)" onClick={decrypt}>
{ _t("Decrypt %(text)s", { text: text }) }
</a>
@ -326,7 +327,7 @@ module.exports = React.createClass({
}
return (
<span className="mx_MFileBody">
<div className="mx_MImageBody_download">
<div className="mx_MFileBody_download">
<div style={{display: "none"}}>
{ /*
* Add dummy copy of the "a" tag
@ -346,7 +347,7 @@ module.exports = React.createClass({
if (this.props.tileShape === "file_grid") {
return (
<span className="mx_MFileBody">
<div className="mx_MImageBody_download">
<div className="mx_MFileBody_download">
<a className="mx_ImageBody_downloadLink" href={contentUrl} download={fileName} target="_blank">
{ fileName }
</a>
@ -359,7 +360,7 @@ module.exports = React.createClass({
} else {
return (
<span className="mx_MFileBody">
<div className="mx_MImageBody_download">
<div className="mx_MFileBody_download">
<a href={contentUrl} download={fileName} target="_blank" rel="noopener">
<img src={tintedDownloadImageURL} width="12" height="14" ref="downloadImage" />
{ _t("Download %(text)s", { text: text }) }

View file

@ -17,8 +17,10 @@ limitations under the License.
'use strict';
import React from 'react';
import PropTypes from 'prop-types';
import { MatrixClient } from 'matrix-js-sdk';
import MFileBody from './MFileBody';
import MatrixClientPeg from '../../../MatrixClientPeg';
import ImageUtils from '../../../ImageUtils';
import Modal from '../../../Modal';
import sdk from '../../../index';
@ -33,10 +35,14 @@ module.exports = React.createClass({
propTypes: {
/* the MatrixEvent to show */
mxEvent: React.PropTypes.object.isRequired,
mxEvent: PropTypes.object.isRequired,
/* called when the image has loaded */
onWidgetLoad: React.PropTypes.func.isRequired,
onWidgetLoad: PropTypes.func.isRequired,
},
contextTypes: {
matrixClient: PropTypes.instanceOf(MatrixClient),
},
getInitialState: function() {
@ -45,9 +51,27 @@ module.exports = React.createClass({
decryptedThumbnailUrl: null,
decryptedBlob: null,
error: null,
imgError: false,
};
},
componentWillMount() {
this.unmounted = false;
this.context.matrixClient.on('sync', this.onClientSync);
},
onClientSync(syncState, prevState) {
if (this.unmounted) return;
// Consider the client reconnected if there is no error with syncing.
// This means the state could be RECONNECTING, SYNCING or PREPARED.
const reconnected = syncState !== "ERROR" && prevState !== syncState;
if (reconnected && this.state.imgError) {
// Load the image again
this.setState({
imgError: false,
});
}
},
onClick: function onClick(ev) {
if (ev.button == 0 && !ev.metaKey) {
@ -96,12 +120,18 @@ module.exports = React.createClass({
imgElement.src = this._getThumbUrl();
},
onImageError: function() {
this.setState({
imgError: true,
});
},
_getContentUrl: function() {
const content = this.props.mxEvent.getContent();
if (content.file !== undefined) {
return this.state.decryptedUrl;
} else {
return MatrixClientPeg.get().mxcUrlToHttp(content.url);
return this.context.matrixClient.mxcUrlToHttp(content.url);
}
},
@ -114,7 +144,7 @@ module.exports = React.createClass({
}
return this.state.decryptedUrl;
} else {
return MatrixClientPeg.get().mxcUrlToHttp(content.url, 800, 600);
return this.context.matrixClient.mxcUrlToHttp(content.url, 800, 600);
}
},
@ -155,7 +185,9 @@ module.exports = React.createClass({
},
componentWillUnmount: function() {
this.unmounted = true;
dis.unregister(this.dispatcherRef);
this.context.matrixClient.removeListener('sync', this.onClientSync);
},
onAction: function(payload) {
@ -216,6 +248,14 @@ module.exports = React.createClass({
);
}
if (this.state.imgError) {
return (
<span className="mx_MImageBody">
{ _t("This image cannot be displayed.") }
</span>
);
}
const contentUrl = this._getContentUrl();
let thumbUrl;
if (this._isGif() && SettingsStore.getValue("autoplayGifsAndVideos")) {
@ -230,6 +270,7 @@ module.exports = React.createClass({
<a href={contentUrl} onClick={this.onClick}>
<img className="mx_MImageBody_thumbnail" src={thumbUrl} ref="image"
alt={content.body}
onError={this.onImageError}
onLoad={this.props.onWidgetLoad}
onMouseEnter={this.onImageEnter}
onMouseLeave={this.onImageLeave} />

View file

@ -17,6 +17,7 @@ limitations under the License.
'use strict';
import React from 'react';
import PropTypes from 'prop-types';
import MFileBody from './MFileBody';
import MatrixClientPeg from '../../../MatrixClientPeg';
import { decryptFile, readBlobAsDataUri } from '../../../utils/DecryptFile';
@ -29,10 +30,10 @@ module.exports = React.createClass({
propTypes: {
/* the MatrixEvent to show */
mxEvent: React.PropTypes.object.isRequired,
mxEvent: PropTypes.object.isRequired,
/* called when the video has loaded */
onWidgetLoad: React.PropTypes.func.isRequired,
onWidgetLoad: PropTypes.func.isRequired,
},
getInitialState: function() {

View file

@ -17,6 +17,7 @@ limitations under the License.
'use strict';
const React = require('react');
import PropTypes from 'prop-types';
const sdk = require('../../../index');
module.exports = React.createClass({
@ -24,22 +25,22 @@ module.exports = React.createClass({
propTypes: {
/* the MatrixEvent to show */
mxEvent: React.PropTypes.object.isRequired,
mxEvent: PropTypes.object.isRequired,
/* a list of words to highlight */
highlights: React.PropTypes.array,
highlights: PropTypes.array,
/* link URL for the highlights */
highlightLink: React.PropTypes.string,
highlightLink: PropTypes.string,
/* should show URL previews for this event */
showUrlPreview: React.PropTypes.bool,
showUrlPreview: PropTypes.bool,
/* callback called when dynamic content in events are loaded */
onWidgetLoad: React.PropTypes.func,
onWidgetLoad: PropTypes.func,
/* the shsape of the tile, used */
tileShape: React.PropTypes.string,
tileShape: PropTypes.string,
},
getEventTileOps: function() {

View file

@ -15,6 +15,7 @@ limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import MatrixClientPeg from '../../../MatrixClientPeg';
import { ContentRepo } from 'matrix-js-sdk';
import { _t } from '../../../languageHandler';
@ -27,7 +28,7 @@ module.exports = React.createClass({
propTypes: {
/* the MatrixEvent to show */
mxEvent: React.PropTypes.object.isRequired,
mxEvent: PropTypes.object.isRequired,
},
onAvatarClick: function(name) {

View file

@ -1,5 +1,6 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -18,6 +19,7 @@ limitations under the License.
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import highlight from 'highlight.js';
import * as HtmlUtils from '../../../HtmlUtils';
import * as linkify from 'linkifyjs';
@ -31,8 +33,6 @@ import dis from '../../../dispatcher';
import { _t } from '../../../languageHandler';
import MatrixClientPeg from '../../../MatrixClientPeg';
import ContextualMenu from '../../structures/ContextualMenu';
import {RoomMember} from 'matrix-js-sdk';
import classNames from 'classnames';
import SettingsStore from "../../../settings/SettingsStore";
import PushProcessor from 'matrix-js-sdk/lib/pushprocessor';
@ -43,19 +43,26 @@ module.exports = React.createClass({
propTypes: {
/* the MatrixEvent to show */
mxEvent: React.PropTypes.object.isRequired,
mxEvent: PropTypes.object.isRequired,
/* a list of words to highlight */
highlights: React.PropTypes.array,
highlights: PropTypes.array,
/* link URL for the highlights */
highlightLink: React.PropTypes.string,
highlightLink: PropTypes.string,
/* should show URL previews for this event */
showUrlPreview: React.PropTypes.bool,
showUrlPreview: PropTypes.bool,
/* callback for when our widget has loaded */
onWidgetLoad: React.PropTypes.func,
onWidgetLoad: PropTypes.func,
/* the shape of the tile, used */
tileShape: PropTypes.string,
},
contextTypes: {
addRichQuote: PropTypes.func,
},
getInitialState: function() {
@ -179,6 +186,7 @@ module.exports = React.createClass({
// If the link is a (localised) matrix.to link, replace it with a pill
const Pill = sdk.getComponent('elements.Pill');
const Quote = sdk.getComponent('elements.Quote');
if (Pill.isMessagePillUrl(href)) {
const pillContainer = document.createElement('span');
@ -197,6 +205,21 @@ module.exports = React.createClass({
// update the current node with one that's now taken its place
node = pillContainer;
} else if (SettingsStore.isFeatureEnabled("feature_rich_quoting") && Quote.isMessageUrl(href)) {
if (this.context.addRichQuote) { // We're already a Rich Quote so just append the next one above
this.context.addRichQuote(href);
node.remove();
} else { // We're the first in the chain
const quoteContainer = document.createElement('span');
const quote =
<Quote url={href} parentEv={this.props.mxEvent} node={node} />;
ReactDOM.render(quote, quoteContainer);
node.parentNode.replaceChild(quoteContainer, node);
node = quoteContainer;
}
pillified = true;
}
} else if (node.nodeType == Node.TEXT_NODE) {
const Pill = sdk.getComponent('elements.Pill');

View file

@ -17,6 +17,7 @@ limitations under the License.
'use strict';
const React = require('react');
import PropTypes from 'prop-types';
const TextForEvent = require('../../../TextForEvent');
import sdk from '../../../index';
@ -26,7 +27,7 @@ module.exports = React.createClass({
propTypes: {
/* the MatrixEvent to show */
mxEvent: React.PropTypes.object.isRequired,
mxEvent: PropTypes.object.isRequired,
},
render: function() {