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

pull/5578/head
David Baker 2017-10-27 19:01:34 +01:00
commit b9e2b59504
12 changed files with 291 additions and 232 deletions

View File

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

View File

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

View File

@ -18,14 +18,16 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { _t } from 'matrix-react-sdk/lib/languageHandler';
import sdk from 'matrix-react-sdk';
import dis from 'matrix-react-sdk/lib/dispatcher';
import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
import MatrixClient from 'matrix-js-sdk';
import Analytics from 'matrix-react-sdk/lib/Analytics';
import rate_limited_func from 'matrix-react-sdk/lib/ratelimitedfunc';
import AccessibleButton from 'matrix-react-sdk/lib/components/views/elements/AccessibleButton';
import { showGroupInviteDialog, showGroupAddRoomDialog } from 'matrix-react-sdk/lib/GroupAddressPicker';
import GroupStoreCache from 'matrix-react-sdk/lib/stores/GroupStoreCache';
class HeaderButton extends React.Component {
constructor() {
@ -80,6 +82,10 @@ module.exports = React.createClass({
collapsed: React.PropTypes.bool, // currently unused property to request for a minimized view of the panel
},
contextTypes: {
matrixClient: PropTypes.instanceOf(MatrixClient),
},
Phase: {
RoomMemberList: 'RoomMemberList',
GroupMemberList: 'GroupMemberList',
@ -92,29 +98,52 @@ module.exports = React.createClass({
componentWillMount: function() {
this.dispatcherRef = dis.register(this.onAction);
const cli = MatrixClientPeg.get();
const cli = this.context.matrixClient;
cli.on("RoomState.members", this.onRoomStateMember);
this._initGroupStore(this.props.groupId);
},
componentWillUnmount: function() {
dis.unregister(this.dispatcherRef);
if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
if (this.context.matrixClient) {
this.context.matrixClient.removeListener("RoomState.members", this.onRoomStateMember);
}
this._unregisterGroupStore();
},
getInitialState: function() {
if (this.props.groupId) {
return {
phase: this.Phase.GroupMemberList,
};
} else {
return {
phase: this.Phase.RoomMemberList,
};
return {
phase: this.props.groupId ? this.Phase.GroupMemberList : this.Phase.RoomMemberList,
isUserPrivilegedInGroup: null,
}
},
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() {
dis.dispatch({
action: 'hide_right_panel',
@ -122,7 +151,7 @@ module.exports = React.createClass({
},
onInviteButtonClick: function() {
if (MatrixClientPeg.get().isGuest()) {
if (this.context.matrixClient.isGuest()) {
dis.dispatch({action: 'view_set_mxid'});
return;
}
@ -222,13 +251,13 @@ module.exports = React.createClass({
if ((this.state.phase == this.Phase.RoomMemberList || this.state.phase === this.Phase.RoomMemberInfo)
&& this.props.roomId
) {
const cli = MatrixClientPeg.get();
const cli = this.context.matrixClient;
const room = cli.getRoom(this.props.roomId);
let userIsInRoom;
if (room) {
membersBadge = room.getJoinedMembers().length;
userIsInRoom = room.hasMembershipState(
MatrixClientPeg.get().credentials.userId, 'join',
this.context.matrixClient.credentials.userId, 'join',
);
}
@ -243,6 +272,11 @@ module.exports = React.createClass({
}
}
const isPhaseGroup = [
this.Phase.GroupMemberInfo,
this.Phase.GroupMemberList
].includes(this.state.phase);
let headerButtons = [];
if (this.props.roomId) {
headerButtons = [
@ -266,7 +300,7 @@ module.exports = React.createClass({
} else if (this.props.groupId) {
headerButtons = [
<HeaderButton key="_groupMembersButton" title={_t('Members')} iconSrc="img/icons-people.svg"
isHighlighted={this.state.phase === this.Phase.GroupMemberList}
isHighlighted={isPhaseGroup}
clickPhase={this.Phase.GroupMemberList}
analytics={['Right Panel', 'Group Member List Button', 'click']}
/>,
@ -317,8 +351,8 @@ module.exports = React.createClass({
panel = <div className="mx_RightPanel_blank"></div>;
}
if (this.props.groupId) {
inviteGroup = this.state.phase === this.Phase.GroupMemberList ? (
if (this.props.groupId && this.state.isUserPrivilegedInGroup) {
inviteGroup = isPhaseGroup ? (
<AccessibleButton className="mx_RightPanel_invite" onClick={ this.onInviteButtonClick } >
<div className="mx_RightPanel_icon" >
<TintableSvg src="img/icon-invite-people.svg" width="35" height="35" />
@ -335,13 +369,16 @@ module.exports = React.createClass({
);
}
let classes = "mx_RightPanel mx_fadable";
if (this.props.collapsed) {
classes += " collapsed";
}
let classes = classNames(
"mx_RightPanel", "mx_fadable",
{
"collapsed": this.props.collapsed,
"mx_fadable_faded": this.props.disabled,
}
);
return (
<aside className={classes} style={{ opacity: this.props.opacity }}>
<aside className={classes}>
<div className="mx_RightPanel_header">
<div className="mx_RightPanel_headerButtonGroup">
{headerButtons}

View File

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

View File

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

View File

@ -82,6 +82,11 @@ textarea {
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.
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.

View File

@ -33,10 +33,12 @@ limitations under the License.
min-height: 70px;
align-items: center;
display: flex;
padding-bottom: 10px;
}
.mx_GroupView_header_view {
border-bottom: 1px solid $primary-hairline-color;
padding-bottom: 0px;
}
.mx_GroupView_header_avatar, .mx_GroupView_header_info {
@ -159,8 +161,12 @@ limitations under the License.
user-select: none;
}
.mx_GroupView_rooms_header h3 {
margin-bottom: 10px
.mx_GroupView h3 {
text-transform: uppercase;
color: $h3-color;
font-weight: 600;
font-size: 13px;
margin-bottom: 10px;
}
.mx_GroupView_rooms_header .mx_AccessibleButton {
@ -169,15 +175,24 @@ limitations under the License.
height: 24px;
}
.mx_GroupView_rooms_header_addButton {
display: inline-block;
.mx_GroupView_group {
border-top: 1px solid $primary-hairline-color;
}
.mx_GroupView_rooms_header_addButton object {
.mx_GroupView_group_disabled {
opacity: 0.3;
pointer-events: none;
}
.mx_GroupView_rooms_header_addButton_label {
.mx_GroupView_rooms_header_addRow_button {
display: inline-block;
}
.mx_GroupView_rooms_header_addRow_button object {
pointer-events: none;
}
.mx_GroupView_rooms_header_addRow_label {
display: inline-block;
vertical-align: top;
line-height: 24px;
@ -222,14 +237,6 @@ limitations under the License.
user-select: none;
}
.mx_GroupView_memberSettings h3 {
text-transform: uppercase;
color: $h3-color;
font-weight: 600;
font-size: 13px;
margin-bottom: 10px;
}
.mx_GroupView_memberSettings input {
margin-right: 6px;
}

View File

@ -68,7 +68,7 @@ limitations under the License.
min-width: 0px;
max-width: 960px;
width: 100%;
margin: auto;
margin: 0px auto;
overflow: auto;
border-bottom: 1px solid $primary-hairline-color;
@ -80,17 +80,32 @@ limitations under the License.
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;
}
.mx_RoomView_messagePanel {
.mx_RoomView_body .mx_MessageComposer {
order: 4;
}
flex: 1 1 0;
.mx_RoomView_messagePanel {
width: 100%;
overflow-y: auto;
}
@ -131,18 +146,6 @@ limitations under the License.
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 {
height: 0px;
margin: 0px;
@ -160,8 +163,6 @@ hr.mx_RoomView_myReadMarker {
}
.mx_RoomView_statusArea {
order: 5;
width: 100%;
flex: 0 0 auto;
@ -236,8 +237,6 @@ hr.mx_RoomView_myReadMarker {
}
.mx_RoomView .mx_MessageComposer {
order: 6;
width: 100%;
flex: 0 0 auto;
margin-right: 2px;

View File

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

View File

@ -94,30 +94,26 @@ limitations under the License.
}
.mx_RightPanel_footer .mx_RightPanel_invite {
line-height: 35px;
font-size: 14px;
color: $primary-fg-color;
padding-top: 13px;
padding-left: 5px;
cursor: pointer;
display: flex;
align-items: center;
}
.collapsed .mx_RightPanel_footer .mx_RightPanel_invite {
display: none;
}
.mx_RightPanel_invite .mx_RightPanel_icon {
display: inline-block;
}
.mx_RightPanel_invite .mx_RightPanel_icon object {
pointer-events: none;
}
.mx_RightPanel_invite .mx_RightPanel_message {
display: inline-block;
vertical-align: top;
padding-left: 10px
padding-left: 10px;
line-height: 18px;
}
.mx_MatrixChat_useCompactLayout {

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<g>
<path fill="#FF0064" d="M85.633,454.889c0,31.168,25.553,56.661,56.79,56.661h227.156c31.234,0,56.787-25.493,56.787-56.661
V128.225H85.633V454.889z M468.958,43.042H362.479L326.828,0.45H185.173l-35.652,42.591H43.042v42.592h425.916V43.042z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 738 B