Merge branch 'develop' of github.com:vector-im/riot-web into t3chguy/fix_typecheck_warn

This commit is contained in:
Michael Telatynski 2017-10-30 15:27:20 +00:00
commit c39044f397
No known key found for this signature in database
GPG Key ID: 3F879DA5AD802A5E
9 changed files with 244 additions and 208 deletions

View File

@ -6,8 +6,8 @@
"integrations_rest_url": "https://scalar.vector.im/api", "integrations_rest_url": "https://scalar.vector.im/api",
"bug_report_endpoint_url": "https://riot.im/bugreports/submit", "bug_report_endpoint_url": "https://riot.im/bugreports/submit",
"features": { "features": {
"feature_groups": "labs" "feature_groups": "labs",
"feature_pinning": "labs", "feature_pinning": "labs"
}, },
"default_federate": true, "default_federate": true,
"roomDirectory": { "roomDirectory": {

View File

@ -19,6 +19,7 @@ limitations under the License.
import React from 'react'; import React from 'react';
import { DragDropContext } from 'react-dnd'; import { DragDropContext } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend'; import HTML5Backend from 'react-dnd-html5-backend';
import classNames from 'classnames';
import KeyCode from 'matrix-react-sdk/lib/KeyCode'; import KeyCode from 'matrix-react-sdk/lib/KeyCode';
import sdk from 'matrix-react-sdk'; import sdk from 'matrix-react-sdk';
import dis from 'matrix-react-sdk/lib/dispatcher'; import dis from 'matrix-react-sdk/lib/dispatcher';
@ -55,7 +56,7 @@ var LeftPanel = React.createClass({
// We just need to update if any of these things change. // We just need to update if any of these things change.
if ( if (
this.props.collapsed !== nextProps.collapsed || this.props.collapsed !== nextProps.collapsed ||
this.props.opacity !== nextProps.opacity this.props.disabled !== nextProps.disabled
) { ) {
return true; return true;
} }
@ -176,14 +177,16 @@ var LeftPanel = React.createClass({
topBox = <SearchBox collapsed={ this.props.collapsed } onSearch={ this.onSearch } />; topBox = <SearchBox collapsed={ this.props.collapsed } onSearch={ this.onSearch } />;
} }
let classes = "mx_LeftPanel mx_fadable"; let classes = classNames(
if (this.props.collapsed) { "mx_LeftPanel", "mx_fadable",
classes += " collapsed"; {
"collapsed": this.props.collapsed,
"mx_fadable_faded": this.props.disabled,
} }
);
return ( return (
<aside className={classes} style={{ opacity: this.props.opacity }} <aside className={classes} onKeyDown={ this._onKeyDown } onFocus={ this._onFocus } onBlur={ this._onBlur }>
onKeyDown={ this._onKeyDown } onFocus={ this._onFocus } onBlur={ this._onBlur }>
{ topBox } { topBox }
<CallPreview ConferenceHandler={VectorConferenceHandler} /> <CallPreview ConferenceHandler={VectorConferenceHandler} />
<RoomList <RoomList

View File

@ -18,6 +18,7 @@ limitations under the License.
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classNames from 'classnames';
import { _t } from 'matrix-react-sdk/lib/languageHandler'; import { _t } from 'matrix-react-sdk/lib/languageHandler';
import sdk from 'matrix-react-sdk'; import sdk from 'matrix-react-sdk';
import dis from 'matrix-react-sdk/lib/dispatcher'; import dis from 'matrix-react-sdk/lib/dispatcher';
@ -99,6 +100,7 @@ module.exports = React.createClass({
this.dispatcherRef = dis.register(this.onAction); this.dispatcherRef = dis.register(this.onAction);
const cli = this.context.matrixClient; const cli = this.context.matrixClient;
cli.on("RoomState.members", this.onRoomStateMember); cli.on("RoomState.members", this.onRoomStateMember);
this._initGroupStore(this.props.groupId);
}, },
componentWillUnmount: function() { componentWillUnmount: function() {
@ -106,20 +108,42 @@ module.exports = React.createClass({
if (this.context.matrixClient) { if (this.context.matrixClient) {
this.context.matrixClient.removeListener("RoomState.members", this.onRoomStateMember); this.context.matrixClient.removeListener("RoomState.members", this.onRoomStateMember);
} }
this._unregisterGroupStore();
}, },
getInitialState: function() { getInitialState: function() {
if (this.props.groupId) {
return { return {
phase: this.Phase.GroupMemberList, phase: this.props.groupId ? this.Phase.GroupMemberList : this.Phase.RoomMemberList,
}; isUserPrivilegedInGroup: null,
} else {
return {
phase: this.Phase.RoomMemberList,
};
} }
}, },
componentWillReceiveProps(newProps) {
if (newProps.groupId !== this.props.groupId) {
this._unregisterGroupStore();
this._initGroupStore(newProps.groupId);
}
},
_initGroupStore(groupId) {
this._groupStore = GroupStoreCache.getGroupStore(
this.context.matrixClient, this.props.groupId,
);
this._groupStore.registerListener(this.onGroupStoreUpdated);
},
_unregisterGroupStore() {
if (this._groupStore) {
this._groupStore.unregisterListener(this.onGroupStoreUpdated);
}
},
onGroupStoreUpdated: function(){
this.setState({
isUserPrivilegedInGroup: this._groupStore.isUserPrivileged(),
});
},
onCollapseClick: function() { onCollapseClick: function() {
dis.dispatch({ dis.dispatch({
action: 'hide_right_panel', action: 'hide_right_panel',
@ -248,6 +272,11 @@ module.exports = React.createClass({
} }
} }
const isPhaseGroup = [
this.Phase.GroupMemberInfo,
this.Phase.GroupMemberList
].includes(this.state.phase);
let headerButtons = []; let headerButtons = [];
if (this.props.roomId) { if (this.props.roomId) {
headerButtons = [ headerButtons = [
@ -271,7 +300,7 @@ module.exports = React.createClass({
} else if (this.props.groupId) { } else if (this.props.groupId) {
headerButtons = [ headerButtons = [
<HeaderButton key="_groupMembersButton" title={_t('Members')} iconSrc="img/icons-people.svg" <HeaderButton key="_groupMembersButton" title={_t('Members')} iconSrc="img/icons-people.svg"
isHighlighted={this.state.phase === this.Phase.GroupMemberList} isHighlighted={isPhaseGroup}
clickPhase={this.Phase.GroupMemberList} clickPhase={this.Phase.GroupMemberList}
analytics={['Right Panel', 'Group Member List Button', 'click']} analytics={['Right Panel', 'Group Member List Button', 'click']}
/>, />,
@ -322,13 +351,8 @@ module.exports = React.createClass({
panel = <div className="mx_RightPanel_blank"></div>; panel = <div className="mx_RightPanel_blank"></div>;
} }
if (this.props.groupId && if (this.props.groupId && this.state.isUserPrivilegedInGroup) {
GroupStoreCache.getGroupStore(this.context.matrixClient, this.props.groupId).isUserPrivileged() inviteGroup = isPhaseGroup ? (
) {
inviteGroup = [
this.Phase.GroupMemberInfo,
this.Phase.GroupMemberList,
].includes(this.state.phase) ? (
<AccessibleButton className="mx_RightPanel_invite" onClick={ this.onInviteButtonClick } > <AccessibleButton className="mx_RightPanel_invite" onClick={ this.onInviteButtonClick } >
<div className="mx_RightPanel_icon" > <div className="mx_RightPanel_icon" >
<TintableSvg src="img/icon-invite-people.svg" width="35" height="35" /> <TintableSvg src="img/icon-invite-people.svg" width="35" height="35" />
@ -345,13 +369,16 @@ module.exports = React.createClass({
); );
} }
let classes = "mx_RightPanel mx_fadable"; let classes = classNames(
if (this.props.collapsed) { "mx_RightPanel", "mx_fadable",
classes += " collapsed"; {
"collapsed": this.props.collapsed,
"mx_fadable_faded": this.props.disabled,
} }
);
return ( return (
<aside className={classes} style={{ opacity: this.props.opacity }}> <aside className={classes}>
<div className="mx_RightPanel_header"> <div className="mx_RightPanel_header">
<div className="mx_RightPanel_headerButtonGroup"> <div className="mx_RightPanel_headerButtonGroup">
{headerButtons} {headerButtons}

View File

@ -89,17 +89,17 @@ module.exports = React.createClass({
}); });
// dis.dispatch({ // dis.dispatch({
// action: 'ui_opacity', // action: 'panel_disable',
// sideOpacity: 0.3, // sideDisabled: true,
// middleOpacity: 0.3, // middleDisabled: true,
// }); // });
}, },
componentWillUnmount: function() { componentWillUnmount: function() {
// dis.dispatch({ // dis.dispatch({
// action: 'ui_opacity', // action: 'panel_disable',
// sideOpacity: 1.0, // sideDisabled: false,
// middleOpacity: 1.0, // middleDisabled: false,
// }); // });
if (this.filterTimeout) { if (this.filterTimeout) {
clearTimeout(this.filterTimeout); clearTimeout(this.filterTimeout);

View File

@ -57,7 +57,7 @@ class SendCustomEvent extends React.Component {
_buttons() { _buttons() {
return <div className="mx_Dialog_buttons"> return <div className="mx_Dialog_buttons">
<button onClick={this.onBack}>{ _t('Back') }</button> <button onClick={this.onBack}>{ _t('Back') }</button>
{!this.state.message && <button onClick={this._send}>{ _t('Send') }</button>} { !this.state.message && <button onClick={this._send}>{ _t('Send') }</button> }
</div>; </div>;
} }
@ -83,7 +83,7 @@ class SendCustomEvent extends React.Component {
} }
_additionalFields() { _additionalFields() {
return <div/>; return <div />;
} }
_onChange(e) { _onChange(e) {
@ -94,15 +94,15 @@ class SendCustomEvent extends React.Component {
if (this.state.message) { if (this.state.message) {
return <div> return <div>
<div className="mx_Dialog_content"> <div className="mx_Dialog_content">
{this.state.message} { this.state.message }
</div> </div>
{this._buttons()} { this._buttons() }
</div>; </div>;
} }
return <div> return <div>
<div className="mx_Dialog_content"> <div className="mx_Dialog_content">
{this._additionalFields()} { this._additionalFields() }
<div className="mx_TextInputDialog_label"> <div className="mx_TextInputDialog_label">
<label htmlFor="eventType"> { _t('Event Type') } </label> <label htmlFor="eventType"> { _t('Event Type') } </label>
</div> </div>
@ -117,7 +117,7 @@ class SendCustomEvent extends React.Component {
<textarea id="evContent" onChange={this._onChange} value={this.state.input_evContent} className="mx_TextInputDialog_input" cols="63" rows="5" /> <textarea id="evContent" onChange={this._onChange} value={this.state.input_evContent} className="mx_TextInputDialog_input" cols="63" rows="5" />
</div> </div>
</div> </div>
{this._buttons()} { this._buttons() }
</div>; </div>;
} }
} }
@ -223,7 +223,7 @@ class RoomStateExplorer extends React.Component {
if (this.state.event) { if (this.state.event) {
return <div className="mx_ViewSource"> return <div className="mx_ViewSource">
<div className="mx_Dialog_content"> <div className="mx_Dialog_content">
<pre>{JSON.stringify(this.state.event.event, null, 2)}</pre> <pre>{ JSON.stringify(this.state.event.event, null, 2) }</pre>
</div> </div>
<div className="mx_Dialog_buttons"> <div className="mx_Dialog_buttons">
<button onClick={this.onBack}>{ _t('Back') }</button> <button onClick={this.onBack}>{ _t('Back') }</button>
@ -237,7 +237,7 @@ class RoomStateExplorer extends React.Component {
if (this.state.eventType === null) { if (this.state.eventType === null) {
Object.keys(this.roomStateEvents).forEach((evType) => { Object.keys(this.roomStateEvents).forEach((evType) => {
// Skip this entry if does not contain search query // Skip this entry if does not contain search query
if (this.state.query && !evType.includes(this.state.query)) return; if (this.state.query && !evType.toLowerCase().includes(this.state.query.toLowerCase())) return;
const stateGroup = this.roomStateEvents[evType]; const stateGroup = this.roomStateEvents[evType];
const stateKeys = Object.keys(stateGroup); const stateKeys = Object.keys(stateGroup);
@ -258,7 +258,7 @@ class RoomStateExplorer extends React.Component {
const stateGroup = this.roomStateEvents[evType]; const stateGroup = this.roomStateEvents[evType];
Object.keys(stateGroup).forEach((stateKey) => { Object.keys(stateGroup).forEach((stateKey) => {
// Skip this entry if does not contain search query // Skip this entry if does not contain search query
if (this.state.query && !stateKey.includes(this.state.query)) return; if (this.state.query && !stateKey.toLowerCase().includes(this.state.query.toLowerCase())) return;
const ev = stateGroup[stateKey]; const ev = stateGroup[stateKey];
rows.push(<button className="mx_DevTools_RoomStateExplorer_button" key={stateKey} rows.push(<button className="mx_DevTools_RoomStateExplorer_button" key={stateKey}
@ -271,7 +271,7 @@ class RoomStateExplorer extends React.Component {
return <div> return <div>
<div className="mx_Dialog_content"> <div className="mx_Dialog_content">
<input onChange={this.onQuery} placeholder={_t('Filter results')} size="64" className="mx_TextInputDialog_input mx_DevTools_RoomStateExplorer_query" value={this.state.query} /> <input onChange={this.onQuery} placeholder={_t('Filter results')} size="64" className="mx_TextInputDialog_input mx_DevTools_RoomStateExplorer_query" value={this.state.query} />
{rows} { rows }
</div> </div>
<div className="mx_Dialog_buttons"> <div className="mx_Dialog_buttons">
<button onClick={this.onBack}>{ _t('Back') }</button> <button onClick={this.onBack}>{ _t('Back') }</button>

View File

@ -14,16 +14,19 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
'use strict'; import React from 'react';
var React = require('react');
import { _t, _tJsx } from 'matrix-react-sdk/lib/languageHandler';
import Promise from 'bluebird'; import Promise from 'bluebird';
var sdk = require('matrix-react-sdk'); import sdk from 'matrix-react-sdk';
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); import { _t, _tJsx } from 'matrix-react-sdk/lib/languageHandler';
var UserSettingsStore = require('matrix-react-sdk/lib/UserSettingsStore'); import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
var Modal = require('matrix-react-sdk/lib/Modal'); import UserSettingsStore from 'matrix-react-sdk/lib/UserSettingsStore';
import Modal from 'matrix-react-sdk/lib/Modal';
var notifications = require('../../../notifications'); import {
NotificationUtils,
VectorPushRulesDefinitions,
PushRuleVectorState,
ContentRules
} from '../../../notifications';
// TODO: this "view" component still has far too much application logic in it, // TODO: this "view" component still has far too much application logic in it,
// which should be factored out to other files. // which should be factored out to other files.
@ -31,17 +34,13 @@ var notifications = require('../../../notifications');
// TODO: this component also does a lot of direct poking into this.state, which // TODO: this component also does a lot of direct poking into this.state, which
// is VERY NAUGHTY. // is VERY NAUGHTY.
var NotificationUtils = notifications.NotificationUtils;
var VectorPushRulesDefinitions = notifications.VectorPushRulesDefinitions;
var PushRuleVectorState = notifications.PushRuleVectorState;
var ContentRules = notifications.ContentRules;
/** /**
* Rules that Vector used to set in order to override the actions of default rules. * Rules that Vector used to set in order to override the actions of default rules.
* These are used to port peoples existing overrides to match the current API. * These are used to port peoples existing overrides to match the current API.
* These can be removed and forgotten once everyone has moved to the new client. * These can be removed and forgotten once everyone has moved to the new client.
*/ */
var LEGACY_RULES = { const LEGACY_RULES = {
"im.vector.rule.contains_display_name": ".m.rule.contains_display_name", "im.vector.rule.contains_display_name": ".m.rule.contains_display_name",
"im.vector.rule.room_one_to_one": ".m.rule.room_one_to_one", "im.vector.rule.room_one_to_one": ".m.rule.room_one_to_one",
"im.vector.rule.room_message": ".m.rule.message", "im.vector.rule.room_message": ".m.rule.message",
@ -51,7 +50,7 @@ var LEGACY_RULES = {
}; };
function portLegacyActions(actions) { function portLegacyActions(actions) {
var decoded = NotificationUtils.decodeActions(actions); const decoded = NotificationUtils.decodeActions(actions);
if (decoded !== null) { if (decoded !== null) {
return NotificationUtils.encodeActions(decoded); return NotificationUtils.encodeActions(decoded);
} else { } else {
@ -62,7 +61,7 @@ function portLegacyActions(actions) {
} }
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'Notififications', displayName: 'Notifications',
phases: { phases: {
LOADING: "LOADING", // The component is loading or sending data to the hs LOADING: "LOADING", // The component is loading or sending data to the hs
@ -102,7 +101,7 @@ module.exports = React.createClass({
}, },
onEnableNotificationsChange: function(event) { onEnableNotificationsChange: function(event) {
var self = this; const self = this;
this.setState({ this.setState({
phase: this.phases.LOADING phase: this.phases.LOADING
}); });
@ -122,20 +121,20 @@ module.exports = React.createClass({
}, },
onEnableEmailNotificationsChange: function(address, event) { onEnableEmailNotificationsChange: function(address, event) {
var emailPusherPromise; let emailPusherPromise;
if (event.target.checked) { if (event.target.checked) {
var data = {} const data = {}
data['brand'] = this.props.brand || 'Riot'; data['brand'] = this.props.brand || 'Riot';
emailPusherPromise = UserSettingsStore.addEmailPusher(address, data); emailPusherPromise = UserSettingsStore.addEmailPusher(address, data);
} else { } else {
var emailPusher = UserSettingsStore.getEmailPusher(this.state.pushers, address); const emailPusher = UserSettingsStore.getEmailPusher(this.state.pushers, address);
emailPusher.kind = null; emailPusher.kind = null;
emailPusherPromise = MatrixClientPeg.get().setPusher(emailPusher); emailPusherPromise = MatrixClientPeg.get().setPusher(emailPusher);
} }
emailPusherPromise.done(() => { emailPusherPromise.done(() => {
this._refreshFromServer(); this._refreshFromServer();
}, (error) => { }, (error) => {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Error saving email notification preferences', '', ErrorDialog, { Modal.createTrackedDialog('Error saving email notification preferences', '', ErrorDialog, {
title: _t('Error saving email notification preferences'), title: _t('Error saving email notification preferences'),
description: _t('An error occurred whilst saving your email notification preferences.'), description: _t('An error occurred whilst saving your email notification preferences.'),
@ -145,14 +144,14 @@ module.exports = React.createClass({
onNotifStateButtonClicked: function(event) { onNotifStateButtonClicked: function(event) {
// FIXME: use .bind() rather than className metadata here surely // FIXME: use .bind() rather than className metadata here surely
var vectorRuleId = event.target.className.split("-")[0]; const vectorRuleId = event.target.className.split("-")[0];
var newPushRuleVectorState = event.target.className.split("-")[1]; const newPushRuleVectorState = event.target.className.split("-")[1];
if ("_keywords" === vectorRuleId) { if ("_keywords" === vectorRuleId) {
this._setKeywordsPushRuleVectorState(newPushRuleVectorState) this._setKeywordsPushRuleVectorState(newPushRuleVectorState)
} }
else { else {
var rule = this.getRule(vectorRuleId); const rule = this.getRule(vectorRuleId);
if (rule) { if (rule) {
this._setPushRuleVectorState(rule, newPushRuleVectorState); this._setPushRuleVectorState(rule, newPushRuleVectorState);
} }
@ -160,12 +159,12 @@ module.exports = React.createClass({
}, },
onKeywordsClicked: function(event) { onKeywordsClicked: function(event) {
var self = this; const self = this;
// Compute the keywords list to display // Compute the keywords list to display
var keywords = []; let keywords = [];
for (var i in this.state.vectorContentRules.rules) { for (let i in this.state.vectorContentRules.rules) {
var rule = this.state.vectorContentRules.rules[i]; const rule = this.state.vectorContentRules.rules[i];
keywords.push(rule.pattern); keywords.push(rule.pattern);
} }
if (keywords.length) { if (keywords.length) {
@ -179,7 +178,7 @@ module.exports = React.createClass({
keywords = ""; keywords = "";
} }
var TextInputDialog = sdk.getComponent("dialogs.TextInputDialog"); const TextInputDialog = sdk.getComponent("dialogs.TextInputDialog");
Modal.createTrackedDialog('Keywords Dialog', '', TextInputDialog, { Modal.createTrackedDialog('Keywords Dialog', '', TextInputDialog, {
title: _t('Keywords'), title: _t('Keywords'),
description: _t('Enter keywords separated by a comma:'), description: _t('Enter keywords separated by a comma:'),
@ -188,8 +187,8 @@ module.exports = React.createClass({
onFinished: function onFinished(should_leave, newValue) { onFinished: function onFinished(should_leave, newValue) {
if (should_leave && newValue !== keywords) { if (should_leave && newValue !== keywords) {
var newKeywords = newValue.split(','); let newKeywords = newValue.split(',');
for (var i in newKeywords) { for (let i in newKeywords) {
newKeywords[i] = newKeywords[i].trim(); newKeywords[i] = newKeywords[i].trim();
} }
@ -208,8 +207,8 @@ module.exports = React.createClass({
}, },
getRule: function(vectorRuleId) { getRule: function(vectorRuleId) {
for (var i in this.state.vectorPushRules) { for (let i in this.state.vectorPushRules) {
var rule = this.state.vectorPushRules[i]; const rule = this.state.vectorPushRules[i];
if (rule.vectorRuleId === vectorRuleId) { if (rule.vectorRuleId === vectorRuleId) {
return rule; return rule;
} }
@ -223,13 +222,13 @@ module.exports = React.createClass({
phase: this.phases.LOADING phase: this.phases.LOADING
}); });
var self = this; const self = this;
var cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
var deferreds = []; const deferreds = [];
var ruleDefinition = VectorPushRulesDefinitions[rule.vectorRuleId]; const ruleDefinition = VectorPushRulesDefinitions[rule.vectorRuleId];
if (rule.rule) { if (rule.rule) {
var actions = ruleDefinition.vectorStateToActions[newPushRuleVectorState]; const actions = ruleDefinition.vectorStateToActions[newPushRuleVectorState];
if (!actions) { if (!actions) {
// The new state corresponds to disabling the rule. // The new state corresponds to disabling the rule.
@ -244,7 +243,7 @@ module.exports = React.createClass({
Promise.all(deferreds).done(function() { Promise.all(deferreds).done(function() {
self._refreshFromServer(); self._refreshFromServer();
}, function(error) { }, function(error) {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to change settings: " + error); console.error("Failed to change settings: " + error);
Modal.createTrackedDialog('Failed to change settings', '', ErrorDialog, { Modal.createTrackedDialog('Failed to change settings', '', ErrorDialog, {
title: _t('Failed to change settings'), title: _t('Failed to change settings'),
@ -262,19 +261,19 @@ module.exports = React.createClass({
return; return;
} }
var self = this; const self = this;
var cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
this.setState({ this.setState({
phase: this.phases.LOADING phase: this.phases.LOADING
}); });
// Update all rules in self.state.vectorContentRules // Update all rules in self.state.vectorContentRules
var deferreds = []; const deferreds = [];
for (var i in this.state.vectorContentRules.rules) { for (let i in this.state.vectorContentRules.rules) {
var rule = this.state.vectorContentRules.rules[i]; const rule = this.state.vectorContentRules.rules[i];
var enabled, actions; let enabled, actions;
switch (newPushRuleVectorState) { switch (newPushRuleVectorState) {
case PushRuleVectorState.ON: case PushRuleVectorState.ON:
if (rule.actions.length !== 1) { if (rule.actions.length !== 1) {
@ -314,7 +313,7 @@ module.exports = React.createClass({
Promise.all(deferreds).done(function(resps) { Promise.all(deferreds).done(function(resps) {
self._refreshFromServer(); self._refreshFromServer();
}, function(error) { }, function(error) {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Can't update user notification settings: " + error); console.error("Can't update user notification settings: " + error);
Modal.createTrackedDialog('Can\'t update user notifcation settings', '', ErrorDialog, { Modal.createTrackedDialog('Can\'t update user notifcation settings', '', ErrorDialog, {
title: _t('Can\'t update user notification settings'), title: _t('Can\'t update user notification settings'),
@ -329,14 +328,14 @@ module.exports = React.createClass({
phase: this.phases.LOADING phase: this.phases.LOADING
}); });
var self = this; const self = this;
var cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
var removeDeferreds = []; const removeDeferreds = [];
// Remove per-word push rules of keywords that are no more in the list // Remove per-word push rules of keywords that are no more in the list
var vectorContentRulesPatterns = []; const vectorContentRulesPatterns = [];
for (var i in self.state.vectorContentRules.rules) { for (let i in self.state.vectorContentRules.rules) {
var rule = self.state.vectorContentRules.rules[i]; const rule = self.state.vectorContentRules.rules[i];
vectorContentRulesPatterns.push(rule.pattern); vectorContentRulesPatterns.push(rule.pattern);
@ -347,16 +346,16 @@ module.exports = React.createClass({
// If the keyword is part of `externalContentRules`, remove the rule // If the keyword is part of `externalContentRules`, remove the rule
// before recreating it in the right Vector path // before recreating it in the right Vector path
for (var i in self.state.externalContentRules) { for (let i in self.state.externalContentRules) {
var rule = self.state.externalContentRules[i]; const rule = self.state.externalContentRules[i];
if (newKeywords.indexOf(rule.pattern) >= 0) { if (newKeywords.indexOf(rule.pattern) >= 0) {
removeDeferreds.push(cli.deletePushRule('global', rule.kind, rule.rule_id)); removeDeferreds.push(cli.deletePushRule('global', rule.kind, rule.rule_id));
} }
} }
var onError = function(error) { const onError = function(error) {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to update keywords: " + error); console.error("Failed to update keywords: " + error);
Modal.createTrackedDialog('Failed to update keywords', '', ErrorDialog, { Modal.createTrackedDialog('Failed to update keywords', '', ErrorDialog, {
title: _t('Failed to update keywords'), title: _t('Failed to update keywords'),
@ -367,9 +366,9 @@ module.exports = React.createClass({
// Then, add the new ones // Then, add the new ones
Promise.all(removeDeferreds).done(function(resps) { Promise.all(removeDeferreds).done(function(resps) {
var deferreds = []; const deferreds = [];
var pushRuleVectorStateKind = self.state.vectorContentRules.vectorState; let pushRuleVectorStateKind = self.state.vectorContentRules.vectorState;
if (pushRuleVectorStateKind === PushRuleVectorState.OFF) { if (pushRuleVectorStateKind === PushRuleVectorState.OFF) {
// When the current global keywords rule is OFF, we need to look at // When the current global keywords rule is OFF, we need to look at
// the flavor of rules in 'vectorContentRules' to apply the same actions // the flavor of rules in 'vectorContentRules' to apply the same actions
@ -384,8 +383,8 @@ module.exports = React.createClass({
} }
} }
for (var i in newKeywords) { for (let i in newKeywords) {
var keyword = newKeywords[i]; const keyword = newKeywords[i];
if (vectorContentRulesPatterns.indexOf(keyword) < 0) { if (vectorContentRulesPatterns.indexOf(keyword) < 0) {
if (self.state.vectorContentRules.vectorState !== PushRuleVectorState.OFF) { if (self.state.vectorContentRules.vectorState !== PushRuleVectorState.OFF) {
@ -412,31 +411,31 @@ module.exports = React.createClass({
// Create a push rule but disabled // Create a push rule but disabled
_addDisabledPushRule: function(scope, kind, ruleId, body) { _addDisabledPushRule: function(scope, kind, ruleId, body) {
var cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
return cli.addPushRule(scope, kind, ruleId, body).then(function() { return cli.addPushRule(scope, kind, ruleId, body).then(() =>
return cli.setPushRuleEnabled(scope, kind, ruleId, false); cli.setPushRuleEnabled(scope, kind, ruleId, false)
}); );
}, },
// Check if any legacy im.vector rules need to be ported to the new API // Check if any legacy im.vector rules need to be ported to the new API
// for overriding the actions of default rules. // for overriding the actions of default rules.
_portRulesToNewAPI: function(rulesets) { _portRulesToNewAPI: function(rulesets) {
var self = this; const self = this;
var needsUpdate = []; const needsUpdate = [];
var cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
for (var kind in rulesets.global) { for (let kind in rulesets.global) {
var ruleset = rulesets.global[kind]; const ruleset = rulesets.global[kind];
for (var i = 0; i < ruleset.length; ++i) { for (let i = 0; i < ruleset.length; ++i) {
var rule = ruleset[i]; const rule = ruleset[i];
if (rule.rule_id in LEGACY_RULES) { if (rule.rule_id in LEGACY_RULES) {
console.log("Porting legacy rule", rule); console.log("Porting legacy rule", rule);
needsUpdate.push( function(kind, rule) { needsUpdate.push( function(kind, rule) {
return cli.setPushRuleActions( return cli.setPushRuleActions(
'global', kind, LEGACY_RULES[rule.rule_id], portLegacyActions(rule.actions) 'global', kind, LEGACY_RULES[rule.rule_id], portLegacyActions(rule.actions)
).then( function() { ).then(() =>
return cli.deletePushRule('global', kind, rule.rule_id); cli.deletePushRule('global', kind, rule.rule_id)
}).catch( (e) => { ).catch( (e) => {
console.warn(`Error when porting legacy rule: ${e}`); console.warn(`Error when porting legacy rule: ${e}`);
}); });
}(kind, rule)); }(kind, rule));
@ -447,9 +446,9 @@ module.exports = React.createClass({
if (needsUpdate.length > 0) { if (needsUpdate.length > 0) {
// If some of the rules need to be ported then wait for the porting // If some of the rules need to be ported then wait for the porting
// to happen and then fetch the rules again. // to happen and then fetch the rules again.
return Promise.all(needsUpdate).then( function() { return Promise.all(needsUpdate).then(() =>
return cli.getPushRules(); cli.getPushRules()
}); );
} else { } else {
// Otherwise return the rules that we already have. // Otherwise return the rules that we already have.
return rulesets; return rulesets;
@ -457,15 +456,14 @@ module.exports = React.createClass({
}, },
_refreshFromServer: function() { _refreshFromServer: function() {
var self = this; const self = this;
var pushRulesPromise = MatrixClientPeg.get().getPushRules().then(self._portRulesToNewAPI).then(function(rulesets) { const pushRulesPromise = MatrixClientPeg.get().getPushRules().then(self._portRulesToNewAPI).then(function(rulesets) {
//console.log("resolving pushRulesPromise");
/// XXX seriously? wtf is this? /// XXX seriously? wtf is this?
MatrixClientPeg.get().pushRules = rulesets; MatrixClientPeg.get().pushRules = rulesets;
// Get homeserver default rules and triage them by categories // Get homeserver default rules and triage them by categories
var rule_categories = { const rule_categories = {
// The master rule (all notifications disabling) // The master rule (all notifications disabling)
'.m.rule.master': 'master', '.m.rule.master': 'master',
@ -483,12 +481,12 @@ module.exports = React.createClass({
}; };
// HS default rules // HS default rules
var defaultRules = {master: [], vector: {}, others: []}; const defaultRules = {master: [], vector: {}, others: []};
for (var kind in rulesets.global) { for (let kind in rulesets.global) {
for (var i = 0; i < Object.keys(rulesets.global[kind]).length; ++i) { for (let i = 0; i < Object.keys(rulesets.global[kind]).length; ++i) {
var r = rulesets.global[kind][i]; const r = rulesets.global[kind][i];
var cat = rule_categories[r.rule_id]; const cat = rule_categories[r.rule_id];
r.kind = kind; r.kind = kind;
if (r.rule_id[0] === '.') { if (r.rule_id[0] === '.') {
@ -511,7 +509,7 @@ module.exports = React.createClass({
} }
// parse the keyword rules into our state // parse the keyword rules into our state
var contentRules = ContentRules.parseContentRules(rulesets); const contentRules = ContentRules.parseContentRules(rulesets);
self.state.vectorContentRules = { self.state.vectorContentRules = {
vectorState: contentRules.vectorState, vectorState: contentRules.vectorState,
rules: contentRules.rules, rules: contentRules.rules,
@ -522,7 +520,7 @@ module.exports = React.createClass({
self.state.vectorPushRules = []; self.state.vectorPushRules = [];
self.state.externalPushRules = []; self.state.externalPushRules = [];
var vectorRuleIds = [ const vectorRuleIds = [
'.m.rule.contains_display_name', '.m.rule.contains_display_name',
'.m.rule.contains_user_name', '.m.rule.contains_user_name',
'_keywords', '_keywords',
@ -533,8 +531,8 @@ module.exports = React.createClass({
'.m.rule.call', '.m.rule.call',
'.m.rule.suppress_notices' '.m.rule.suppress_notices'
]; ];
for (var i in vectorRuleIds) { for (let i in vectorRuleIds) {
var vectorRuleId = vectorRuleIds[i]; const vectorRuleId = vectorRuleIds[i];
if (vectorRuleId === '_keywords') { if (vectorRuleId === '_keywords') {
// keywords needs a special handling // keywords needs a special handling
@ -546,9 +544,8 @@ module.exports = React.createClass({
<span> <span>
{ _tJsx('Messages containing <span>keywords</span>', { _tJsx('Messages containing <span>keywords</span>',
/<span>(.*?)<\/span>/, /<span>(.*?)<\/span>/,
(sub) => { (sub) =>
return <span className="mx_UserNotifSettings_keywords" onClick={ self.onKeywordsClicked }>{sub}</span>; <span className="mx_UserNotifSettings_keywords" onClick={ self.onKeywordsClicked }>{sub}</span>
}
)} )}
</span> </span>
), ),
@ -556,10 +553,10 @@ module.exports = React.createClass({
}); });
} }
else { else {
var ruleDefinition = VectorPushRulesDefinitions[vectorRuleId]; const ruleDefinition = VectorPushRulesDefinitions[vectorRuleId];
var rule = defaultRules.vector[vectorRuleId]; const rule = defaultRules.vector[vectorRuleId];
var vectorState = ruleDefinition.ruleToVectorState(rule); const vectorState = ruleDefinition.ruleToVectorState(rule);
//console.log("Refreshing vectorPushRules for " + vectorRuleId +", "+ ruleDefinition.description +", " + rule +", " + vectorState); //console.log("Refreshing vectorPushRules for " + vectorRuleId +", "+ ruleDefinition.description +", " + rule +", " + vectorState);
@ -579,14 +576,14 @@ module.exports = React.createClass({
} }
// Build the rules not managed by Vector UI // Build the rules not managed by Vector UI
var otherRulesDescriptions = { const otherRulesDescriptions = {
'.m.rule.message': _t('Notify for all other messages/rooms'), '.m.rule.message': _t('Notify for all other messages/rooms'),
'.m.rule.fallback': _t('Notify me for anything else'), '.m.rule.fallback': _t('Notify me for anything else'),
}; };
for (var i in defaultRules.others) { for (let i in defaultRules.others) {
var rule = defaultRules.others[i]; const rule = defaultRules.others[i];
var ruleDescription = otherRulesDescriptions[rule.rule_id]; const ruleDescription = otherRulesDescriptions[rule.rule_id];
// Show enabled default rules that was modified by the user // Show enabled default rules that was modified by the user
if (ruleDescription && rule.enabled && !rule.default) { if (ruleDescription && rule.enabled && !rule.default) {
@ -596,8 +593,7 @@ module.exports = React.createClass({
} }
}); });
var pushersPromise = MatrixClientPeg.get().getPushers().then(function(resp) { const pushersPromise = MatrixClientPeg.get().getPushers().then(function(resp) {
//console.log("resolving pushersPromise");
self.setState({pushers: resp.pushers}); self.setState({pushers: resp.pushers});
}); });
@ -623,7 +619,7 @@ module.exports = React.createClass({
}, },
_updatePushRuleActions: function(rule, actions, enabled) { _updatePushRuleActions: function(rule, actions, enabled) {
var cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
return cli.setPushRuleActions( return cli.setPushRuleActions(
'global', rule.kind, rule.rule_id, actions 'global', rule.kind, rule.rule_id, actions
@ -669,9 +665,9 @@ module.exports = React.createClass({
}, },
renderNotifRulesTableRows: function() { renderNotifRulesTableRows: function() {
var rows = []; const rows = [];
for (var i in this.state.vectorPushRules) { for (let i in this.state.vectorPushRules) {
var rule = this.state.vectorPushRules[i]; const rule = this.state.vectorPushRules[i];
//console.log("rendering: " + rule.description + ", " + rule.vectorRuleId + ", " + rule.vectorState); //console.log("rendering: " + rule.description + ", " + rule.vectorRuleId + ", " + rule.vectorState);
rows.push(this.renderNotifRulesTableRow(rule.description, rule.vectorRuleId, rule.vectorState)); rows.push(this.renderNotifRulesTableRow(rule.description, rule.vectorRuleId, rule.vectorState));
} }
@ -697,23 +693,25 @@ module.exports = React.createClass({
}, },
render: function() { render: function() {
var self = this; const self = this;
var spinner; let spinner;
if (this.state.phase === this.phases.LOADING) { if (this.state.phase === this.phases.LOADING) {
var Loader = sdk.getComponent("elements.Spinner"); const Loader = sdk.getComponent("elements.Spinner");
spinner = <Loader />; spinner = <Loader />;
} }
let masterPushRuleDiv;
if (this.state.masterPushRule) { if (this.state.masterPushRule) {
var masterPushRuleDiv = ( masterPushRuleDiv = (
<div className="mx_UserNotifSettings_tableRow"> <div className="mx_UserNotifSettings_tableRow">
<div className="mx_UserNotifSettings_inputCell"> <div className="mx_UserNotifSettings_inputCell">
<input id="enableNotifications" <input id="enableNotifications"
ref="enableNotifications" ref="enableNotifications"
type="checkbox" type="checkbox"
checked={ !this.state.masterPushRule.enabled } checked={ !this.state.masterPushRule.enabled }
onChange={ this.onEnableNotificationsChange } /> onChange={ this.onEnableNotificationsChange }
/>
</div> </div>
<div className="mx_UserNotifSettings_labelCell"> <div className="mx_UserNotifSettings_labelCell">
<label htmlFor="enableNotifications"> <label htmlFor="enableNotifications">
@ -748,29 +746,29 @@ module.exports = React.createClass({
// This only supports the first email address in your profile for now // This only supports the first email address in your profile for now
emailNotificationsRow = this.emailNotificationsRow( emailNotificationsRow = this.emailNotificationsRow(
emailThreepids[0].address, emailThreepids[0].address,
_t('Enable email notifications') + ' (' + emailThreepids[0].address + ')' `${_t('Enable email notifications')} (${emailThreepids[0].address})`
); );
} }
// Build external push rules // Build external push rules
var externalRules = []; const externalRules = [];
for (var i in this.state.externalPushRules) { for (let i in this.state.externalPushRules) {
var rule = this.state.externalPushRules[i]; const rule = this.state.externalPushRules[i];
externalRules.push(<li>{ _t(rule.description) }</li>); externalRules.push(<li>{ _t(rule.description) }</li>);
} }
// Show keywords not displayed by the vector UI as a single external push rule // Show keywords not displayed by the vector UI as a single external push rule
var externalKeyWords = []; let externalKeywords = [];
for (var i in this.state.externalContentRules) { for (let i in this.state.externalContentRules) {
var rule = this.state.externalContentRules[i]; const rule = this.state.externalContentRules[i];
externalKeyWords.push(rule.pattern); externalKeywords.push(rule.pattern);
} }
if (externalKeyWords.length) { if (externalKeywords.length) {
externalKeyWords = externalKeyWords.join(", "); externalKeywords = externalKeywords.join(", ");
externalRules.push(<li>{ _t('Notifications on the following keywords follow rules which cant be displayed here:') } { externalKeyWords }</li>); externalRules.push(<li>{ _t('Notifications on the following keywords follow rules which cant be displayed here:') } { externalKeywords }</li>);
} }
var devicesSection; let devicesSection;
if (this.state.pushers === undefined) { if (this.state.pushers === undefined) {
devicesSection = <div className="error">{ _t('Unable to fetch notification target list') }</div> devicesSection = <div className="error">{ _t('Unable to fetch notification target list') }</div>
} else if (this.state.pushers.length == 0) { } else if (this.state.pushers.length == 0) {
@ -778,8 +776,8 @@ module.exports = React.createClass({
} else { } else {
// TODO: It would be great to be able to delete pushers from here too, // TODO: It would be great to be able to delete pushers from here too,
// and this wouldn't be hard to add. // and this wouldn't be hard to add.
var rows = []; const rows = [];
for (var i = 0; i < this.state.pushers.length; ++i) { for (let i = 0; i < this.state.pushers.length; ++i) {
rows.push(<tr key={ i }> rows.push(<tr key={ i }>
<td>{this.state.pushers[i].app_display_name}</td> <td>{this.state.pushers[i].app_display_name}</td>
<td>{this.state.pushers[i].device_display_name}</td> <td>{this.state.pushers[i].device_display_name}</td>
@ -798,7 +796,7 @@ module.exports = React.createClass({
</div>); </div>);
} }
var advancedSettings; let advancedSettings;
if (externalRules.length) { if (externalRules.length) {
advancedSettings = ( advancedSettings = (
<div> <div>

View File

@ -87,6 +87,11 @@ textarea {
transition: opacity 0.2s ease-in-out; transition: opacity 0.2s ease-in-out;
} }
.mx_fadable.mx_fadable_faded {
opacity: 0.3;
pointer-events: none;
}
/* XXX: critical hack to GeminiScrollbar to allow them to work in FF 42 and Chrome 48. /* XXX: critical hack to GeminiScrollbar to allow them to work in FF 42 and Chrome 48.
Stop the scrollbar view from pushing out the container's overall sizing, which causes Stop the scrollbar view from pushing out the container's overall sizing, which causes
flexbox to adapt to the new size and cause the view to keep growing. flexbox to adapt to the new size and cause the view to keep growing.

View File

@ -68,7 +68,7 @@ limitations under the License.
min-width: 0px; min-width: 0px;
max-width: 960px; max-width: 960px;
width: 100%; width: 100%;
margin: auto; margin: 0px auto;
overflow: auto; overflow: auto;
border-bottom: 1px solid $primary-hairline-color; border-bottom: 1px solid $primary-hairline-color;
@ -80,17 +80,32 @@ limitations under the License.
max-width: 1920px ! important; max-width: 1920px ! important;
} }
.mx_RoomView_topUnreadMessagesBar {
.mx_RoomView_body {
order: 3;
flex: 1 1 0;
flex-direction: column;
display: flex;
}
.mx_RoomView_body .mx_RoomView_topUnreadMessagesBar {
order: 1;
}
.mx_RoomView_body .mx_RoomView_messagePanel {
order: 2;
}
.mx_RoomView_body .mx_RoomView_statusArea {
order: 3; order: 3;
} }
.mx_RoomView_messagePanel { .mx_RoomView_body .mx_MessageComposer {
order: 4; order: 4;
}
flex: 1 1 0; .mx_RoomView_messagePanel {
width: 100%; width: 100%;
overflow-y: auto; overflow-y: auto;
} }
@ -131,18 +146,6 @@ limitations under the License.
clear: both; clear: both;
} }
.mx_RoomView_invitePrompt {
order: 2;
min-width: 0px;
max-width: 960px;
width: 100%;
margin: auto;
margin-top: 12px;
margin-bottom: 12px;
}
li.mx_RoomView_myReadMarker_container { li.mx_RoomView_myReadMarker_container {
height: 0px; height: 0px;
margin: 0px; margin: 0px;
@ -160,8 +163,6 @@ hr.mx_RoomView_myReadMarker {
} }
.mx_RoomView_statusArea { .mx_RoomView_statusArea {
order: 5;
width: 100%; width: 100%;
flex: 0 0 auto; flex: 0 0 auto;
@ -236,8 +237,6 @@ hr.mx_RoomView_myReadMarker {
} }
.mx_RoomView .mx_MessageComposer { .mx_RoomView .mx_MessageComposer {
order: 6;
width: 100%; width: 100%;
flex: 0 0 auto; flex: 0 0 auto;
margin-right: 2px; margin-right: 2px;

View File

@ -43,3 +43,7 @@ limitations under the License.
width: auto; width: auto;
height: 100%; height: 100%;
} }
.mx_VideoView_localVideoFeed.mx_VideoView_localVideoFeed_flipped video {
transform: scale(-1, 1);
}