@@ -212,7 +207,6 @@ class PasswordLogin extends React.Component {
value={this.state.phoneCountry}
isSmall={true}
showPrefix={true}
- disabled={disabled}
/>
;
}
@@ -257,27 +250,35 @@ class PasswordLogin extends React.Component {
;
}
- let matrixIdText = _t('Matrix ID');
+ let yourMatrixAccountText = _t('Your account');
if (this.props.hsName) {
- matrixIdText = _t('%(serverName)s Matrix ID', {serverName: this.props.hsName});
+ yourMatrixAccountText = _t('Your %(serverName)s account', {serverName: this.props.hsName});
} else {
try {
const parsedHsUrl = new URL(this.props.hsUrl);
- matrixIdText = _t('%(serverName)s Matrix ID', {serverName: parsedHsUrl.hostname});
+ yourMatrixAccountText = _t('Your %(serverName)s account', {serverName: parsedHsUrl.hostname});
} catch (e) {
// ignore
}
}
+ let editLink = null;
+ if (this.props.onEditServerDetailsClick) {
+ editLink =
@@ -313,7 +313,7 @@ module.exports = React.createClass({
{ emailSection }
{ phoneSection }
diff --git a/src/components/views/auth/ServerConfig.js b/src/components/views/auth/ServerConfig.js
index f20695e9e2..fb35104e49 100644
--- a/src/components/views/auth/ServerConfig.js
+++ b/src/components/views/auth/ServerConfig.js
@@ -1,5 +1,6 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
+Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,21 +15,18 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-'use strict';
-
-const React = require('react');
+import React from 'react';
import PropTypes from 'prop-types';
-const Modal = require('../../../Modal');
-const sdk = require('../../../index');
+import Modal from '../../../Modal';
+import sdk from '../../../index';
import { _t } from '../../../languageHandler';
-/**
+/*
* A pure UI component which displays the HS and IS to use.
*/
-module.exports = React.createClass({
- displayName: 'ServerConfig',
- propTypes: {
+export default class ServerConfig extends React.PureComponent {
+ static propTypes = {
onServerConfigChange: PropTypes.func,
// default URLs are defined in config.json (or the hardcoded defaults)
@@ -45,155 +43,103 @@ module.exports = React.createClass({
customHsUrl: PropTypes.string,
customIsUrl: PropTypes.string,
- withToggleButton: PropTypes.bool,
delayTimeMs: PropTypes.number, // time to wait before invoking onChanged
- },
+ }
- getDefaultProps: function() {
- return {
- onServerConfigChange: function() {},
- customHsUrl: "",
- customIsUrl: "",
- withToggleButton: false,
- delayTimeMs: 0,
+ static defaultProps = {
+ onServerConfigChange: function() {},
+ customHsUrl: "",
+ customIsUrl: "",
+ delayTimeMs: 0,
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ hsUrl: props.customHsUrl,
+ isUrl: props.customIsUrl,
};
- },
+ }
- getInitialState: function() {
- return {
- hs_url: this.props.customHsUrl,
- is_url: this.props.customIsUrl,
- // if withToggleButton is false, then show the config all the time given we have no way otherwise of making it visible
- configVisible: !this.props.withToggleButton ||
- (this.props.customHsUrl !== this.props.defaultHsUrl) ||
- (this.props.customIsUrl !== this.props.defaultIsUrl),
- };
- },
-
- componentWillReceiveProps: function(newProps) {
- if (newProps.customHsUrl === this.state.hs_url &&
- newProps.customIsUrl === this.state.is_url) return;
+ componentWillReceiveProps(newProps) {
+ if (newProps.customHsUrl === this.state.hsUrl &&
+ newProps.customIsUrl === this.state.isUrl) return;
this.setState({
- hs_url: newProps.customHsUrl,
- is_url: newProps.customIsUrl,
- configVisible: !newProps.withToggleButton ||
- (newProps.customHsUrl !== newProps.defaultHsUrl) ||
- (newProps.customIsUrl !== newProps.defaultIsUrl),
+ hsUrl: newProps.customHsUrl,
+ isUrl: newProps.customIsUrl,
});
this.props.onServerConfigChange({
hsUrl: newProps.customHsUrl,
isUrl: newProps.customIsUrl,
});
- },
+ }
- onHomeserverChanged: function(ev) {
- this.setState({hs_url: ev.target.value}, () => {
+ onHomeserverChanged = (ev) => {
+ this.setState({hsUrl: ev.target.value}, () => {
this._hsTimeoutId = this._waitThenInvoke(this._hsTimeoutId, () => {
- let hsUrl = this.state.hs_url.trim().replace(/\/$/, "");
+ let hsUrl = this.state.hsUrl.trim().replace(/\/$/, "");
if (hsUrl === "") hsUrl = this.props.defaultHsUrl;
this.props.onServerConfigChange({
- hsUrl: this.state.hs_url,
- isUrl: this.state.is_url,
+ hsUrl: this.state.hsUrl,
+ isUrl: this.state.isUrl,
});
});
});
- },
+ }
- onIdentityServerChanged: function(ev) {
- this.setState({is_url: ev.target.value}, () => {
+ onIdentityServerChanged = (ev) => {
+ this.setState({isUrl: ev.target.value}, () => {
this._isTimeoutId = this._waitThenInvoke(this._isTimeoutId, () => {
- let isUrl = this.state.is_url.trim().replace(/\/$/, "");
+ let isUrl = this.state.isUrl.trim().replace(/\/$/, "");
if (isUrl === "") isUrl = this.props.defaultIsUrl;
this.props.onServerConfigChange({
- hsUrl: this.state.hs_url,
- isUrl: this.state.is_url,
+ hsUrl: this.state.hsUrl,
+ isUrl: this.state.isUrl,
});
});
});
- },
+ }
- _waitThenInvoke: function(existingTimeoutId, fn) {
+ _waitThenInvoke(existingTimeoutId, fn) {
if (existingTimeoutId) {
clearTimeout(existingTimeoutId);
}
return setTimeout(fn.bind(this), this.props.delayTimeMs);
- },
+ }
- onServerConfigVisibleChange: function(visible, ev) {
- this.setState({
- configVisible: visible,
- });
- if (!visible) {
- this.props.onServerConfigChange({
- hsUrl: this.props.defaultHsUrl,
- isUrl: this.props.defaultIsUrl,
- });
- } else {
- this.props.onServerConfigChange({
- hsUrl: this.state.hs_url,
- isUrl: this.state.is_url,
- });
- }
- },
-
- showHelpPopup: function() {
+ showHelpPopup = () => {
const CustomServerDialog = sdk.getComponent('auth.CustomServerDialog');
Modal.createTrackedDialog('Custom Server Dialog', '', CustomServerDialog);
- },
+ }
- render: function() {
- const serverConfigStyle = {};
- serverConfigStyle.display = this.state.configVisible ? 'block' : 'none';
-
- let toggleButton;
- if (this.props.withToggleButton) {
- toggleButton = (
-
-
-
- { _t("Default server") }
-
-
-
-
- { _t("Custom server") }
-
-
- );
- }
+ render() {
+ const Field = sdk.getComponent('elements.Field');
return (
-
- { toggleButton }
-
);
- },
-});
+ }
+}
diff --git a/src/components/views/auth/ServerTypeSelector.js b/src/components/views/auth/ServerTypeSelector.js
new file mode 100644
index 0000000000..de76e6acf9
--- /dev/null
+++ b/src/components/views/auth/ServerTypeSelector.js
@@ -0,0 +1,148 @@
+/*
+Copyright 2019 New Vector Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import { _t } from '../../../languageHandler';
+import sdk from '../../../index';
+import classnames from 'classnames';
+
+const MODULAR_URL = 'https://modular.im/?utm_source=riot-web&utm_medium=web&utm_campaign=riot-web-authentication';
+
+export const FREE = 'Free';
+export const PREMIUM = 'Premium';
+export const ADVANCED = 'Advanced';
+
+export const TYPES = {
+ FREE: {
+ id: FREE,
+ label: () => _t('Free'),
+ logo: () =>
,
+ description: () => _t('Join millions for free on the largest public server'),
+ hsUrl: 'https://matrix.org',
+ isUrl: 'https://vector.im',
+ },
+ PREMIUM: {
+ id: PREMIUM,
+ label: () => _t('Premium'),
+ logo: () =>
,
+ description: () => _t('Premium hosting for organisations
Learn more ', {}, {
+ a: sub =>
+ {sub}
+ ,
+ }),
+ },
+ ADVANCED: {
+ id: ADVANCED,
+ label: () => _t('Advanced'),
+ logo: () =>
+
+ {_t('Other')}
+
,
+ description: () => _t('Find other public servers or use a custom server'),
+ },
+};
+
+function getDefaultType(defaultHsUrl) {
+ if (!defaultHsUrl) {
+ return null;
+ } else if (defaultHsUrl === TYPES.FREE.hsUrl) {
+ return FREE;
+ } else if (new URL(defaultHsUrl).hostname.endsWith('.modular.im')) {
+ // TODO: Use a Riot config parameter to detect Modular-ness.
+ // https://github.com/vector-im/riot-web/issues/8253
+ return PREMIUM;
+ } else {
+ return ADVANCED;
+ }
+}
+
+export default class ServerTypeSelector extends React.PureComponent {
+ static propTypes = {
+ // The default HS URL as another way to set the initially selected type.
+ defaultHsUrl: PropTypes.string,
+ // Handler called when the selected type changes.
+ onChange: PropTypes.func.isRequired,
+ }
+
+ constructor(props) {
+ super(props);
+
+ const {
+ defaultHsUrl,
+ onChange,
+ } = props;
+ const type = getDefaultType(defaultHsUrl);
+ this.state = {
+ selected: type,
+ };
+ if (onChange) {
+ onChange(type);
+ }
+ }
+
+ updateSelectedType(type) {
+ if (this.state.selected === type) {
+ return;
+ }
+ this.setState({
+ selected: type,
+ });
+ if (this.props.onChange) {
+ this.props.onChange(type);
+ }
+ }
+
+ onClick = (e) => {
+ e.stopPropagation();
+ const type = e.currentTarget.dataset.id;
+ this.updateSelectedType(type);
+ }
+
+ render() {
+ const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
+
+ const serverTypes = [];
+ for (const type of Object.values(TYPES)) {
+ const { id, label, logo, description } = type;
+ const classes = classnames(
+ "mx_ServerTypeSelector_type",
+ `mx_ServerTypeSelector_type_${id}`,
+ {
+ "mx_ServerTypeSelector_type_selected": id === this.state.selected,
+ },
+ );
+
+ serverTypes.push(
+
+ {label()}
+
+
+
+ {logo()}
+
+
+ {description()}
+
+
+
);
+ }
+
+ return
+ {serverTypes}
+
;
+ }
+}
diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js
index 00a2c30ccc..12c692b958 100644
--- a/src/components/views/dialogs/UserSettingsDialog.js
+++ b/src/components/views/dialogs/UserSettingsDialog.js
@@ -28,6 +28,7 @@ import NotificationSettingsTab from "../settings/tabs/NotificationSettingsTab";
import PreferencesSettingsTab from "../settings/tabs/PreferencesSettingsTab";
import VoiceSettingsTab from "../settings/tabs/VoiceSettingsTab";
import HelpSettingsTab from "../settings/tabs/HelpSettingsTab";
+import FlairSettingsTab from "../settings/tabs/FlairSettingsTab";
// TODO: Ditch this whole component
export class TempTab extends React.Component {
@@ -58,6 +59,11 @@ export default class UserSettingsDialog extends React.Component {
"mx_UserSettingsDialog_settingsIcon",
,
));
+ tabs.push(new Tab(
+ _td("Flair"),
+ "mx_UserSettingsDialog_flairIcon",
+
,
+ ));
tabs.push(new Tab(
_td("Notifications"),
"mx_UserSettingsDialog_bellIcon",
diff --git a/src/components/views/groups/GroupPublicityToggle.js b/src/components/views/groups/GroupPublicityToggle.js
index ff0fc553b8..e27bf9a9d5 100644
--- a/src/components/views/groups/GroupPublicityToggle.js
+++ b/src/components/views/groups/GroupPublicityToggle.js
@@ -18,7 +18,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import sdk from '../../../index';
import GroupStore from '../../../stores/GroupStore';
-import { _t } from '../../../languageHandler.js';
+import ToggleSwitch from "../elements/ToggleSwitch";
export default React.createClass({
displayName: 'GroupPublicityToggle',
@@ -52,8 +52,7 @@ export default React.createClass({
if (this._groupStoreToken) this._groupStoreToken.unregister();
},
- _onPublicityToggle: function(e) {
- e.stopPropagation();
+ _onPublicityToggle: function() {
this.setState({
busy: true,
// Optimistic early update
@@ -68,21 +67,11 @@ export default React.createClass({
render() {
const GroupTile = sdk.getComponent('groups.GroupTile');
- const input =
;
- const labelText = !this.state.ready ? _t("Loading...") :
- (this.state.isGroupPublicised ?
- _t("Flair will appear if enabled in room settings") :
- _t("Flair will not appear")
- );
return
-
- { input }
- { labelText }
-
+
;
},
});
diff --git a/src/components/views/groups/GroupUserSettings.js b/src/components/views/groups/GroupUserSettings.js
index a349a34caf..210fca404a 100644
--- a/src/components/views/groups/GroupUserSettings.js
+++ b/src/components/views/groups/GroupUserSettings.js
@@ -43,9 +43,9 @@ export default React.createClass({
});
},
- _renderGroupPublicity() {
+ render() {
let text = "";
- let scrollbox =
;
+ let groupPublicityToggles = null;
const groups = this.state.groups;
if (this.state.error) {
@@ -54,16 +54,10 @@ export default React.createClass({
text = _t('Loading...');
} else if (groups.length > 0) {
const GroupPublicityToggle = sdk.getComponent('groups.GroupPublicityToggle');
- const GeminiScrollbarWrapper = sdk.getComponent('elements.GeminiScrollbarWrapper');
- const groupPublicityToggles = groups.map((groupId, index) => {
+ groupPublicityToggles = groups.map((groupId, index) => {
return
;
});
text = _t('Display your community flair in rooms configured to show it.');
- scrollbox =
-
- { groupPublicityToggles }
-
-
;
} else {
text = _t("You're not currently a member of any communities.");
}
@@ -71,16 +65,10 @@ export default React.createClass({
return (
{ text }
- { scrollbox }
+
+ { groupPublicityToggles }
+
);
},
-
- render() {
- const groupPublicity = this._renderGroupPublicity();
-
- return
- { groupPublicity }
-
;
- },
});
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index 4f92d0cad6..44fb14476a 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -82,7 +82,11 @@ module.exports = React.createClass({
this.collapsedState = collapsedJson ? JSON.parse(collapsedJson) : {};
this._layoutSections = [];
- this._layout = new Layout((key, size) => {
+ const unfilteredOptions = {
+ allowWhitespace: true,
+ handleHeight: 1,
+ };
+ this._unfilteredlayout = new Layout((key, size) => {
const subList = this._subListRefs[key];
if (subList) {
subList.setHeight(size);
@@ -95,7 +99,19 @@ module.exports = React.createClass({
window.localStorage.setItem("mx_roomlist_sizes",
JSON.stringify(this.subListSizes));
}
- }, this.subListSizes, this.collapsedState);
+ }, this.subListSizes, this.collapsedState, unfilteredOptions);
+
+ this._filteredLayout = new Layout((key, size) => {
+ const subList = this._subListRefs[key];
+ if (subList) {
+ subList.setHeight(size);
+ }
+ }, null, null, {
+ allowWhitespace: false,
+ handleHeight: 0,
+ });
+
+ this._layout = this._unfilteredlayout;
return {
isLoadingLeftRooms: false,
@@ -187,15 +203,21 @@ module.exports = React.createClass({
},
componentDidUpdate: function(prevProps) {
+ let forceLayoutUpdate = false;
this._repositionIncomingCallBox(undefined, false);
- // if (this.props.searchFilter !== prevProps.searchFilter) {
- // this._checkSubListsOverflow();
- // }
+ if (!this.props.searchFilter && prevProps.searchFilter) {
+ this._layout = this._unfilteredlayout;
+ forceLayoutUpdate = true;
+ } else if (this.props.searchFilter && !prevProps.searchFilter) {
+ this._layout = this._filteredLayout;
+ forceLayoutUpdate = true;
+ }
this._layout.update(
this._layoutSections,
this.resizeContainer && this.resizeContainer.clientHeight,
+ forceLayoutUpdate,
);
- // TODO: call layout.setAvailableHeight, window height was changed when bannerShown prop was changed
+ this._checkSubListsOverflow();
},
onAction: function(payload) {
@@ -617,7 +639,7 @@ module.exports = React.createClass({
onHeaderClick(collapsed);
}
};
- const startAsHidden = props.startAsHidden || this.collapsedState[chosenKey];
+ let startAsHidden = props.startAsHidden || this.collapsedState[chosenKey];
this._layoutSections.push({
id: chosenKey,
count: len,
@@ -625,6 +647,7 @@ module.exports = React.createClass({
let subList = (
+ {_t("Flair")}
+
+
+
+
+
+
+ );
+ }
+}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 7fe8a27956..4ae95f7559 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -4,7 +4,7 @@
"Failed to verify email address: make sure you clicked the link in the email": "Failed to verify email address: make sure you clicked the link in the email",
"The platform you're on": "The platform you're on",
"The version of Riot.im": "The version of Riot.im",
- "Whether or not you're logged in (we don't record your user name)": "Whether or not you're logged in (we don't record your user name)",
+ "Whether or not you're logged in (we don't record your username)": "Whether or not you're logged in (we don't record your username)",
"Your language of choice": "Your language of choice",
"Which officially provided instance you are using, if any": "Which officially provided instance you are using, if any",
"Whether or not you're using the Richtext mode of the Rich Text Editor": "Whether or not you're using the Richtext mode of the Rich Text Editor",
@@ -305,7 +305,7 @@
"Uploading report": "Uploading report",
"Waiting for response from server": "Waiting for response from server",
"Messages containing my display name": "Messages containing my display name",
- "Messages containing my user name": "Messages containing my user name",
+ "Messages containing my username": "Messages containing my username",
"Messages containing @room": "Messages containing @room",
"Messages in one-to-one chats": "Messages in one-to-one chats",
"Encrypted messages in one-to-one chats": "Encrypted messages in one-to-one chats",
@@ -1170,9 +1170,8 @@
"Robot check is currently unavailable on desktop - please use a
web browser ": "Robot check is currently unavailable on desktop - please use a
web browser ",
"This Home Server would like to make sure you are not a robot": "This Home Server would like to make sure you are not a robot",
"Custom Server Options": "Custom Server Options",
- "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.",
- "This allows you to use this app with an existing Matrix account on a different home server.": "This allows you to use this app with an existing Matrix account on a different home server.",
- "You can also set a custom identity server but this will typically prevent interaction with users based on email address.": "You can also set a custom identity server but this will typically prevent interaction with users based on email address.",
+ "You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use this app with an existing Matrix account on a different homeserver.": "You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use this app with an existing Matrix account on a different homeserver.",
+ "You can also set a custom identity server, but you won't be able to invite users by email address, or be invited by email address yourself.": "You can also set a custom identity server, but you won't be able to invite users by email address, or be invited by email address yourself.",
"To continue, please enter your password.": "To continue, please enter your password.",
"Password:": "Password:",
"Please review and accept all of the homeserver's policies": "Please review and accept all of the homeserver's policies",
@@ -1184,25 +1183,34 @@
"Please enter the code it contains:": "Please enter the code it contains:",
"Code": "Code",
"Start authentication": "Start authentication",
+ "Your Modular server": "Your Modular server",
+ "Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of
modular.im .": "Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of
modular.im .",
+ "Server Name": "Server Name",
"The email field must not be blank.": "The email field must not be blank.",
- "The user name field must not be blank.": "The user name field must not be blank.",
+ "The username field must not be blank.": "The username field must not be blank.",
"The phone number field must not be blank.": "The phone number field must not be blank.",
"The password field must not be blank.": "The password field must not be blank.",
"Username on %(hs)s": "Username on %(hs)s",
- "User name": "User name",
+ "Username": "Username",
"Mobile phone number": "Mobile phone number",
"Not sure of your password?
Set a new one ": "Not sure of your password?
Set a new one ",
- "%(serverName)s Matrix ID": "%(serverName)s Matrix ID",
+ "Your account": "Your account",
+ "Your %(serverName)s account": "Your %(serverName)s account",
"Sign in with": "Sign in with",
"Sign in": "Sign in",
"If you don't specify an email address, you won't be able to reset your password. Are you sure?": "If you don't specify an email address, you won't be able to reset your password. Are you sure?",
"Email address (optional)": "Email address (optional)",
"Mobile phone number (optional)": "Mobile phone number (optional)",
- "Default server": "Default server",
- "Custom server": "Custom server",
- "Home server URL": "Home server URL",
- "Identity server URL": "Identity server URL",
- "What does this mean?": "What does this mean?",
+ "Other servers": "Other servers",
+ "Enter custom server URLs
What does this mean? ": "Enter custom server URLs
What does this mean? ",
+ "Homeserver URL": "Homeserver URL",
+ "Identity Server URL": "Identity Server URL",
+ "Free": "Free",
+ "Join millions for free on the largest public server": "Join millions for free on the largest public server",
+ "Premium": "Premium",
+ "Premium hosting for organisations
Learn more ": "Premium hosting for organisations
Learn more ",
+ "Other": "Other",
+ "Find other public servers or use a custom server": "Find other public servers or use a custom server",
"Sorry, your browser is
not able to run Riot.": "Sorry, your browser is
not able to run Riot.",
"Riot uses many advanced browser features, some of which are not available or experimental in your current browser.": "Riot uses many advanced browser features, some of which are not available or experimental in your current browser.",
"Please install
Chrome or
Firefox for the best experience.": "Please install
Chrome or
Firefox for the best experience.",
@@ -1405,7 +1413,7 @@
"This doesn't look like a valid phone number.": "This doesn't look like a valid phone number.",
"An email address is required to register on this homeserver.": "An email address is required to register on this homeserver.",
"A phone number is required to register on this homeserver.": "A phone number is required to register on this homeserver.",
- "You need to enter a user name.": "You need to enter a user name.",
+ "You need to enter a username.": "You need to enter a username.",
"An unknown error occurred.": "An unknown error occurred.",
"Create your account": "Create your account",
"Commands": "Commands",
diff --git a/src/notifications/VectorPushRulesDefinitions.js b/src/notifications/VectorPushRulesDefinitions.js
index 3df2e70774..402a69e7a6 100644
--- a/src/notifications/VectorPushRulesDefinitions.js
+++ b/src/notifications/VectorPushRulesDefinitions.js
@@ -83,7 +83,7 @@ module.exports = {
// Messages containing user's username (localpart/MXID)
".m.rule.contains_user_name": new VectorPushRuleDefinition({
kind: "override",
- description: _td("Messages containing my user name"), // passed through _t() translation in src/components/views/settings/Notifications.js
+ description: _td("Messages containing my username"), // passed through _t() translation in src/components/views/settings/Notifications.js
vectorStateToActions: { // The actions for each vector state, or null to disable the rule.
on: StandardActions.ACTION_NOTIFY,
loud: StandardActions.ACTION_HIGHLIGHT_DEFAULT_SOUND,
diff --git a/src/resizer/distributors/roomsublist2.js b/src/resizer/distributors/roomsublist2.js
index d6bf8d6c7a..e70e6893c6 100644
--- a/src/resizer/distributors/roomsublist2.js
+++ b/src/resizer/distributors/roomsublist2.js
@@ -16,9 +16,6 @@ limitations under the License.
import FixedDistributor from "./fixed";
-// const allowWhitespace = true;
-const handleHeight = 1;
-
function clamp(height, min, max) {
if (height > max) return max;
if (height < min) return min;
@@ -26,7 +23,7 @@ function clamp(height, min, max) {
}
export class Layout {
- constructor(applyHeight, initialSizes, collapsedState) {
+ constructor(applyHeight, initialSizes, collapsedState, options) {
// callback to set height of section
this._applyHeight = applyHeight;
// list of {id, count} objects,
@@ -41,6 +38,17 @@ export class Layout {
this._sectionHeights = Object.assign({}, initialSizes);
// in-progress heights, while dragging. Committed on mouse-up.
this._heights = [];
+ // use while manually resizing to cancel
+ // the resize for a given mouse position
+ // when the previous resize made the layout
+ // constrained
+ this._clampedOffset = 0;
+ // used while manually resizing, to clear
+ // _clampedOffset when the direction of resizing changes
+ this._lastOffset = 0;
+
+ this._allowWhitespace = options && options.allowWhitespace;
+ this._handleHeight = (options && options.handleHeight) || 0;
}
setAvailableHeight(newSize) {
@@ -60,7 +68,7 @@ export class Layout {
this._applyNewSize();
}
- update(sections, availableHeight) {
+ update(sections, availableHeight, force = false) {
let heightChanged = false;
if (Number.isFinite(availableHeight) && availableHeight !== this._availableHeight) {
@@ -75,7 +83,7 @@ export class Layout {
return a.id !== b.id || a.count !== b.count;
});
- if (!heightChanged && !sectionsChanged) {
+ if (!heightChanged && !sectionsChanged && !force) {
return;
}
@@ -104,7 +112,7 @@ export class Layout {
const collapsed = this._collapsedState[section.id];
return count + (collapsed ? 0 : 1);
}, 0);
- return this._availableHeight - ((nonCollapsedSectionCount - 1) * handleHeight);
+ return this._availableHeight - ((nonCollapsedSectionCount - 1) * this._handleHeight);
}
_applyNewSize() {
@@ -130,9 +138,10 @@ export class Layout {
if (collapsed) {
return this._sectionHeight(0);
+ } else if (!this._allowWhitespace) {
+ return this._sectionHeight(section.count);
} else {
return 100000;
- // return this._sectionHeight(section.count);
}
}
@@ -268,6 +277,22 @@ export class Layout {
this._sectionHeights[section.id] = this._heights[i];
});
}
+
+ _setUncommittedSectionHeight(sectionIndex, offset) {
+ if (Math.sign(offset) != Math.sign(this._lastOffset)) {
+ this._clampedOffset = undefined;
+ }
+ if (this._clampedOffset !== undefined) {
+ if (offset < 0 && offset < this._clampedOffset) {
+ return;
+ }
+ if (offset > 0 && offset > this._clampedOffset) {
+ return;
+ }
+ }
+ this._clampedOffset = this._relayout(sectionIndex, offset);
+ this._lastOffset = offset;
+ }
}
class Handle {
@@ -278,7 +303,10 @@ class Handle {
}
setHeight(height) {
- this._layout._relayout(this._sectionIndex, height - this._initialHeight);
+ this._layout._setUncommittedSectionHeight(
+ this._sectionIndex,
+ height - this._initialHeight,
+ );
return this;
}