Merge branch 'develop' into t3chguy/login_local_error
This commit is contained in:
commit
de316134a9
21 changed files with 266 additions and 175 deletions
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
src/autocomplete/AutocompleteProvider.js
|
src/autocomplete/AutocompleteProvider.js
|
||||||
src/autocomplete/Autocompleter.js
|
src/autocomplete/Autocompleter.js
|
||||||
src/autocomplete/EmojiProvider.js
|
|
||||||
src/autocomplete/UserProvider.js
|
src/autocomplete/UserProvider.js
|
||||||
src/component-index.js
|
src/component-index.js
|
||||||
src/components/structures/BottomLeftMenu.js
|
src/components/structures/BottomLeftMenu.js
|
||||||
|
@ -17,7 +16,6 @@ src/components/structures/MessagePanel.js
|
||||||
src/components/structures/NotificationPanel.js
|
src/components/structures/NotificationPanel.js
|
||||||
src/components/structures/RoomDirectory.js
|
src/components/structures/RoomDirectory.js
|
||||||
src/components/structures/RoomStatusBar.js
|
src/components/structures/RoomStatusBar.js
|
||||||
src/components/structures/RoomSubList.js
|
|
||||||
src/components/structures/RoomView.js
|
src/components/structures/RoomView.js
|
||||||
src/components/structures/ScrollPanel.js
|
src/components/structures/ScrollPanel.js
|
||||||
src/components/structures/SearchBox.js
|
src/components/structures/SearchBox.js
|
||||||
|
@ -29,7 +27,6 @@ src/components/views/avatars/BaseAvatar.js
|
||||||
src/components/views/avatars/MemberAvatar.js
|
src/components/views/avatars/MemberAvatar.js
|
||||||
src/components/views/create_room/RoomAlias.js
|
src/components/views/create_room/RoomAlias.js
|
||||||
src/components/views/dialogs/ChangelogDialog.js
|
src/components/views/dialogs/ChangelogDialog.js
|
||||||
src/components/views/dialogs/ChatCreateOrReuseDialog.js
|
|
||||||
src/components/views/dialogs/DeactivateAccountDialog.js
|
src/components/views/dialogs/DeactivateAccountDialog.js
|
||||||
src/components/views/dialogs/SetPasswordDialog.js
|
src/components/views/dialogs/SetPasswordDialog.js
|
||||||
src/components/views/dialogs/UnknownDeviceDialog.js
|
src/components/views/dialogs/UnknownDeviceDialog.js
|
||||||
|
@ -37,7 +34,6 @@ src/components/views/directory/NetworkDropdown.js
|
||||||
src/components/views/elements/AddressSelector.js
|
src/components/views/elements/AddressSelector.js
|
||||||
src/components/views/elements/DeviceVerifyButtons.js
|
src/components/views/elements/DeviceVerifyButtons.js
|
||||||
src/components/views/elements/DirectorySearchBox.js
|
src/components/views/elements/DirectorySearchBox.js
|
||||||
src/components/views/elements/EditableText.js
|
|
||||||
src/components/views/elements/ImageView.js
|
src/components/views/elements/ImageView.js
|
||||||
src/components/views/elements/InlineSpinner.js
|
src/components/views/elements/InlineSpinner.js
|
||||||
src/components/views/elements/MemberEventListSummary.js
|
src/components/views/elements/MemberEventListSummary.js
|
||||||
|
@ -81,7 +77,6 @@ src/components/views/rooms/TopUnreadMessagesBar.js
|
||||||
src/components/views/rooms/UserTile.js
|
src/components/views/rooms/UserTile.js
|
||||||
src/components/views/settings/AddPhoneNumber.js
|
src/components/views/settings/AddPhoneNumber.js
|
||||||
src/components/views/settings/ChangeAvatar.js
|
src/components/views/settings/ChangeAvatar.js
|
||||||
src/components/views/settings/ChangeDisplayName.js
|
|
||||||
src/components/views/settings/ChangePassword.js
|
src/components/views/settings/ChangePassword.js
|
||||||
src/components/views/settings/DevicesPanel.js
|
src/components/views/settings/DevicesPanel.js
|
||||||
src/components/views/settings/IntegrationsManager.js
|
src/components/views/settings/IntegrationsManager.js
|
||||||
|
|
|
@ -73,7 +73,7 @@
|
||||||
"glob": "^5.0.14",
|
"glob": "^5.0.14",
|
||||||
"highlight.js": "^9.0.0",
|
"highlight.js": "^9.0.0",
|
||||||
"isomorphic-fetch": "^2.2.1",
|
"isomorphic-fetch": "^2.2.1",
|
||||||
"linkifyjs": "^2.1.3",
|
"linkifyjs": "^2.1.6",
|
||||||
"lodash": "^4.13.1",
|
"lodash": "^4.13.1",
|
||||||
"lolex": "2.3.2",
|
"lolex": "2.3.2",
|
||||||
"matrix-js-sdk": "0.10.4",
|
"matrix-js-sdk": "0.10.4",
|
||||||
|
|
|
@ -70,6 +70,7 @@ limitations under the License.
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
cursor: text;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageComposer_input {
|
.mx_MessageComposer_input {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2018 New Vector Ltd
|
Copyright 2018 New Vector Ltd
|
||||||
|
Copyright 2018 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -26,7 +27,7 @@ import {makeGroupPermalink} from "../matrix-to";
|
||||||
import type {Completion, SelectionRange} from "./Autocompleter";
|
import type {Completion, SelectionRange} from "./Autocompleter";
|
||||||
import FlairStore from "../stores/FlairStore";
|
import FlairStore from "../stores/FlairStore";
|
||||||
|
|
||||||
const COMMUNITY_REGEX = /(?=\+)(\S*)/g;
|
const COMMUNITY_REGEX = /\B\+\S*/g;
|
||||||
|
|
||||||
function score(query, space) {
|
function score(query, space) {
|
||||||
const index = space.indexOf(query);
|
const index = space.indexOf(query);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
//@flow
|
//@flow
|
||||||
/*
|
/*
|
||||||
Copyright 2017 Aviral Dasgupta
|
Copyright 2017 Aviral Dasgupta
|
||||||
|
Copyright 2018 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -27,6 +28,10 @@ class KeyMap {
|
||||||
priorityMap = new Map();
|
priorityMap = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stripDiacritics(str: string): string {
|
||||||
|
return str.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
export default class QueryMatcher {
|
export default class QueryMatcher {
|
||||||
/**
|
/**
|
||||||
* @param {object[]} objects the objects to perform a match on
|
* @param {object[]} objects the objects to perform a match on
|
||||||
|
@ -46,10 +51,11 @@ export default class QueryMatcher {
|
||||||
objects.forEach((object, i) => {
|
objects.forEach((object, i) => {
|
||||||
const keyValues = _at(object, keys);
|
const keyValues = _at(object, keys);
|
||||||
for (const keyValue of keyValues) {
|
for (const keyValue of keyValues) {
|
||||||
if (!map.hasOwnProperty(keyValue)) {
|
const key = stripDiacritics(keyValue).toLowerCase();
|
||||||
map[keyValue] = [];
|
if (!map.hasOwnProperty(key)) {
|
||||||
|
map[key] = [];
|
||||||
}
|
}
|
||||||
map[keyValue].push(object);
|
map[key].push(object);
|
||||||
}
|
}
|
||||||
keyMap.priorityMap.set(object, i);
|
keyMap.priorityMap.set(object, i);
|
||||||
});
|
});
|
||||||
|
@ -82,7 +88,7 @@ export default class QueryMatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
match(query: String): Array<Object> {
|
match(query: String): Array<Object> {
|
||||||
query = query.toLowerCase();
|
query = stripDiacritics(query).toLowerCase();
|
||||||
if (this.options.shouldMatchWordsOnly) {
|
if (this.options.shouldMatchWordsOnly) {
|
||||||
query = query.replace(/[^\w]/g, '');
|
query = query.replace(/[^\w]/g, '');
|
||||||
}
|
}
|
||||||
|
@ -91,7 +97,7 @@ export default class QueryMatcher {
|
||||||
}
|
}
|
||||||
const results = [];
|
const results = [];
|
||||||
this.keyMap.keys.forEach((key) => {
|
this.keyMap.keys.forEach((key) => {
|
||||||
let resultKey = key.toLowerCase();
|
let resultKey = key;
|
||||||
if (this.options.shouldMatchWordsOnly) {
|
if (this.options.shouldMatchWordsOnly) {
|
||||||
resultKey = resultKey.replace(/[^\w]/g, '');
|
resultKey = resultKey.replace(/[^\w]/g, '');
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
Copyright 2016 Aviral Dasgupta
|
Copyright 2016 Aviral Dasgupta
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
Copyright 2017, 2018 New Vector Ltd
|
Copyright 2017, 2018 New Vector Ltd
|
||||||
|
Copyright 2018 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -28,7 +29,7 @@ import _sortBy from 'lodash/sortBy';
|
||||||
import {makeRoomPermalink} from "../matrix-to";
|
import {makeRoomPermalink} from "../matrix-to";
|
||||||
import type {Completion, SelectionRange} from "./Autocompleter";
|
import type {Completion, SelectionRange} from "./Autocompleter";
|
||||||
|
|
||||||
const ROOM_REGEX = /(?=#)(\S*)/g;
|
const ROOM_REGEX = /\B#\S*/g;
|
||||||
|
|
||||||
function score(query, space) {
|
function score(query, space) {
|
||||||
const index = space.indexOf(query);
|
const index = space.indexOf(query);
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
Copyright 2016 Aviral Dasgupta
|
Copyright 2016 Aviral Dasgupta
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
Copyright 2017, 2018 New Vector Ltd
|
Copyright 2017, 2018 New Vector Ltd
|
||||||
|
Copyright 2018 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -26,11 +27,11 @@ import FuzzyMatcher from './FuzzyMatcher';
|
||||||
import _sortBy from 'lodash/sortBy';
|
import _sortBy from 'lodash/sortBy';
|
||||||
import MatrixClientPeg from '../MatrixClientPeg';
|
import MatrixClientPeg from '../MatrixClientPeg';
|
||||||
|
|
||||||
import type {Room, RoomMember} from 'matrix-js-sdk';
|
import type {MatrixEvent, Room, RoomMember, RoomState} from 'matrix-js-sdk';
|
||||||
import {makeUserPermalink} from "../matrix-to";
|
import {makeUserPermalink} from "../matrix-to";
|
||||||
import type {SelectionRange} from "./Autocompleter";
|
import type {Completion, SelectionRange} from "./Autocompleter";
|
||||||
|
|
||||||
const USER_REGEX = /@\S*/g;
|
const USER_REGEX = /\B@\S*/g;
|
||||||
|
|
||||||
export default class UserProvider extends AutocompleteProvider {
|
export default class UserProvider extends AutocompleteProvider {
|
||||||
users: Array<RoomMember> = null;
|
users: Array<RoomMember> = null;
|
||||||
|
@ -44,7 +45,7 @@ export default class UserProvider extends AutocompleteProvider {
|
||||||
this.matcher = new FuzzyMatcher([], {
|
this.matcher = new FuzzyMatcher([], {
|
||||||
keys: ['name', 'userId'],
|
keys: ['name', 'userId'],
|
||||||
shouldMatchPrefix: true,
|
shouldMatchPrefix: true,
|
||||||
shouldMatchWordsOnly: false
|
shouldMatchWordsOnly: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
this._onRoomTimelineBound = this._onRoomTimeline.bind(this);
|
this._onRoomTimelineBound = this._onRoomTimeline.bind(this);
|
||||||
|
@ -61,7 +62,7 @@ export default class UserProvider extends AutocompleteProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onRoomTimeline(ev, room, toStartOfTimeline, removed, data) {
|
_onRoomTimeline(ev: MatrixEvent, room: Room, toStartOfTimeline: boolean, removed: boolean, data: Object) {
|
||||||
if (!room) return;
|
if (!room) return;
|
||||||
if (removed) return;
|
if (removed) return;
|
||||||
if (room.roomId !== this.room.roomId) return;
|
if (room.roomId !== this.room.roomId) return;
|
||||||
|
@ -77,7 +78,7 @@ export default class UserProvider extends AutocompleteProvider {
|
||||||
this.onUserSpoke(ev.sender);
|
this.onUserSpoke(ev.sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onRoomStateMember(ev, state, member) {
|
_onRoomStateMember(ev: MatrixEvent, state: RoomState, member: RoomMember) {
|
||||||
// ignore members in other rooms
|
// ignore members in other rooms
|
||||||
if (member.roomId !== this.room.roomId) {
|
if (member.roomId !== this.room.roomId) {
|
||||||
return;
|
return;
|
||||||
|
@ -87,7 +88,7 @@ export default class UserProvider extends AutocompleteProvider {
|
||||||
this.users = null;
|
this.users = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCompletions(query: string, selection: SelectionRange, force?: boolean = false) {
|
async getCompletions(query: string, selection: SelectionRange, force?: boolean = false): Array<Completion> {
|
||||||
const MemberAvatar = sdk.getComponent('views.avatars.MemberAvatar');
|
const MemberAvatar = sdk.getComponent('views.avatars.MemberAvatar');
|
||||||
|
|
||||||
// Disable autocompletions when composing commands because of various issues
|
// Disable autocompletions when composing commands because of various issues
|
||||||
|
@ -128,7 +129,7 @@ export default class UserProvider extends AutocompleteProvider {
|
||||||
return completions;
|
return completions;
|
||||||
}
|
}
|
||||||
|
|
||||||
getName() {
|
getName(): string {
|
||||||
return '👥 ' + _t('Users');
|
return '👥 ' + _t('Users');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,13 +142,9 @@ export default class UserProvider extends AutocompleteProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentUserId = MatrixClientPeg.get().credentials.userId;
|
const currentUserId = MatrixClientPeg.get().credentials.userId;
|
||||||
this.users = this.room.getJoinedMembers().filter((member) => {
|
this.users = this.room.getJoinedMembers().filter(({userId}) => userId !== currentUserId);
|
||||||
if (member.userId !== currentUserId) return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.users = _sortBy(this.users, (member) =>
|
this.users = _sortBy(this.users, (member) => 1E20 - lastSpoken[member.userId] || 1E20);
|
||||||
1E20 - lastSpoken[member.userId] || 1E20,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.matcher.setObjects(this.users);
|
this.matcher.setObjects(this.users);
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,9 @@ export default class ContextualMenu extends React.Component {
|
||||||
// The component to render as the context menu
|
// The component to render as the context menu
|
||||||
elementClass: PropTypes.element.isRequired,
|
elementClass: PropTypes.element.isRequired,
|
||||||
// on resize callback
|
// on resize callback
|
||||||
windowResize: PropTypes.func
|
windowResize: PropTypes.func,
|
||||||
|
// method to close menu
|
||||||
|
closeMenu: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -73,6 +75,7 @@ export default class ContextualMenu extends React.Component {
|
||||||
contextMenuRect: null,
|
contextMenuRect: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.onContextMenu = this.onContextMenu.bind(this);
|
||||||
this.collectContextMenuRect = this.collectContextMenuRect.bind(this);
|
this.collectContextMenuRect = this.collectContextMenuRect.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,6 +88,28 @@ export default class ContextualMenu extends React.Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onContextMenu(e) {
|
||||||
|
if (this.props.closeMenu) {
|
||||||
|
this.props.closeMenu();
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
const x = e.clientX;
|
||||||
|
const y = e.clientY;
|
||||||
|
|
||||||
|
// XXX: This isn't pretty but the only way to allow opening a different context menu on right click whilst
|
||||||
|
// a context menu and its click-guard are up without completely rewriting how the context menus work.
|
||||||
|
setImmediate(() => {
|
||||||
|
const clickEvent = document.createEvent('MouseEvents');
|
||||||
|
clickEvent.initMouseEvent(
|
||||||
|
'contextmenu', true, true, window, 0,
|
||||||
|
0, 0, x, y, false, false,
|
||||||
|
false, false, 0, null,
|
||||||
|
);
|
||||||
|
document.elementFromPoint(x, y).dispatchEvent(clickEvent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const position = {};
|
const position = {};
|
||||||
let chevronFace = null;
|
let chevronFace = null;
|
||||||
|
@ -195,7 +220,7 @@ export default class ContextualMenu extends React.Component {
|
||||||
{ chevron }
|
{ chevron }
|
||||||
<ElementClass {...props} onFinished={props.closeMenu} onResize={props.windowResize} />
|
<ElementClass {...props} onFinished={props.closeMenu} onResize={props.windowResize} />
|
||||||
</div>
|
</div>
|
||||||
{ props.hasBackground && <div className="mx_ContextualMenu_background" onClick={props.closeMenu} /> }
|
{ props.hasBackground && <div className="mx_ContextualMenu_background" onClick={props.closeMenu} onContextMenu={this.onContextMenu} /> }
|
||||||
<style>{ chevronCSS }</style>
|
<style>{ chevronCSS }</style>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import MatrixClientPeg from '../../MatrixClientPeg';
|
||||||
import MemberAvatar from '../views/avatars/MemberAvatar';
|
import MemberAvatar from '../views/avatars/MemberAvatar';
|
||||||
import Resend from '../../Resend';
|
import Resend from '../../Resend';
|
||||||
import * as cryptodevices from '../../cryptodevices';
|
import * as cryptodevices from '../../cryptodevices';
|
||||||
|
import dis from '../../dispatcher';
|
||||||
|
|
||||||
const STATUS_BAR_HIDDEN = 0;
|
const STATUS_BAR_HIDDEN = 0;
|
||||||
const STATUS_BAR_EXPANDED = 1;
|
const STATUS_BAR_EXPANDED = 1;
|
||||||
|
@ -157,10 +158,12 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
_onResendAllClick: function() {
|
_onResendAllClick: function() {
|
||||||
Resend.resendUnsentEvents(this.props.room);
|
Resend.resendUnsentEvents(this.props.room);
|
||||||
|
dis.dispatch({action: 'focus_composer'});
|
||||||
},
|
},
|
||||||
|
|
||||||
_onCancelAllClick: function() {
|
_onCancelAllClick: function() {
|
||||||
Resend.cancelUnsentEvents(this.props.room);
|
Resend.cancelUnsentEvents(this.props.room);
|
||||||
|
dis.dispatch({action: 'focus_composer'});
|
||||||
},
|
},
|
||||||
|
|
||||||
_onShowDevicesClick: function() {
|
_onShowDevicesClick: function() {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2017 Vector Creations Ltd
|
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
|
Copyright 2017 Vector Creations Ltd
|
||||||
|
Copyright 2018 New Vector Ltd
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -15,30 +16,24 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict';
|
import React from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
var React = require('react');
|
import sdk from '../../index';
|
||||||
var ReactDOM = require('react-dom');
|
|
||||||
var classNames = require('classnames');
|
|
||||||
var sdk = require('../../index');
|
|
||||||
import { Droppable } from 'react-beautiful-dnd';
|
import { Droppable } from 'react-beautiful-dnd';
|
||||||
import { _t } from '../../languageHandler';
|
import { _t } from '../../languageHandler';
|
||||||
var dis = require('../../dispatcher');
|
import dis from '../../dispatcher';
|
||||||
var Unread = require('../../Unread');
|
import Unread from '../../Unread';
|
||||||
var MatrixClientPeg = require('../../MatrixClientPeg');
|
import * as RoomNotifs from '../../RoomNotifs';
|
||||||
var RoomNotifs = require('../../RoomNotifs');
|
import * as FormattingUtils from '../../utils/FormattingUtils';
|
||||||
var FormattingUtils = require('../../utils/FormattingUtils');
|
|
||||||
var AccessibleButton = require('../../components/views/elements/AccessibleButton');
|
|
||||||
import Modal from '../../Modal';
|
|
||||||
import { KeyCode } from '../../Keyboard';
|
import { KeyCode } from '../../Keyboard';
|
||||||
|
|
||||||
|
|
||||||
// turn this on for drop & drag console debugging galore
|
// turn this on for drop & drag console debugging galore
|
||||||
var debug = false;
|
const debug = false;
|
||||||
|
|
||||||
const TRUNCATE_AT = 10;
|
const TRUNCATE_AT = 10;
|
||||||
|
|
||||||
var RoomSubList = React.createClass({
|
const RoomSubList = React.createClass({
|
||||||
displayName: 'RoomSubList',
|
displayName: 'RoomSubList',
|
||||||
|
|
||||||
debug: debug,
|
debug: debug,
|
||||||
|
@ -77,8 +72,10 @@ var RoomSubList = React.createClass({
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
return {
|
return {
|
||||||
onHeaderClick: function() {}, // NOP
|
onHeaderClick: function() {
|
||||||
onShowMoreRooms: function() {}, // NOP
|
}, // NOP
|
||||||
|
onShowMoreRooms: function() {
|
||||||
|
}, // NOP
|
||||||
extraTiles: [],
|
extraTiles: [],
|
||||||
isInvite: false,
|
isInvite: false,
|
||||||
};
|
};
|
||||||
|
@ -115,7 +112,7 @@ var RoomSubList = React.createClass({
|
||||||
// The header is collapsable if it is hidden or not stuck
|
// The header is collapsable if it is hidden or not stuck
|
||||||
// The dataset elements are added in the RoomList _initAndPositionStickyHeaders method
|
// The dataset elements are added in the RoomList _initAndPositionStickyHeaders method
|
||||||
isCollapsableOnClick: function() {
|
isCollapsableOnClick: function() {
|
||||||
var stuck = this.refs.header.dataset.stuck;
|
const stuck = this.refs.header.dataset.stuck;
|
||||||
if (this.state.hidden || stuck === undefined || stuck === "none") {
|
if (this.state.hidden || stuck === undefined || stuck === "none") {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -141,12 +138,12 @@ var RoomSubList = React.createClass({
|
||||||
onClick: function(ev) {
|
onClick: function(ev) {
|
||||||
if (this.isCollapsableOnClick()) {
|
if (this.isCollapsableOnClick()) {
|
||||||
// The header isCollapsable, so the click is to be interpreted as collapse and truncation logic
|
// The header isCollapsable, so the click is to be interpreted as collapse and truncation logic
|
||||||
var isHidden = !this.state.hidden;
|
const isHidden = !this.state.hidden;
|
||||||
this.setState({ hidden : isHidden });
|
this.setState({hidden: isHidden});
|
||||||
|
|
||||||
if (isHidden) {
|
if (isHidden) {
|
||||||
// as good a way as any to reset the truncate state
|
// as good a way as any to reset the truncate state
|
||||||
this.setState({ truncateAt : TRUNCATE_AT });
|
this.setState({truncateAt: TRUNCATE_AT});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.onShowMoreRooms();
|
this.props.onShowMoreRooms();
|
||||||
|
@ -161,7 +158,7 @@ var RoomSubList = React.createClass({
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_room',
|
action: 'view_room',
|
||||||
room_id: roomId,
|
room_id: roomId,
|
||||||
clear_search: (ev && (ev.keyCode == KeyCode.ENTER || ev.keyCode == KeyCode.SPACE)),
|
clear_search: (ev && (ev.keyCode === KeyCode.ENTER || ev.keyCode === KeyCode.SPACE)),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -171,17 +168,17 @@ var RoomSubList = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
_shouldShowMentionBadge: function(roomNotifState) {
|
_shouldShowMentionBadge: function(roomNotifState) {
|
||||||
return roomNotifState != RoomNotifs.MUTE;
|
return roomNotifState !== RoomNotifs.MUTE;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Total up all the notification counts from the rooms
|
* Total up all the notification counts from the rooms
|
||||||
*
|
*
|
||||||
* @param {Number} If supplied will only total notifications for rooms outside the truncation number
|
* @param {Number} truncateAt If supplied will only total notifications for rooms outside the truncation number
|
||||||
* @returns {Array} The array takes the form [total, highlight] where highlight is a bool
|
* @returns {Array} The array takes the form [total, highlight] where highlight is a bool
|
||||||
*/
|
*/
|
||||||
roomNotificationCount: function(truncateAt) {
|
roomNotificationCount: function(truncateAt) {
|
||||||
var self = this;
|
const self = this;
|
||||||
|
|
||||||
if (this.props.isInvite) {
|
if (this.props.isInvite) {
|
||||||
return [0, true];
|
return [0, true];
|
||||||
|
@ -189,9 +186,9 @@ var RoomSubList = React.createClass({
|
||||||
|
|
||||||
return this.props.list.reduce(function(result, room, index) {
|
return this.props.list.reduce(function(result, room, index) {
|
||||||
if (truncateAt === undefined || index >= truncateAt) {
|
if (truncateAt === undefined || index >= truncateAt) {
|
||||||
var roomNotifState = RoomNotifs.getRoomNotifsState(room.roomId);
|
const roomNotifState = RoomNotifs.getRoomNotifsState(room.roomId);
|
||||||
var highlight = room.getUnreadNotificationCount('highlight') > 0;
|
const highlight = room.getUnreadNotificationCount('highlight') > 0;
|
||||||
var notificationCount = room.getUnreadNotificationCount();
|
const notificationCount = room.getUnreadNotificationCount();
|
||||||
|
|
||||||
const notifBadges = notificationCount > 0 && self._shouldShowNotifBadge(roomNotifState);
|
const notifBadges = notificationCount > 0 && self._shouldShowNotifBadge(roomNotifState);
|
||||||
const mentionBadges = highlight && self._shouldShowMentionBadge(roomNotifState);
|
const mentionBadges = highlight && self._shouldShowMentionBadge(roomNotifState);
|
||||||
|
@ -241,29 +238,27 @@ var RoomSubList = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
_getHeaderJsx: function() {
|
_getHeaderJsx: function() {
|
||||||
var TintableSvg = sdk.getComponent("elements.TintableSvg");
|
const subListNotifications = this.roomNotificationCount();
|
||||||
|
const subListNotifCount = subListNotifications[0];
|
||||||
|
const subListNotifHighlight = subListNotifications[1];
|
||||||
|
|
||||||
var subListNotifications = this.roomNotificationCount();
|
const totalTiles = this.props.list.length + (this.props.extraTiles || []).length;
|
||||||
var subListNotifCount = subListNotifications[0];
|
const roomCount = totalTiles > 0 ? totalTiles : '';
|
||||||
var subListNotifHighlight = subListNotifications[1];
|
|
||||||
|
|
||||||
var totalTiles = this.props.list.length + (this.props.extraTiles || []).length;
|
const chevronClasses = classNames({
|
||||||
var roomCount = totalTiles > 0 ? totalTiles : '';
|
|
||||||
|
|
||||||
var chevronClasses = classNames({
|
|
||||||
'mx_RoomSubList_chevron': true,
|
'mx_RoomSubList_chevron': true,
|
||||||
'mx_RoomSubList_chevronRight': this.state.hidden,
|
'mx_RoomSubList_chevronRight': this.state.hidden,
|
||||||
'mx_RoomSubList_chevronDown': !this.state.hidden,
|
'mx_RoomSubList_chevronDown': !this.state.hidden,
|
||||||
});
|
});
|
||||||
|
|
||||||
var badgeClasses = classNames({
|
const badgeClasses = classNames({
|
||||||
'mx_RoomSubList_badge': true,
|
'mx_RoomSubList_badge': true,
|
||||||
'mx_RoomSubList_badgeHighlight': subListNotifHighlight,
|
'mx_RoomSubList_badgeHighlight': subListNotifHighlight,
|
||||||
});
|
});
|
||||||
|
|
||||||
var badge;
|
let badge;
|
||||||
if (subListNotifCount > 0) {
|
if (subListNotifCount > 0) {
|
||||||
badge = <div className={badgeClasses}>{ FormattingUtils.formatCount(subListNotifCount) }</div>;
|
badge = <div className={badgeClasses}>{FormattingUtils.formatCount(subListNotifCount)}</div>;
|
||||||
} else if (this.props.isInvite) {
|
} else if (this.props.isInvite) {
|
||||||
// no notifications but highlight anyway because this is an invite badge
|
// no notifications but highlight anyway because this is an invite badge
|
||||||
badge = <div className={badgeClasses}>!</div>;
|
badge = <div className={badgeClasses}>!</div>;
|
||||||
|
@ -271,7 +266,7 @@ var RoomSubList = React.createClass({
|
||||||
|
|
||||||
// When collapsed, allow a long hover on the header to show user
|
// When collapsed, allow a long hover on the header to show user
|
||||||
// the full tag name and room count
|
// the full tag name and room count
|
||||||
var title;
|
let title;
|
||||||
if (this.props.collapsed) {
|
if (this.props.collapsed) {
|
||||||
title = this.props.label;
|
title = this.props.label;
|
||||||
if (roomCount !== '') {
|
if (roomCount !== '') {
|
||||||
|
@ -279,63 +274,66 @@ var RoomSubList = React.createClass({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var incomingCall;
|
let incomingCall;
|
||||||
if (this.props.incomingCall) {
|
if (this.props.incomingCall) {
|
||||||
var self = this;
|
const self = this;
|
||||||
// Check if the incoming call is for this section
|
// Check if the incoming call is for this section
|
||||||
var incomingCallRoom = this.props.list.filter(function(room) {
|
const incomingCallRoom = this.props.list.filter(function(room) {
|
||||||
return self.props.incomingCall.roomId === room.roomId;
|
return self.props.incomingCall.roomId === room.roomId;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (incomingCallRoom.length === 1) {
|
if (incomingCallRoom.length === 1) {
|
||||||
var IncomingCallBox = sdk.getComponent("voip.IncomingCallBox");
|
const IncomingCallBox = sdk.getComponent("voip.IncomingCallBox");
|
||||||
incomingCall = <IncomingCallBox className="mx_RoomSubList_incomingCall" incomingCall={ this.props.incomingCall }/>;
|
incomingCall =
|
||||||
|
<IncomingCallBox className="mx_RoomSubList_incomingCall" incomingCall={this.props.incomingCall} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var tabindex = this.props.searchFilter === "" ? "0" : "-1";
|
const tabindex = this.props.searchFilter === "" ? "0" : "-1";
|
||||||
|
|
||||||
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
return (
|
return (
|
||||||
<div className="mx_RoomSubList_labelContainer" title={ title } ref="header">
|
<div className="mx_RoomSubList_labelContainer" title={title} ref="header">
|
||||||
<AccessibleButton onClick={ this.onClick } className="mx_RoomSubList_label" tabIndex={tabindex}>
|
<AccessibleButton onClick={this.onClick} className="mx_RoomSubList_label" tabIndex={tabindex}>
|
||||||
{ this.props.collapsed ? '' : this.props.label }
|
{this.props.collapsed ? '' : this.props.label}
|
||||||
<div className="mx_RoomSubList_roomCount">{ roomCount }</div>
|
<div className="mx_RoomSubList_roomCount">{roomCount}</div>
|
||||||
<div className={chevronClasses}></div>
|
<div className={chevronClasses} />
|
||||||
{ badge }
|
{badge}
|
||||||
{ incomingCall }
|
{incomingCall}
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
_createOverflowTile: function(overflowCount, totalCount) {
|
_createOverflowTile: function(overflowCount, totalCount) {
|
||||||
var content = <div className="mx_RoomSubList_chevronDown"></div>;
|
let content = <div className="mx_RoomSubList_chevronDown" />;
|
||||||
|
|
||||||
var overflowNotifications = this.roomNotificationCount(TRUNCATE_AT);
|
const overflowNotifications = this.roomNotificationCount(TRUNCATE_AT);
|
||||||
var overflowNotifCount = overflowNotifications[0];
|
const overflowNotifCount = overflowNotifications[0];
|
||||||
var overflowNotifHighlight = overflowNotifications[1];
|
const overflowNotifHighlight = overflowNotifications[1];
|
||||||
if (overflowNotifCount && !this.props.collapsed) {
|
if (overflowNotifCount && !this.props.collapsed) {
|
||||||
content = FormattingUtils.formatCount(overflowNotifCount);
|
content = FormattingUtils.formatCount(overflowNotifCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
var badgeClasses = classNames({
|
const badgeClasses = classNames({
|
||||||
'mx_RoomSubList_moreBadge': true,
|
'mx_RoomSubList_moreBadge': true,
|
||||||
'mx_RoomSubList_moreBadgeNotify': overflowNotifCount && !this.props.collapsed,
|
'mx_RoomSubList_moreBadgeNotify': overflowNotifCount && !this.props.collapsed,
|
||||||
'mx_RoomSubList_moreBadgeHighlight': overflowNotifHighlight && !this.props.collapsed,
|
'mx_RoomSubList_moreBadgeHighlight': overflowNotifHighlight && !this.props.collapsed,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
return (
|
return (
|
||||||
<AccessibleButton className="mx_RoomSubList_ellipsis" onClick={this._showFullMemberList}>
|
<AccessibleButton className="mx_RoomSubList_ellipsis" onClick={this._showFullMemberList}>
|
||||||
<div className="mx_RoomSubList_line"></div>
|
<div className="mx_RoomSubList_line" />
|
||||||
<div className="mx_RoomSubList_more">{ _t("more") }</div>
|
<div className="mx_RoomSubList_more">{_t("more")}</div>
|
||||||
<div className={ badgeClasses }>{ content }</div>
|
<div className={badgeClasses}>{content}</div>
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
_showFullMemberList: function() {
|
_showFullMemberList: function() {
|
||||||
this.setState({
|
this.setState({
|
||||||
truncateAt: -1
|
truncateAt: -1,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.props.onShowMoreRooms();
|
this.props.onShowMoreRooms();
|
||||||
|
@ -343,37 +341,39 @@ var RoomSubList = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var connectDropTarget = this.props.connectDropTarget;
|
const TruncatedList = sdk.getComponent('elements.TruncatedList');
|
||||||
var TruncatedList = sdk.getComponent('elements.TruncatedList');
|
|
||||||
|
|
||||||
var label = this.props.collapsed ? null : this.props.label;
|
|
||||||
|
|
||||||
let content;
|
let content;
|
||||||
if (this.state.sortedList.length === 0 && !this.props.searchFilter && this.props.extraTiles.length === 0) {
|
if (this.state.sortedList.length === 0 && this.props.extraTiles.length === 0) {
|
||||||
content = this.props.emptyContent;
|
// if no search filter is applied and there is a placeholder defined then show it, otherwise show nothing
|
||||||
|
if (!this.props.searchFilter && this.props.emptyContent) {
|
||||||
|
content = this.props.emptyContent;
|
||||||
|
} else {
|
||||||
|
// don't show an empty sublist
|
||||||
|
return null;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
content = this.makeRoomTiles();
|
content = this.makeRoomTiles();
|
||||||
content.push(...this.props.extraTiles);
|
content.push(...this.props.extraTiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.sortedList.length > 0 || this.props.extraTiles.length > 0 || this.props.editable) {
|
if (this.state.sortedList.length > 0 || this.props.extraTiles.length > 0 || this.props.editable) {
|
||||||
var subList;
|
let subList;
|
||||||
var classes = "mx_RoomSubList";
|
const classes = "mx_RoomSubList";
|
||||||
|
|
||||||
if (!this.state.hidden) {
|
if (!this.state.hidden) {
|
||||||
subList = <TruncatedList className={ classes } truncateAt={this.state.truncateAt}
|
subList = <TruncatedList className={classes} truncateAt={this.state.truncateAt}
|
||||||
createOverflowElement={this._createOverflowTile} >
|
createOverflowElement={this._createOverflowTile}>
|
||||||
{ content }
|
{content}
|
||||||
</TruncatedList>;
|
</TruncatedList>;
|
||||||
}
|
} else {
|
||||||
else {
|
subList = <TruncatedList className={classes}>
|
||||||
subList = <TruncatedList className={ classes }>
|
</TruncatedList>;
|
||||||
</TruncatedList>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const subListContent = <div>
|
const subListContent = <div>
|
||||||
{ this._getHeaderJsx() }
|
{this._getHeaderJsx()}
|
||||||
{ subList }
|
{subList}
|
||||||
</div>;
|
</div>;
|
||||||
|
|
||||||
return this.props.editable ?
|
return this.props.editable ?
|
||||||
|
@ -381,23 +381,22 @@ var RoomSubList = React.createClass({
|
||||||
droppableId={"room-sub-list-droppable_" + this.props.tagName}
|
droppableId={"room-sub-list-droppable_" + this.props.tagName}
|
||||||
type="draggable-RoomTile"
|
type="draggable-RoomTile"
|
||||||
>
|
>
|
||||||
{ (provided, snapshot) => (
|
{(provided, snapshot) => (
|
||||||
<div ref={provided.innerRef}>
|
<div ref={provided.innerRef}>
|
||||||
{ subListContent }
|
{subListContent}
|
||||||
</div>
|
</div>
|
||||||
) }
|
)}
|
||||||
</Droppable> : subListContent;
|
</Droppable> : subListContent;
|
||||||
}
|
} else {
|
||||||
else {
|
const Loader = sdk.getComponent("elements.Spinner");
|
||||||
var Loader = sdk.getComponent("elements.Spinner");
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_RoomSubList">
|
<div className="mx_RoomSubList">
|
||||||
{ this.props.alwaysShowHeader ? this._getHeaderJsx() : undefined }
|
{this.props.alwaysShowHeader ? this._getHeaderJsx() : undefined}
|
||||||
{ (this.props.showSpinner && !this.state.hidden) ? <Loader /> : undefined }
|
{(this.props.showSpinner && !this.state.hidden) ? <Loader /> : undefined}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = RoomSubList;
|
module.exports = RoomSubList;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
|
Copyright 2018 New Vector Ltd
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -618,9 +619,11 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_updatePreviewUrlVisibility: function(room) {
|
_updatePreviewUrlVisibility: function({roomId}) {
|
||||||
|
// URL Previews in E2EE rooms can be a privacy leak so use a different setting which is per-room explicit
|
||||||
|
const key = MatrixClientPeg.get().isRoomEncrypted(roomId) ? 'urlPreviewsEnabled_e2ee' : 'urlPreviewsEnabled';
|
||||||
this.setState({
|
this.setState({
|
||||||
showUrlPreview: SettingsStore.getValue("urlPreviewsEnabled", room.roomId),
|
showUrlPreview: SettingsStore.getValue(key, roomId),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -645,19 +648,23 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
onAccountData: function(event) {
|
onAccountData: function(event) {
|
||||||
if (event.getType() === "org.matrix.preview_urls" && this.state.room) {
|
const type = event.getType();
|
||||||
|
if ((type === "org.matrix.preview_urls" || type === "im.vector.web.settings") && this.state.room) {
|
||||||
|
// non-e2ee url previews are stored in legacy event type `org.matrix.room.preview_urls`
|
||||||
this._updatePreviewUrlVisibility(this.state.room);
|
this._updatePreviewUrlVisibility(this.state.room);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onRoomAccountData: function(event, room) {
|
onRoomAccountData: function(event, room) {
|
||||||
if (room.roomId == this.state.roomId) {
|
if (room.roomId == this.state.roomId) {
|
||||||
if (event.getType() === "org.matrix.room.color_scheme") {
|
const type = event.getType();
|
||||||
|
if (type === "org.matrix.room.color_scheme") {
|
||||||
const color_scheme = event.getContent();
|
const color_scheme = event.getContent();
|
||||||
// XXX: we should validate the event
|
// XXX: we should validate the event
|
||||||
console.log("Tinter.tint from onRoomAccountData");
|
console.log("Tinter.tint from onRoomAccountData");
|
||||||
Tinter.tint(color_scheme.primary_color, color_scheme.secondary_color);
|
Tinter.tint(color_scheme.primary_color, color_scheme.secondary_color);
|
||||||
} else if (event.getType() === "org.matrix.room.preview_urls") {
|
} else if (type === "org.matrix.room.preview_urls" || type === "im.vector.web.settings") {
|
||||||
|
// non-e2ee url previews are stored in legacy event type `org.matrix.room.preview_urls`
|
||||||
this._updatePreviewUrlVisibility(room);
|
this._updatePreviewUrlVisibility(room);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,9 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import {EventStatus} from 'matrix-js-sdk';
|
||||||
|
|
||||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
import dis from '../../../dispatcher';
|
import dis from '../../../dispatcher';
|
||||||
|
@ -220,7 +219,10 @@ module.exports = React.createClass({
|
||||||
let replyButton;
|
let replyButton;
|
||||||
let collapseReplyThread;
|
let collapseReplyThread;
|
||||||
|
|
||||||
if (eventStatus === 'not_sent') {
|
// status is SENT before remote-echo, null after
|
||||||
|
const isSent = !eventStatus || eventStatus === EventStatus.SENT;
|
||||||
|
|
||||||
|
if (eventStatus === EventStatus.NOT_SENT) {
|
||||||
resendButton = (
|
resendButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onResendClick}>
|
<div className="mx_MessageContextMenu_field" onClick={this.onResendClick}>
|
||||||
{ _t('Resend') }
|
{ _t('Resend') }
|
||||||
|
@ -228,7 +230,7 @@ module.exports = React.createClass({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!eventStatus && this.state.canRedact) {
|
if (isSent && this.state.canRedact) {
|
||||||
redactButton = (
|
redactButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onRedactClick}>
|
<div className="mx_MessageContextMenu_field" onClick={this.onRedactClick}>
|
||||||
{ _t('Remove') }
|
{ _t('Remove') }
|
||||||
|
@ -236,7 +238,7 @@ module.exports = React.createClass({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eventStatus === "queued" || eventStatus === "not_sent") {
|
if (eventStatus === EventStatus.QUEUED || eventStatus === EventStatus.NOT_SENT) {
|
||||||
cancelButton = (
|
cancelButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onCancelSendClick}>
|
<div className="mx_MessageContextMenu_field" onClick={this.onCancelSendClick}>
|
||||||
{ _t('Cancel Sending') }
|
{ _t('Cancel Sending') }
|
||||||
|
@ -244,7 +246,7 @@ module.exports = React.createClass({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!eventStatus && this.props.mxEvent.getType() === 'm.room.message') {
|
if (isSent && this.props.mxEvent.getType() === 'm.room.message') {
|
||||||
const content = this.props.mxEvent.getContent();
|
const content = this.props.mxEvent.getContent();
|
||||||
if (content.msgtype && content.msgtype !== 'm.bad.encrypted' && content.hasOwnProperty('body')) {
|
if (content.msgtype && content.msgtype !== 'm.bad.encrypted' && content.hasOwnProperty('body')) {
|
||||||
forwardButton = (
|
forwardButton = (
|
||||||
|
|
|
@ -72,14 +72,12 @@ export default React.createClass({
|
||||||
|
|
||||||
_updateRelatedGroups() {
|
_updateRelatedGroups() {
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
const relatedGroupsEvent = this.context.matrixClient
|
const room = this.context.matrixClient.getRoom(this.props.mxEvent.getRoomId());
|
||||||
.getRoom(this.props.mxEvent.getRoomId())
|
if (!room) return;
|
||||||
.currentState
|
|
||||||
.getStateEvents('m.room.related_groups', '');
|
const relatedGroupsEvent = room.currentState.getStateEvents('m.room.related_groups', '');
|
||||||
this.setState({
|
this.setState({
|
||||||
relatedGroups: relatedGroupsEvent ?
|
relatedGroups: relatedGroupsEvent ? relatedGroupsEvent.getContent().groups || [] : [],
|
||||||
relatedGroupsEvent.getContent().groups || []
|
|
||||||
: [],
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
Copyright 2017 Travis Ralston
|
Copyright 2017 Travis Ralston
|
||||||
|
Copyright 2018 New Vector Ltd
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -15,6 +16,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {MatrixClient} from "matrix-js-sdk";
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
const sdk = require("../../../index");
|
const sdk = require("../../../index");
|
||||||
|
@ -29,6 +31,10 @@ module.exports = React.createClass({
|
||||||
room: PropTypes.object,
|
room: PropTypes.object,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
contextTypes: {
|
||||||
|
matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
|
||||||
|
},
|
||||||
|
|
||||||
saveSettings: function() {
|
saveSettings: function() {
|
||||||
const promises = [];
|
const promises = [];
|
||||||
if (this.refs.urlPreviewsRoom) promises.push(this.refs.urlPreviewsRoom.save());
|
if (this.refs.urlPreviewsRoom) promises.push(this.refs.urlPreviewsRoom.save());
|
||||||
|
@ -39,42 +45,58 @@ module.exports = React.createClass({
|
||||||
render: function() {
|
render: function() {
|
||||||
const SettingsFlag = sdk.getComponent("elements.SettingsFlag");
|
const SettingsFlag = sdk.getComponent("elements.SettingsFlag");
|
||||||
const roomId = this.props.room.roomId;
|
const roomId = this.props.room.roomId;
|
||||||
|
const isEncrypted = this.context.matrixClient.isRoomEncrypted(roomId);
|
||||||
|
|
||||||
let previewsForAccount = null;
|
let previewsForAccount = null;
|
||||||
if (SettingsStore.getValueAt(SettingLevel.ACCOUNT, "urlPreviewsEnabled")) {
|
|
||||||
previewsForAccount = (
|
|
||||||
_t("You have <a>enabled</a> URL previews by default.", {}, { 'a': (sub)=><a href="#/settings">{ sub }</a> })
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
previewsForAccount = (
|
|
||||||
_t("You have <a>disabled</a> URL previews by default.", {}, { 'a': (sub)=><a href="#/settings">{ sub }</a> })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let previewsForRoom = null;
|
let previewsForRoom = null;
|
||||||
if (SettingsStore.canSetValue("urlPreviewsEnabled", roomId, "room")) {
|
|
||||||
previewsForRoom = (
|
if (!isEncrypted) {
|
||||||
<label>
|
// Only show account setting state and room state setting state in non-e2ee rooms where they apply
|
||||||
<SettingsFlag name="urlPreviewsEnabled"
|
const accountEnabled = SettingsStore.getValueAt(SettingLevel.ACCOUNT, "urlPreviewsEnabled");
|
||||||
level={SettingLevel.ROOM}
|
if (accountEnabled) {
|
||||||
roomId={this.props.room.roomId}
|
previewsForAccount = (
|
||||||
isExplicit={true}
|
_t("You have <a>enabled</a> URL previews by default.", {}, {
|
||||||
manualSave={true}
|
'a': (sub)=><a href="#/settings">{ sub }</a>,
|
||||||
ref="urlPreviewsRoom" />
|
})
|
||||||
</label>
|
);
|
||||||
);
|
} else if (accountEnabled) {
|
||||||
} else {
|
previewsForAccount = (
|
||||||
let str = _td("URL previews are enabled by default for participants in this room.");
|
_t("You have <a>disabled</a> URL previews by default.", {}, {
|
||||||
if (!SettingsStore.getValueAt(SettingLevel.ROOM, "urlPreviewsEnabled", roomId, /*explicit=*/true)) {
|
'a': (sub)=><a href="#/settings">{ sub }</a>,
|
||||||
str = _td("URL previews are disabled by default for participants in this room.");
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
previewsForRoom = (<label>{ _t(str) }</label>);
|
|
||||||
|
if (SettingsStore.canSetValue("urlPreviewsEnabled", roomId, "room")) {
|
||||||
|
previewsForRoom = (
|
||||||
|
<label>
|
||||||
|
<SettingsFlag name="urlPreviewsEnabled"
|
||||||
|
level={SettingLevel.ROOM}
|
||||||
|
roomId={roomId}
|
||||||
|
isExplicit={true}
|
||||||
|
manualSave={true}
|
||||||
|
ref="urlPreviewsRoom" />
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let str = _td("URL previews are enabled by default for participants in this room.");
|
||||||
|
if (!SettingsStore.getValueAt(SettingLevel.ROOM, "urlPreviewsEnabled", roomId, /*explicit=*/true)) {
|
||||||
|
str = _td("URL previews are disabled by default for participants in this room.");
|
||||||
|
}
|
||||||
|
previewsForRoom = (<label>{ _t(str) }</label>);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
previewsForAccount = (
|
||||||
|
_t("In encrypted rooms, like this one, URL previews are disabled by default to ensure that your " +
|
||||||
|
"homeserver (where the previews are generated) cannot gather information about links you see in " +
|
||||||
|
"this room.")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const previewsForRoomAccount = (
|
const previewsForRoomAccount = ( // in an e2ee room we use a special key to enforce per-room opt-in
|
||||||
<SettingsFlag name="urlPreviewsEnabled"
|
<SettingsFlag name={isEncrypted ? 'urlPreviewsEnabled_e2ee' : 'urlPreviewsEnabled'}
|
||||||
level={SettingLevel.ROOM_ACCOUNT}
|
level={SettingLevel.ROOM_ACCOUNT}
|
||||||
roomId={this.props.room.roomId}
|
roomId={roomId}
|
||||||
manualSave={true}
|
manualSave={true}
|
||||||
ref="urlPreviewsSelf"
|
ref="urlPreviewsSelf"
|
||||||
/>
|
/>
|
||||||
|
@ -83,8 +105,13 @@ module.exports = React.createClass({
|
||||||
return (
|
return (
|
||||||
<div className="mx_RoomSettings_toggles">
|
<div className="mx_RoomSettings_toggles">
|
||||||
<h3>{ _t("URL Previews") }</h3>
|
<h3>{ _t("URL Previews") }</h3>
|
||||||
|
<div>
|
||||||
<label>{ previewsForAccount }</label>
|
{ _t('When someone puts a URL in their message, a URL preview can be shown to give more ' +
|
||||||
|
'information about that link such as the title, description, and an image from the website.') }
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{ previewsForAccount }
|
||||||
|
</div>
|
||||||
{ previewsForRoom }
|
{ previewsForRoom }
|
||||||
<label>{ previewsForRoomAccount }</label>
|
<label>{ previewsForRoomAccount }</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -621,13 +621,14 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
|
|
||||||
switch (this.props.tileShape) {
|
switch (this.props.tileShape) {
|
||||||
case 'notif': {
|
case 'notif': {
|
||||||
|
const EmojiText = sdk.getComponent('elements.EmojiText');
|
||||||
const room = this.props.matrixClient.getRoom(this.props.mxEvent.getRoomId());
|
const room = this.props.matrixClient.getRoom(this.props.mxEvent.getRoomId());
|
||||||
return (
|
return (
|
||||||
<div className={classes}>
|
<div className={classes}>
|
||||||
<div className="mx_EventTile_roomName">
|
<div className="mx_EventTile_roomName">
|
||||||
<a href={permalink} onClick={this.onPermalinkClicked}>
|
<EmojiText element="a" href={permalink} onClick={this.onPermalinkClicked}>
|
||||||
{ room ? room.name : '' }
|
{ room ? room.name : '' }
|
||||||
</a>
|
</EmojiText>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_EventTile_senderDetails">
|
<div className="mx_EventTile_senderDetails">
|
||||||
{ avatar }
|
{ avatar }
|
||||||
|
|
|
@ -157,6 +157,7 @@ export default class MessageComposerInput extends React.Component {
|
||||||
this.setDisplayedCompletion = this.setDisplayedCompletion.bind(this);
|
this.setDisplayedCompletion = this.setDisplayedCompletion.bind(this);
|
||||||
this.onMarkdownToggleClicked = this.onMarkdownToggleClicked.bind(this);
|
this.onMarkdownToggleClicked = this.onMarkdownToggleClicked.bind(this);
|
||||||
this.onTextPasted = this.onTextPasted.bind(this);
|
this.onTextPasted = this.onTextPasted.bind(this);
|
||||||
|
this.focusComposer = this.focusComposer.bind(this);
|
||||||
|
|
||||||
const isRichtextEnabled = SettingsStore.getValue('MessageComposerInput.isRichTextEnabled');
|
const isRichtextEnabled = SettingsStore.getValue('MessageComposerInput.isRichTextEnabled');
|
||||||
|
|
||||||
|
@ -270,13 +271,12 @@ export default class MessageComposerInput extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
onAction = (payload) => {
|
onAction = (payload) => {
|
||||||
const editor = this.refs.editor;
|
|
||||||
let contentState = this.state.editorState.getCurrentContent();
|
let contentState = this.state.editorState.getCurrentContent();
|
||||||
|
|
||||||
switch (payload.action) {
|
switch (payload.action) {
|
||||||
case 'reply_to_event':
|
case 'reply_to_event':
|
||||||
case 'focus_composer':
|
case 'focus_composer':
|
||||||
editor.focus();
|
this.focusComposer();
|
||||||
break;
|
break;
|
||||||
case 'insert_mention': {
|
case 'insert_mention': {
|
||||||
// Pretend that we've autocompleted this user because keeping two code
|
// Pretend that we've autocompleted this user because keeping two code
|
||||||
|
@ -319,7 +319,7 @@ export default class MessageComposerInput extends React.Component {
|
||||||
let editorState = EditorState.push(this.state.editorState, contentState, 'insert-characters');
|
let editorState = EditorState.push(this.state.editorState, contentState, 'insert-characters');
|
||||||
editorState = EditorState.moveSelectionToEnd(editorState);
|
editorState = EditorState.moveSelectionToEnd(editorState);
|
||||||
this.onEditorContentChanged(editorState);
|
this.onEditorContentChanged(editorState);
|
||||||
editor.focus();
|
this.focusComposer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1155,6 +1155,10 @@ export default class MessageComposerInput extends React.Component {
|
||||||
this.handleKeyCommand('toggle-mode');
|
this.handleKeyCommand('toggle-mode');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
focusComposer() {
|
||||||
|
this.refs.editor.focus();
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const activeEditorState = this.state.originalEditorState || this.state.editorState;
|
const activeEditorState = this.state.originalEditorState || this.state.editorState;
|
||||||
|
|
||||||
|
@ -1179,7 +1183,7 @@ export default class MessageComposerInput extends React.Component {
|
||||||
activeEditorState.getCurrentContent().getBlocksAsArray());
|
activeEditorState.getCurrentContent().getBlocksAsArray());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_MessageComposer_input_wrapper">
|
<div className="mx_MessageComposer_input_wrapper" onClick={this.focusComposer}>
|
||||||
<div className="mx_MessageComposer_autocomplete_wrapper">
|
<div className="mx_MessageComposer_autocomplete_wrapper">
|
||||||
<ReplyPreview />
|
<ReplyPreview />
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
import Resend from './Resend';
|
import Resend from './Resend';
|
||||||
import sdk from './index';
|
import sdk from './index';
|
||||||
|
import dis from './dispatcher';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
|
|
||||||
|
@ -65,6 +66,10 @@ export function getUnknownDevicesForRoom(matrixClient, room) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function focusComposer() {
|
||||||
|
dis.dispatch({action: 'focus_composer'});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the UnknownDeviceDialog for a given room. The dialog will inform the user
|
* Show the UnknownDeviceDialog for a given room. The dialog will inform the user
|
||||||
* that messages they sent to this room have not been sent due to unknown devices
|
* that messages they sent to this room have not been sent due to unknown devices
|
||||||
|
@ -86,6 +91,7 @@ export function showUnknownDeviceDialogForMessages(matrixClient, room) {
|
||||||
sendAnywayLabel: _t("Send anyway"),
|
sendAnywayLabel: _t("Send anyway"),
|
||||||
sendLabel: _t("Send"),
|
sendLabel: _t("Send"),
|
||||||
onSend: onSendClicked,
|
onSend: onSendClicked,
|
||||||
|
onFinished: focusComposer,
|
||||||
}, 'mx_Dialog_unknownDevice');
|
}, 'mx_Dialog_unknownDevice');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -333,7 +333,9 @@
|
||||||
"You have <a>disabled</a> URL previews by default.": "You have <a>disabled</a> URL previews by default.",
|
"You have <a>disabled</a> URL previews by default.": "You have <a>disabled</a> URL previews by default.",
|
||||||
"URL previews are enabled by default for participants in this room.": "URL previews are enabled by default for participants in this room.",
|
"URL previews are enabled by default for participants in this room.": "URL previews are enabled by default for participants in this room.",
|
||||||
"URL previews are disabled by default for participants in this room.": "URL previews are disabled by default for participants in this room.",
|
"URL previews are disabled by default for participants in this room.": "URL previews are disabled by default for participants in this room.",
|
||||||
|
"In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.": "In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.",
|
||||||
"URL Previews": "URL Previews",
|
"URL Previews": "URL Previews",
|
||||||
|
"When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.": "When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.",
|
||||||
"Cannot add any more widgets": "Cannot add any more widgets",
|
"Cannot add any more widgets": "Cannot add any more widgets",
|
||||||
"The maximum permitted number of widgets have already been added to this room.": "The maximum permitted number of widgets have already been added to this room.",
|
"The maximum permitted number of widgets have already been added to this room.": "The maximum permitted number of widgets have already been added to this room.",
|
||||||
"Add a widget": "Add a widget",
|
"Add a widget": "Add a widget",
|
||||||
|
|
|
@ -66,10 +66,13 @@ function matrixLinkify(linkify) {
|
||||||
|
|
||||||
S_HASH_NAME_COLON.on(TT.DOMAIN, S_HASH_NAME_COLON_DOMAIN);
|
S_HASH_NAME_COLON.on(TT.DOMAIN, S_HASH_NAME_COLON_DOMAIN);
|
||||||
S_HASH_NAME_COLON.on(TT.LOCALHOST, S_ROOMALIAS); // accept #foo:localhost
|
S_HASH_NAME_COLON.on(TT.LOCALHOST, S_ROOMALIAS); // accept #foo:localhost
|
||||||
|
S_HASH_NAME_COLON.on(TT.TLD, S_ROOMALIAS); // accept #foo:com (mostly for (TLD|DOMAIN)+ mixing)
|
||||||
S_HASH_NAME_COLON_DOMAIN.on(TT.DOT, S_HASH_NAME_COLON_DOMAIN_DOT);
|
S_HASH_NAME_COLON_DOMAIN.on(TT.DOT, S_HASH_NAME_COLON_DOMAIN_DOT);
|
||||||
S_HASH_NAME_COLON_DOMAIN_DOT.on(TT.DOMAIN, S_HASH_NAME_COLON_DOMAIN);
|
S_HASH_NAME_COLON_DOMAIN_DOT.on(TT.DOMAIN, S_HASH_NAME_COLON_DOMAIN);
|
||||||
S_HASH_NAME_COLON_DOMAIN_DOT.on(TT.TLD, S_ROOMALIAS);
|
S_HASH_NAME_COLON_DOMAIN_DOT.on(TT.TLD, S_ROOMALIAS);
|
||||||
|
|
||||||
|
S_ROOMALIAS.on(TT.DOT, S_HASH_NAME_COLON_DOMAIN_DOT); // accept repeated TLDs (e.g .org.uk)
|
||||||
|
|
||||||
|
|
||||||
const USERID = function(value) {
|
const USERID = function(value) {
|
||||||
MultiToken.call(this, value);
|
MultiToken.call(this, value);
|
||||||
|
@ -107,10 +110,13 @@ function matrixLinkify(linkify) {
|
||||||
|
|
||||||
S_AT_NAME_COLON.on(TT.DOMAIN, S_AT_NAME_COLON_DOMAIN);
|
S_AT_NAME_COLON.on(TT.DOMAIN, S_AT_NAME_COLON_DOMAIN);
|
||||||
S_AT_NAME_COLON.on(TT.LOCALHOST, S_USERID); // accept @foo:localhost
|
S_AT_NAME_COLON.on(TT.LOCALHOST, S_USERID); // accept @foo:localhost
|
||||||
|
S_AT_NAME_COLON.on(TT.TLD, S_USERID); // accept @foo:com (mostly for (TLD|DOMAIN)+ mixing)
|
||||||
S_AT_NAME_COLON_DOMAIN.on(TT.DOT, S_AT_NAME_COLON_DOMAIN_DOT);
|
S_AT_NAME_COLON_DOMAIN.on(TT.DOT, S_AT_NAME_COLON_DOMAIN_DOT);
|
||||||
S_AT_NAME_COLON_DOMAIN_DOT.on(TT.DOMAIN, S_AT_NAME_COLON_DOMAIN);
|
S_AT_NAME_COLON_DOMAIN_DOT.on(TT.DOMAIN, S_AT_NAME_COLON_DOMAIN);
|
||||||
S_AT_NAME_COLON_DOMAIN_DOT.on(TT.TLD, S_USERID);
|
S_AT_NAME_COLON_DOMAIN_DOT.on(TT.TLD, S_USERID);
|
||||||
|
|
||||||
|
S_USERID.on(TT.DOT, S_AT_NAME_COLON_DOMAIN_DOT); // accept repeated TLDs (e.g .org.uk)
|
||||||
|
|
||||||
|
|
||||||
const GROUPID = function(value) {
|
const GROUPID = function(value) {
|
||||||
MultiToken.call(this, value);
|
MultiToken.call(this, value);
|
||||||
|
@ -148,9 +154,12 @@ function matrixLinkify(linkify) {
|
||||||
|
|
||||||
S_PLUS_NAME_COLON.on(TT.DOMAIN, S_PLUS_NAME_COLON_DOMAIN);
|
S_PLUS_NAME_COLON.on(TT.DOMAIN, S_PLUS_NAME_COLON_DOMAIN);
|
||||||
S_PLUS_NAME_COLON.on(TT.LOCALHOST, S_GROUPID); // accept +foo:localhost
|
S_PLUS_NAME_COLON.on(TT.LOCALHOST, S_GROUPID); // accept +foo:localhost
|
||||||
|
S_PLUS_NAME_COLON.on(TT.TLD, S_GROUPID); // accept +foo:com (mostly for (TLD|DOMAIN)+ mixing)
|
||||||
S_PLUS_NAME_COLON_DOMAIN.on(TT.DOT, S_PLUS_NAME_COLON_DOMAIN_DOT);
|
S_PLUS_NAME_COLON_DOMAIN.on(TT.DOT, S_PLUS_NAME_COLON_DOMAIN_DOT);
|
||||||
S_PLUS_NAME_COLON_DOMAIN_DOT.on(TT.DOMAIN, S_PLUS_NAME_COLON_DOMAIN);
|
S_PLUS_NAME_COLON_DOMAIN_DOT.on(TT.DOMAIN, S_PLUS_NAME_COLON_DOMAIN);
|
||||||
S_PLUS_NAME_COLON_DOMAIN_DOT.on(TT.TLD, S_GROUPID);
|
S_PLUS_NAME_COLON_DOMAIN_DOT.on(TT.TLD, S_GROUPID);
|
||||||
|
|
||||||
|
S_GROUPID.on(TT.DOT, S_PLUS_NAME_COLON_DOMAIN_DOT); // accept repeated TLDs (e.g .org.uk)
|
||||||
}
|
}
|
||||||
|
|
||||||
// stubs, overwritten in MatrixChat's componentDidMount
|
// stubs, overwritten in MatrixChat's componentDidMount
|
||||||
|
|
|
@ -245,6 +245,13 @@ export const SETTINGS = {
|
||||||
},
|
},
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
"urlPreviewsEnabled_e2ee": {
|
||||||
|
supportedLevels: ['room-device', 'room-account'],
|
||||||
|
displayName: {
|
||||||
|
"room-account": _td("Enable URL previews for this room (only affects you)"),
|
||||||
|
},
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
"roomColor": {
|
"roomColor": {
|
||||||
supportedLevels: LEVELS_ROOM_SETTINGS_WITH_ROOM,
|
supportedLevels: LEVELS_ROOM_SETTINGS_WITH_ROOM,
|
||||||
displayName: _td("Room Colour"),
|
displayName: _td("Room Colour"),
|
||||||
|
|
|
@ -74,7 +74,7 @@ export default class RoomAccountSettingsHandler extends SettingsHandler {
|
||||||
return cli !== undefined && cli !== null;
|
return cli !== undefined && cli !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getSettings(roomId, eventType = "im.vector.settings") {
|
_getSettings(roomId, eventType = "im.vector.web.settings") {
|
||||||
const room = MatrixClientPeg.get().getRoom(roomId);
|
const room = MatrixClientPeg.get().getRoom(roomId);
|
||||||
if (!room) return null;
|
if (!room) return null;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue