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

This commit is contained in:
Weblate 2018-02-06 17:50:58 +00:00
commit 0601d65af7
7 changed files with 148 additions and 15 deletions

View file

@ -34,7 +34,14 @@ export function getRoomNotifsState(roomId) {
} }
// for everything else, look at the room rule. // for everything else, look at the room rule.
const roomRule = MatrixClientPeg.get().getRoomPushRule('global', roomId); let roomRule = null;
try {
roomRule = MatrixClientPeg.get().getRoomPushRule('global', roomId);
} catch (err) {
// Possible that the client doesn't have pushRules yet. If so, it
// hasn't started eiher, so indicate that this room is not notifying.
return null;
}
// XXX: We have to assume the default is to notify for all messages // XXX: We have to assume the default is to notify for all messages
// (in particular this will be 'wrong' for one to one rooms because // (in particular this will be 'wrong' for one to one rooms because
@ -130,6 +137,11 @@ function setRoomNotifsStateUnmuted(roomId, newState) {
} }
function findOverrideMuteRule(roomId) { function findOverrideMuteRule(roomId) {
if (!MatrixClientPeg.get().pushRules ||
!MatrixClientPeg.get().pushRules['global'] ||
!MatrixClientPeg.get().pushRules['global'].override) {
return null;
}
for (const rule of MatrixClientPeg.get().pushRules['global'].override) { for (const rule of MatrixClientPeg.get().pushRules['global'].override) {
if (isRuleForRoom(roomId, rule)) { if (isRuleForRoom(roomId, rule)) {
if (isMuteRule(rule) && rule.enabled) { if (isMuteRule(rule) && rule.enabled) {

View file

@ -44,6 +44,7 @@ const TagPanel = React.createClass({
componentWillMount: function() { componentWillMount: function() {
this.unmounted = false; this.unmounted = false;
this.context.matrixClient.on("Group.myMembership", this._onGroupMyMembership); this.context.matrixClient.on("Group.myMembership", this._onGroupMyMembership);
this.context.matrixClient.on("sync", this.onClientSync);
this._tagOrderStoreToken = TagOrderStore.addListener(() => { this._tagOrderStoreToken = TagOrderStore.addListener(() => {
if (this.unmounted) { if (this.unmounted) {
@ -61,6 +62,7 @@ const TagPanel = React.createClass({
componentWillUnmount() { componentWillUnmount() {
this.unmounted = true; this.unmounted = true;
this.context.matrixClient.removeListener("Group.myMembership", this._onGroupMyMembership); this.context.matrixClient.removeListener("Group.myMembership", this._onGroupMyMembership);
this.context.matrixClient.removeListener("sync", this.onClientSync);
if (this._filterStoreToken) { if (this._filterStoreToken) {
this._filterStoreToken.remove(); this._filterStoreToken.remove();
} }
@ -71,6 +73,16 @@ const TagPanel = React.createClass({
dis.dispatch(GroupActions.fetchJoinedGroups(this.context.matrixClient)); dis.dispatch(GroupActions.fetchJoinedGroups(this.context.matrixClient));
}, },
onClientSync(syncState, prevState) {
// 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) {
// Load joined groups
dis.dispatch(GroupActions.fetchJoinedGroups(this.context.matrixClient));
}
},
onClick(e) { onClick(e) {
// Ignore clicks on children // Ignore clicks on children
if (e.target !== e.currentTarget) return; if (e.target !== e.currentTarget) return;

View file

@ -16,6 +16,7 @@ limitations under the License.
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { MatrixClient } from 'matrix-js-sdk';
import AvatarLogic from '../../../Avatar'; import AvatarLogic from '../../../Avatar';
import sdk from '../../../index'; import sdk from '../../../index';
import AccessibleButton from '../elements/AccessibleButton'; import AccessibleButton from '../elements/AccessibleButton';
@ -36,6 +37,10 @@ module.exports = React.createClass({
defaultToInitialLetter: PropTypes.bool, // true to add default url defaultToInitialLetter: PropTypes.bool, // true to add default url
}, },
contextTypes: {
matrixClient: PropTypes.instanceOf(MatrixClient),
},
getDefaultProps: function() { getDefaultProps: function() {
return { return {
width: 40, width: 40,
@ -49,6 +54,16 @@ module.exports = React.createClass({
return this._getState(this.props); return this._getState(this.props);
}, },
componentWillMount() {
this.unmounted = false;
this.context.matrixClient.on('sync', this.onClientSync);
},
componentWillUnmount() {
this.unmounted = true;
this.context.matrixClient.removeListener('sync', this.onClientSync);
},
componentWillReceiveProps: function(nextProps) { componentWillReceiveProps: function(nextProps) {
// work out if we need to call setState (if the image URLs array has changed) // work out if we need to call setState (if the image URLs array has changed)
const newState = this._getState(nextProps); const newState = this._getState(nextProps);
@ -67,6 +82,23 @@ module.exports = React.createClass({
} }
}, },
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 &&
// Did we fall back?
this.state.urlsIndex > 0
) {
// Start from the highest priority URL again
this.setState({
urlsIndex: 0,
});
}
},
_getState: function(props) { _getState: function(props) {
// work out the full set of urls to try to load. This is formed like so: // work out the full set of urls to try to load. This is formed like so:
// imageUrls: [ props.url, props.urls, default image ] // imageUrls: [ props.url, props.urls, default image ]

View file

@ -17,7 +17,7 @@ import React from 'react';
import sdk from '../../../index'; import sdk from '../../../index';
import dis from '../../../dispatcher'; import dis from '../../../dispatcher';
import classNames from 'classnames'; import classNames from 'classnames';
import { Room, RoomMember } from 'matrix-js-sdk'; import { Room, RoomMember, MatrixClient } from 'matrix-js-sdk';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import MatrixClientPeg from '../../../MatrixClientPeg'; import MatrixClientPeg from '../../../MatrixClientPeg';
import { MATRIXTO_URL_PATTERN } from '../../../linkify-matrix'; import { MATRIXTO_URL_PATTERN } from '../../../linkify-matrix';
@ -61,6 +61,17 @@ const Pill = React.createClass({
shouldShowPillAvatar: PropTypes.bool, shouldShowPillAvatar: PropTypes.bool,
}, },
childContextTypes: {
matrixClient: PropTypes.instanceOf(MatrixClient),
},
getChildContext() {
return {
matrixClient: this._matrixClient,
};
},
getInitialState() { getInitialState() {
return { return {
// ID/alias of the room/user // ID/alias of the room/user
@ -135,6 +146,7 @@ const Pill = React.createClass({
componentWillMount() { componentWillMount() {
this._unmounted = false; this._unmounted = false;
this._matrixClient = MatrixClientPeg.get();
this.componentWillReceiveProps(this.props); this.componentWillReceiveProps(this.props);
}, },

View file

@ -18,8 +18,9 @@ limitations under the License.
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { MatrixClient } from 'matrix-js-sdk';
import MFileBody from './MFileBody'; import MFileBody from './MFileBody';
import MatrixClientPeg from '../../../MatrixClientPeg';
import ImageUtils from '../../../ImageUtils'; import ImageUtils from '../../../ImageUtils';
import Modal from '../../../Modal'; import Modal from '../../../Modal';
import sdk from '../../../index'; import sdk from '../../../index';
@ -40,15 +41,37 @@ module.exports = React.createClass({
onWidgetLoad: PropTypes.func.isRequired, onWidgetLoad: PropTypes.func.isRequired,
}, },
contextTypes: {
matrixClient: PropTypes.instanceOf(MatrixClient),
},
getInitialState: function() { getInitialState: function() {
return { return {
decryptedUrl: null, decryptedUrl: null,
decryptedThumbnailUrl: null, decryptedThumbnailUrl: null,
decryptedBlob: null, decryptedBlob: null,
error: 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) { onClick: function onClick(ev) {
if (ev.button == 0 && !ev.metaKey) { if (ev.button == 0 && !ev.metaKey) {
@ -97,12 +120,18 @@ module.exports = React.createClass({
imgElement.src = this._getThumbUrl(); imgElement.src = this._getThumbUrl();
}, },
onImageError: function() {
this.setState({
imgError: true,
});
},
_getContentUrl: function() { _getContentUrl: function() {
const content = this.props.mxEvent.getContent(); const content = this.props.mxEvent.getContent();
if (content.file !== undefined) { if (content.file !== undefined) {
return this.state.decryptedUrl; return this.state.decryptedUrl;
} else { } else {
return MatrixClientPeg.get().mxcUrlToHttp(content.url); return this.context.matrixClient.mxcUrlToHttp(content.url);
} }
}, },
@ -115,7 +144,7 @@ module.exports = React.createClass({
} }
return this.state.decryptedUrl; return this.state.decryptedUrl;
} else { } else {
return MatrixClientPeg.get().mxcUrlToHttp(content.url, 800, 600); return this.context.matrixClient.mxcUrlToHttp(content.url, 800, 600);
} }
}, },
@ -156,7 +185,9 @@ module.exports = React.createClass({
}, },
componentWillUnmount: function() { componentWillUnmount: function() {
this.unmounted = true;
dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
this.context.matrixClient.removeListener('sync', this.onClientSync);
}, },
onAction: function(payload) { onAction: function(payload) {
@ -217,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(); const contentUrl = this._getContentUrl();
let thumbUrl; let thumbUrl;
if (this._isGif() && SettingsStore.getValue("autoplayGifsAndVideos")) { if (this._isGif() && SettingsStore.getValue("autoplayGifsAndVideos")) {
@ -231,6 +270,7 @@ module.exports = React.createClass({
<a href={contentUrl} onClick={this.onClick}> <a href={contentUrl} onClick={this.onClick}>
<img className="mx_MImageBody_thumbnail" src={thumbUrl} ref="image" <img className="mx_MImageBody_thumbnail" src={thumbUrl} ref="image"
alt={content.body} alt={content.body}
onError={this.onImageError}
onLoad={this.props.onWidgetLoad} onLoad={this.props.onWidgetLoad}
onMouseEnter={this.onImageEnter} onMouseEnter={this.onImageEnter}
onMouseLeave={this.onImageLeave} /> onMouseLeave={this.onImageLeave} />

View file

@ -1,12 +1,15 @@
const expect = require('expect'); import expect from 'expect';
const React = require('react'); import React from 'react';
const ReactDOM = require("react-dom"); import ReactTestUtils from 'react-addons-test-utils';
const ReactTestUtils = require('react-addons-test-utils'); import sdk from 'matrix-react-sdk';
const sdk = require('matrix-react-sdk');
const MemberEventListSummary = sdk.getComponent('views.elements.MemberEventListSummary');
import * as languageHandler from '../../../../src/languageHandler'; import * as languageHandler from '../../../../src/languageHandler';
import * as testUtils from '../../../test-utils';
// Give MELS a matrixClient in its child context
const MemberEventListSummary = testUtils.wrapInMatrixClientContext(
sdk.getComponent('views.elements.MemberEventListSummary'),
);
const testUtils = require('../../../test-utils');
describe('MemberEventListSummary', function() { describe('MemberEventListSummary', function() {
let sandbox; let sandbox;
@ -113,7 +116,6 @@ describe('MemberEventListSummary', function() {
renderer.render(<MemberEventListSummary {...props} />); renderer.render(<MemberEventListSummary {...props} />);
const result = renderer.getRenderOutput(); const result = renderer.getRenderOutput();
expect(result.type).toBe('div');
expect(result.props.children).toEqual([ expect(result.props.children).toEqual([
<div className="event_tile" key="event0">Expanded membership</div>, <div className="event_tile" key="event0">Expanded membership</div>,
]); ]);
@ -136,7 +138,6 @@ describe('MemberEventListSummary', function() {
renderer.render(<MemberEventListSummary {...props} />); renderer.render(<MemberEventListSummary {...props} />);
const result = renderer.getRenderOutput(); const result = renderer.getRenderOutput();
expect(result.type).toBe('div');
expect(result.props.children).toEqual([ expect(result.props.children).toEqual([
<div className="event_tile" key="event0">Expanded membership</div>, <div className="event_tile" key="event0">Expanded membership</div>,
<div className="event_tile" key="event1">Expanded membership</div>, <div className="event_tile" key="event1">Expanded membership</div>,

View file

@ -2,7 +2,8 @@
import sinon from 'sinon'; import sinon from 'sinon';
import Promise from 'bluebird'; import Promise from 'bluebird';
import React from 'react';
import PropTypes from 'prop-types';
import peg from '../src/MatrixClientPeg'; import peg from '../src/MatrixClientPeg';
import dis from '../src/dispatcher'; import dis from '../src/dispatcher';
import jssdk from 'matrix-js-sdk'; import jssdk from 'matrix-js-sdk';
@ -265,3 +266,26 @@ export function getDispatchForStore(store) {
dis._isDispatching = false; dis._isDispatching = false;
}; };
} }
export function wrapInMatrixClientContext(WrappedComponent) {
class Wrapper extends React.Component {
static childContextTypes = {
matrixClient: PropTypes.object,
}
getChildContext() {
return {
matrixClient: this._matrixClient,
};
}
componentWillMount() {
this._matrixClient = peg.get();
}
render() {
return <WrappedComponent {...this.props} />;
}
}
return Wrapper;
}