forked from matrix/element-web
Merge remote-tracking branch 'origin/develop' into matthew/status
This commit is contained in:
commit
b9e2b59504
|
@ -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,
|
||||||
"welcomePageUrl": "home.html",
|
"welcomePageUrl": "home.html",
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -18,14 +18,16 @@ 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';
|
||||||
import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
|
import MatrixClient from 'matrix-js-sdk';
|
||||||
import Analytics from 'matrix-react-sdk/lib/Analytics';
|
import Analytics from 'matrix-react-sdk/lib/Analytics';
|
||||||
import rate_limited_func from 'matrix-react-sdk/lib/ratelimitedfunc';
|
import rate_limited_func from 'matrix-react-sdk/lib/ratelimitedfunc';
|
||||||
import AccessibleButton from 'matrix-react-sdk/lib/components/views/elements/AccessibleButton';
|
import AccessibleButton from 'matrix-react-sdk/lib/components/views/elements/AccessibleButton';
|
||||||
import { showGroupInviteDialog, showGroupAddRoomDialog } from 'matrix-react-sdk/lib/GroupAddressPicker';
|
import { showGroupInviteDialog, showGroupAddRoomDialog } from 'matrix-react-sdk/lib/GroupAddressPicker';
|
||||||
|
import GroupStoreCache from 'matrix-react-sdk/lib/stores/GroupStoreCache';
|
||||||
|
|
||||||
class HeaderButton extends React.Component {
|
class HeaderButton extends React.Component {
|
||||||
constructor() {
|
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
|
collapsed: React.PropTypes.bool, // currently unused property to request for a minimized view of the panel
|
||||||
},
|
},
|
||||||
|
|
||||||
|
contextTypes: {
|
||||||
|
matrixClient: PropTypes.instanceOf(MatrixClient),
|
||||||
|
},
|
||||||
|
|
||||||
Phase: {
|
Phase: {
|
||||||
RoomMemberList: 'RoomMemberList',
|
RoomMemberList: 'RoomMemberList',
|
||||||
GroupMemberList: 'GroupMemberList',
|
GroupMemberList: 'GroupMemberList',
|
||||||
|
@ -92,29 +98,52 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
const cli = MatrixClientPeg.get();
|
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() {
|
||||||
dis.unregister(this.dispatcherRef);
|
dis.unregister(this.dispatcherRef);
|
||||||
if (MatrixClientPeg.get()) {
|
if (this.context.matrixClient) {
|
||||||
MatrixClientPeg.get().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.props.groupId ? this.Phase.GroupMemberList : this.Phase.RoomMemberList,
|
||||||
phase: this.Phase.GroupMemberList,
|
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',
|
||||||
|
@ -122,7 +151,7 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
onInviteButtonClick: function() {
|
onInviteButtonClick: function() {
|
||||||
if (MatrixClientPeg.get().isGuest()) {
|
if (this.context.matrixClient.isGuest()) {
|
||||||
dis.dispatch({action: 'view_set_mxid'});
|
dis.dispatch({action: 'view_set_mxid'});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -222,13 +251,13 @@ module.exports = React.createClass({
|
||||||
if ((this.state.phase == this.Phase.RoomMemberList || this.state.phase === this.Phase.RoomMemberInfo)
|
if ((this.state.phase == this.Phase.RoomMemberList || this.state.phase === this.Phase.RoomMemberInfo)
|
||||||
&& this.props.roomId
|
&& this.props.roomId
|
||||||
) {
|
) {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = this.context.matrixClient;
|
||||||
const room = cli.getRoom(this.props.roomId);
|
const room = cli.getRoom(this.props.roomId);
|
||||||
let userIsInRoom;
|
let userIsInRoom;
|
||||||
if (room) {
|
if (room) {
|
||||||
membersBadge = room.getJoinedMembers().length;
|
membersBadge = room.getJoinedMembers().length;
|
||||||
userIsInRoom = room.hasMembershipState(
|
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 = [];
|
let headerButtons = [];
|
||||||
if (this.props.roomId) {
|
if (this.props.roomId) {
|
||||||
headerButtons = [
|
headerButtons = [
|
||||||
|
@ -266,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']}
|
||||||
/>,
|
/>,
|
||||||
|
@ -317,8 +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) {
|
||||||
inviteGroup = this.state.phase === this.Phase.GroupMemberList ? (
|
inviteGroup = isPhaseGroup ? (
|
||||||
<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" />
|
||||||
|
@ -335,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}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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,30 +693,32 @@ 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 className="mx_UserNotifSettings_labelCell">
|
|
||||||
<label htmlFor="enableNotifications">
|
|
||||||
{ _t('Enable notifications for this account') }
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</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
|
// 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 can’t be displayed here:') } { externalKeyWords }</li>);
|
externalRules.push(<li>{ _t('Notifications on the following keywords follow rules which can’t 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>
|
||||||
|
|
|
@ -82,6 +82,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.
|
||||||
|
|
|
@ -33,10 +33,12 @@ limitations under the License.
|
||||||
min-height: 70px;
|
min-height: 70px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
padding-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_GroupView_header_view {
|
.mx_GroupView_header_view {
|
||||||
border-bottom: 1px solid $primary-hairline-color;
|
border-bottom: 1px solid $primary-hairline-color;
|
||||||
|
padding-bottom: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_GroupView_header_avatar, .mx_GroupView_header_info {
|
.mx_GroupView_header_avatar, .mx_GroupView_header_info {
|
||||||
|
@ -159,8 +161,12 @@ limitations under the License.
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_GroupView_rooms_header h3 {
|
.mx_GroupView h3 {
|
||||||
margin-bottom: 10px
|
text-transform: uppercase;
|
||||||
|
color: $h3-color;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 13px;
|
||||||
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_GroupView_rooms_header .mx_AccessibleButton {
|
.mx_GroupView_rooms_header .mx_AccessibleButton {
|
||||||
|
@ -169,15 +175,24 @@ limitations under the License.
|
||||||
height: 24px;
|
height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_GroupView_rooms_header_addButton {
|
.mx_GroupView_group {
|
||||||
display: inline-block;
|
border-top: 1px solid $primary-hairline-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_GroupView_rooms_header_addButton object {
|
.mx_GroupView_group_disabled {
|
||||||
|
opacity: 0.3;
|
||||||
pointer-events: none;
|
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;
|
display: inline-block;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
|
@ -222,14 +237,6 @@ limitations under the License.
|
||||||
user-select: none;
|
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 {
|
.mx_GroupView_memberSettings input {
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -42,4 +42,8 @@ limitations under the License.
|
||||||
.mx_VideoView_localVideoFeed video {
|
.mx_VideoView_localVideoFeed video {
|
||||||
width: auto;
|
width: auto;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_VideoView_localVideoFeed.mx_VideoView_localVideoFeed_flipped video {
|
||||||
|
transform: scale(-1, 1);
|
||||||
|
}
|
||||||
|
|
|
@ -94,30 +94,26 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RightPanel_footer .mx_RightPanel_invite {
|
.mx_RightPanel_footer .mx_RightPanel_invite {
|
||||||
line-height: 35px;
|
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: $primary-fg-color;
|
color: $primary-fg-color;
|
||||||
padding-top: 13px;
|
padding-top: 13px;
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.collapsed .mx_RightPanel_footer .mx_RightPanel_invite {
|
.collapsed .mx_RightPanel_footer .mx_RightPanel_invite {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RightPanel_invite .mx_RightPanel_icon {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_RightPanel_invite .mx_RightPanel_icon object {
|
.mx_RightPanel_invite .mx_RightPanel_icon object {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RightPanel_invite .mx_RightPanel_message {
|
.mx_RightPanel_invite .mx_RightPanel_message {
|
||||||
display: inline-block;
|
padding-left: 10px;
|
||||||
vertical-align: top;
|
line-height: 18px;
|
||||||
padding-left: 10px
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MatrixChat_useCompactLayout {
|
.mx_MatrixChat_useCompactLayout {
|
||||||
|
|
|
@ -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 |
Loading…
Reference in New Issue