Merge pull request #2476 from matrix-org/travis/modal-tab-settings
Basic structure for tabbed user settings
This commit is contained in:
commit
94b1d739fb
21 changed files with 725 additions and 79 deletions
|
@ -610,7 +610,17 @@ export default React.createClass({
|
|||
case 'view_indexed_room':
|
||||
this._viewIndexedRoom(payload.roomIndex);
|
||||
break;
|
||||
case 'view_user_settings':
|
||||
case 'view_user_settings': {
|
||||
if (SettingsStore.isFeatureEnabled("feature_tabbed_settings")) {
|
||||
const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog");
|
||||
Modal.createTrackedDialog('User settings', '', UserSettingsDialog, {});
|
||||
} else {
|
||||
this._setPage(PageTypes.UserSettings);
|
||||
this.notifyNewScreen('settings');
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'view_old_user_settings':
|
||||
this._setPage(PageTypes.UserSettings);
|
||||
this.notifyNewScreen('settings');
|
||||
break;
|
||||
|
|
118
src/components/structures/TabbedView.js
Normal file
118
src/components/structures/TabbedView.js
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
Copyright 2017 Travis Ralston
|
||||
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 * as React from "react";
|
||||
import {_t} from '../../languageHandler';
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
/**
|
||||
* Represents a tab for the TabbedView.
|
||||
*/
|
||||
export class Tab {
|
||||
/**
|
||||
* Creates a new tab.
|
||||
* @param {string} tabLabel The untranslated tab label.
|
||||
* @param {string} tabIconClass The class for the tab icon. This should be a simple mask.
|
||||
* @param {string} tabJsx The JSX for the tab container.
|
||||
*/
|
||||
constructor(tabLabel, tabIconClass, tabJsx) {
|
||||
this.label = tabLabel;
|
||||
this.icon = tabIconClass;
|
||||
this.body = tabJsx;
|
||||
}
|
||||
}
|
||||
|
||||
export class TabbedView extends React.Component {
|
||||
static propTypes = {
|
||||
// The tabs to show
|
||||
tabs: PropTypes.arrayOf(PropTypes.instanceOf(Tab)).isRequired,
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
activeTabIndex: 0,
|
||||
};
|
||||
}
|
||||
|
||||
_getActiveTabIndex() {
|
||||
if (!this.state || !this.state.activeTabIndex) return 0;
|
||||
return this.state.activeTabIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the given tab
|
||||
* @param {Tab} tab the tab to show
|
||||
* @private
|
||||
*/
|
||||
_setActiveTab(tab) {
|
||||
const idx = this.props.tabs.indexOf(tab);
|
||||
if (idx !== -1) {
|
||||
this.setState({activeTabIndex: idx});
|
||||
} else {
|
||||
console.error("Could not find tab " + tab.label + " in tabs");
|
||||
}
|
||||
}
|
||||
|
||||
_renderTabLabel(tab) {
|
||||
let classes = "mx_TabbedView_tabLabel ";
|
||||
|
||||
const idx = this.props.tabs.indexOf(tab);
|
||||
if (idx === this._getActiveTabIndex()) classes += "mx_TabbedView_tabLabel_active";
|
||||
if (tab.label === "Visit old settings") classes += "mx_TabbedView_tabLabel_TEMP_HACK";
|
||||
|
||||
let tabIcon = null;
|
||||
if (tab.icon) {
|
||||
tabIcon = <span className={`mx_TabbedView_maskedIcon ${tab.icon}`} />;
|
||||
}
|
||||
|
||||
const onClickHandler = () => this._setActiveTab(tab);
|
||||
|
||||
return (
|
||||
<span className={classes} key={"tab_label_" + tab.label}
|
||||
onClick={onClickHandler}>
|
||||
{tabIcon}
|
||||
<span className="mx_TabbedView_tabLabel_text">
|
||||
{_t(tab.label)}
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
_renderTabPanel(tab) {
|
||||
return (
|
||||
<div className="mx_TabbedView_tabPanel" key={"mx_tabpanel_" + tab.label}>
|
||||
{tab.body}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const labels = this.props.tabs.map(tab => this._renderTabLabel(tab));
|
||||
const panel = this._renderTabPanel(this.props.tabs[this._getActiveTabIndex()]);
|
||||
|
||||
return (
|
||||
<div className="mx_TabbedView">
|
||||
<div className="mx_TabbedView_tabLabels">
|
||||
{labels}
|
||||
</div>
|
||||
{panel}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
99
src/components/views/dialogs/UserSettingsDialog.js
Normal file
99
src/components/views/dialogs/UserSettingsDialog.js
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
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 {Tab, TabbedView} from "../../structures/TabbedView";
|
||||
import {_t, _td} from "../../../languageHandler";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import GeneralSettingsTab from "../settings/tabs/GeneralSettingsTab";
|
||||
import dis from '../../../dispatcher';
|
||||
|
||||
// TODO: Ditch this whole component
|
||||
export class TempTab extends React.Component {
|
||||
static propTypes = {
|
||||
onClose: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
componentDidMount(): void {
|
||||
dis.dispatch({action: "view_old_user_settings"});
|
||||
this.props.onClose();
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div>Hello World</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export default class UserSettingsDialog extends React.Component {
|
||||
static propTypes = {
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
_getTabs() {
|
||||
return [
|
||||
new Tab(
|
||||
_td("General"),
|
||||
"mx_UserSettingsDialog_settingsIcon",
|
||||
<GeneralSettingsTab />,
|
||||
),
|
||||
new Tab(
|
||||
_td("Notifications"),
|
||||
"mx_UserSettingsDialog_bellIcon",
|
||||
<div>Notifications Test</div>,
|
||||
),
|
||||
new Tab(
|
||||
_td("Preferences"),
|
||||
"mx_UserSettingsDialog_preferencesIcon",
|
||||
<div>Preferences Test</div>,
|
||||
),
|
||||
new Tab(
|
||||
_td("Voice & Video"),
|
||||
"mx_UserSettingsDialog_voiceIcon",
|
||||
<div>Voice Test</div>,
|
||||
),
|
||||
new Tab(
|
||||
_td("Security & Privacy"),
|
||||
"mx_UserSettingsDialog_securityIcon",
|
||||
<div>Security Test</div>,
|
||||
),
|
||||
new Tab(
|
||||
_td("Help & About"),
|
||||
"mx_UserSettingsDialog_helpIcon",
|
||||
<div>Help Test</div>,
|
||||
),
|
||||
new Tab(
|
||||
_td("Visit old settings"),
|
||||
"mx_UserSettingsDialog_helpIcon",
|
||||
<TempTab onClose={this.props.onFinished} />,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="mx_UserSettingsDialog">
|
||||
<div className="mx_UserSettingsDialog_header">
|
||||
{_t("Settings")}
|
||||
<span className="mx_UserSettingsDialog_close">
|
||||
<AccessibleButton className="mx_UserSettingsDialog_closeIcon" onClick={this.props.onFinished} />
|
||||
</span>
|
||||
</div>
|
||||
<TabbedView tabs={this._getTabs()} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -28,41 +28,56 @@ import { KeyCode } from '../../../Keyboard';
|
|||
* @returns {Object} rendered react
|
||||
*/
|
||||
export default function AccessibleButton(props) {
|
||||
const {element, onClick, children, ...restProps} = props;
|
||||
restProps.onClick = onClick;
|
||||
// We need to consume enter onKeyDown and space onKeyUp
|
||||
// otherwise we are risking also activating other keyboard focusable elements
|
||||
// that might receive focus as a result of the AccessibleButtonClick action
|
||||
// It's because we are using html buttons at a few places e.g. inside dialogs
|
||||
// And divs which we report as role button to assistive technologies.
|
||||
// Browsers handle space and enter keypresses differently and we are only adjusting to the
|
||||
// inconsistencies here
|
||||
restProps.onKeyDown = function(e) {
|
||||
if (e.keyCode === KeyCode.ENTER) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
return onClick(e);
|
||||
}
|
||||
if (e.keyCode === KeyCode.SPACE) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
restProps.onKeyUp = function(e) {
|
||||
if (e.keyCode === KeyCode.SPACE) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
return onClick(e);
|
||||
}
|
||||
if (e.keyCode === KeyCode.ENTER) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
const {element, onClick, children, kind, disabled, ...restProps} = props;
|
||||
|
||||
if (!disabled) {
|
||||
restProps.onClick = onClick;
|
||||
// We need to consume enter onKeyDown and space onKeyUp
|
||||
// otherwise we are risking also activating other keyboard focusable elements
|
||||
// that might receive focus as a result of the AccessibleButtonClick action
|
||||
// It's because we are using html buttons at a few places e.g. inside dialogs
|
||||
// And divs which we report as role button to assistive technologies.
|
||||
// Browsers handle space and enter keypresses differently and we are only adjusting to the
|
||||
// inconsistencies here
|
||||
restProps.onKeyDown = function(e) {
|
||||
if (e.keyCode === KeyCode.ENTER) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
return onClick(e);
|
||||
}
|
||||
if (e.keyCode === KeyCode.SPACE) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
restProps.onKeyUp = function(e) {
|
||||
if (e.keyCode === KeyCode.SPACE) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
return onClick(e);
|
||||
}
|
||||
if (e.keyCode === KeyCode.ENTER) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
restProps.tabIndex = restProps.tabIndex || "0";
|
||||
restProps.role = "button";
|
||||
restProps.className = (restProps.className ? restProps.className + " " : "") +
|
||||
"mx_AccessibleButton";
|
||||
|
||||
if (kind) {
|
||||
// We apply a hasKind class to maintain backwards compatibility with
|
||||
// buttons which might not know about kind and break
|
||||
restProps.className += " mx_AccessibleButton_hasKind mx_AccessibleButton_kind_" + kind;
|
||||
}
|
||||
|
||||
if (disabled) {
|
||||
restProps.className += " mx_AccessibleButton_disabled";
|
||||
}
|
||||
|
||||
return React.createElement(element, restProps, children);
|
||||
}
|
||||
|
||||
|
@ -76,6 +91,12 @@ AccessibleButton.propTypes = {
|
|||
children: PropTypes.node,
|
||||
element: PropTypes.string,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
|
||||
// The kind of button, similar to how Bootstrap works.
|
||||
// See available classes for AccessibleButton for options.
|
||||
kind: PropTypes.string,
|
||||
|
||||
disabled: PropTypes.bool,
|
||||
};
|
||||
|
||||
AccessibleButton.defaultProps = {
|
||||
|
|
95
src/components/views/settings/tabs/GeneralSettingsTab.js
Normal file
95
src/components/views/settings/tabs/GeneralSettingsTab.js
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
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 {_t} from "../../../../languageHandler";
|
||||
import MatrixClientPeg from "../../../../MatrixClientPeg";
|
||||
import Field from "../../elements/Field";
|
||||
import AccessibleButton from "../../elements/AccessibleButton";
|
||||
|
||||
export default class GeneralSettingsTab extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
const client = MatrixClientPeg.get();
|
||||
this.state = {
|
||||
userId: client.getUserId(),
|
||||
displayName: client.getUser(client.getUserId()).displayName,
|
||||
enableProfileSave: false,
|
||||
};
|
||||
}
|
||||
|
||||
_saveProfile = async (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
if (!this.state.enableProfileSave) return;
|
||||
this.setState({enableProfileSave: false});
|
||||
|
||||
// TODO: What do we do about errors?
|
||||
await MatrixClientPeg.get().setDisplayName(this.state.displayName);
|
||||
|
||||
// TODO: Support avatars
|
||||
|
||||
this.setState({enableProfileSave: true});
|
||||
};
|
||||
|
||||
_onDisplayNameChanged = (e) => {
|
||||
this.setState({
|
||||
displayName: e.target.value,
|
||||
enableProfileSave: true,
|
||||
});
|
||||
};
|
||||
|
||||
_renderProfileSection() {
|
||||
// TODO: Ditch avatar placeholder and use the real thing
|
||||
const form = (
|
||||
<form onSubmit={this._saveProfile} autoComplete={false} noValidate={true}>
|
||||
<div className="mx_GeneralSettingsTab_profile">
|
||||
<div className="mx_GeneralSettingsTab_profileControls">
|
||||
<p className="mx_GeneralSettingsTab_profileUsername">{this.state.userId}</p>
|
||||
<Field id="profileDisplayName" label={_t("Display Name")}
|
||||
type="text" value={this.state.displayName} autocomplete="off"
|
||||
onChange={this._onDisplayNameChanged} />
|
||||
</div>
|
||||
<div className="mx_GeneralSettingsTab_profileAvatar">
|
||||
<div />
|
||||
</div>
|
||||
</div>
|
||||
<AccessibleButton onClick={this._saveProfile} kind="primary"
|
||||
disabled={!this.state.enableProfileSave}>
|
||||
{_t("Save")}
|
||||
</AccessibleButton>
|
||||
</form>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="mx_SettingsTab_section">
|
||||
<span className="mx_SettingsTab_subheading">{_t("Profile")}</span>
|
||||
{form}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="mx_SettingsTab">
|
||||
<div className="mx_SettingsTab_heading">{_t("General")}</div>
|
||||
{this._renderProfileSection()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -262,6 +262,7 @@
|
|||
"Please contact your homeserver administrator.": "Please contact your homeserver administrator.",
|
||||
"Failed to join room": "Failed to join room",
|
||||
"Message Pinning": "Message Pinning",
|
||||
"Tabbed settings": "Tabbed settings",
|
||||
"Custom user status messages": "Custom user status messages",
|
||||
"Increase performance by only loading room members on first view": "Increase performance by only loading room members on first view",
|
||||
"Backup of encryption keys to server": "Backup of encryption keys to server",
|
||||
|
@ -401,6 +402,10 @@
|
|||
"Off": "Off",
|
||||
"On": "On",
|
||||
"Noisy": "Noisy",
|
||||
"Display Name": "Display Name",
|
||||
"Save": "Save",
|
||||
"Profile": "Profile",
|
||||
"General": "General",
|
||||
"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.",
|
||||
"Add a widget": "Add a widget",
|
||||
|
@ -533,7 +538,6 @@
|
|||
"World readable": "World readable",
|
||||
"Guests can join": "Guests can join",
|
||||
"Failed to set avatar.": "Failed to set avatar.",
|
||||
"Save": "Save",
|
||||
"(~%(count)s results)|other": "(~%(count)s results)",
|
||||
"(~%(count)s results)|one": "(~%(count)s result)",
|
||||
"Join Room": "Join Room",
|
||||
|
@ -710,46 +714,6 @@
|
|||
"Removed or unknown message type": "Removed or unknown message type",
|
||||
"Message removed by %(userId)s": "Message removed by %(userId)s",
|
||||
"Message removed": "Message removed",
|
||||
"Robot check is currently unavailable on desktop - please use a <a>web browser</a>": "Robot check is currently unavailable on desktop - please use a <a>web browser</a>",
|
||||
"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.",
|
||||
"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",
|
||||
"Please review and accept the policies of this homeserver:": "Please review and accept the policies of this homeserver:",
|
||||
"An email has been sent to %(emailAddress)s": "An email has been sent to %(emailAddress)s",
|
||||
"Please check your email to continue registration.": "Please check your email to continue registration.",
|
||||
"Token incorrect": "Token incorrect",
|
||||
"A text message has been sent to %(msisdn)s": "A text message has been sent to %(msisdn)s",
|
||||
"Please enter the code it contains:": "Please enter the code it contains:",
|
||||
"Code": "Code",
|
||||
"Start authentication": "Start authentication",
|
||||
"powered by Matrix": "powered by Matrix",
|
||||
"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 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",
|
||||
"Mobile phone number": "Mobile phone number",
|
||||
"Forgot your password?": "Forgot your password?",
|
||||
"Matrix ID": "Matrix ID",
|
||||
"%(serverName)s Matrix ID": "%(serverName)s Matrix ID",
|
||||
"Sign in with": "Sign in with",
|
||||
"Email address": "Email address",
|
||||
"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)",
|
||||
"You are registering with %(SelectedTeamName)s": "You are registering with %(SelectedTeamName)s",
|
||||
"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?",
|
||||
"Remove from community": "Remove from community",
|
||||
"Disinvite this user from community?": "Disinvite this user from community?",
|
||||
"Remove this user from community?": "Remove this user from community?",
|
||||
|
@ -886,6 +850,7 @@
|
|||
"And %(count)s more...|other": "And %(count)s more...",
|
||||
"ex. @bob:example.com": "ex. @bob:example.com",
|
||||
"Add User": "Add User",
|
||||
"Matrix ID": "Matrix ID",
|
||||
"Matrix Room ID": "Matrix Room ID",
|
||||
"email address": "email address",
|
||||
"That doesn't look like a valid email address": "That doesn't look like a valid email address",
|
||||
|
@ -1012,6 +977,7 @@
|
|||
"Please check your email and click on the link it contains. Once this is done, click continue.": "Please check your email and click on the link it contains. Once this is done, click continue.",
|
||||
"Unable to add email address": "Unable to add email address",
|
||||
"Unable to verify email address.": "Unable to verify email address.",
|
||||
"Email address": "Email address",
|
||||
"This will allow you to reset your password and receive notifications.": "This will allow you to reset your password and receive notifications.",
|
||||
"Skip": "Skip",
|
||||
"Only use lower case letters, numbers and '=_-./'": "Only use lower case letters, numbers and '=_-./'",
|
||||
|
@ -1043,6 +1009,11 @@
|
|||
"Room contains unknown devices": "Room contains unknown devices",
|
||||
"\"%(RoomName)s\" contains devices that you haven't seen before.": "\"%(RoomName)s\" contains devices that you haven't seen before.",
|
||||
"Unknown devices": "Unknown devices",
|
||||
"Preferences": "Preferences",
|
||||
"Voice & Video": "Voice & Video",
|
||||
"Security & Privacy": "Security & Privacy",
|
||||
"Help & About": "Help & About",
|
||||
"Visit old settings": "Visit old settings",
|
||||
"Unable to load backup status": "Unable to load backup status",
|
||||
"Unable to restore backup": "Unable to restore backup",
|
||||
"No backup found!": "No backup found!",
|
||||
|
@ -1094,6 +1065,44 @@
|
|||
"Set status": "Set status",
|
||||
"Set a new status...": "Set a new status...",
|
||||
"View Community": "View Community",
|
||||
"Robot check is currently unavailable on desktop - please use a <a>web browser</a>": "Robot check is currently unavailable on desktop - please use a <a>web browser</a>",
|
||||
"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.",
|
||||
"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",
|
||||
"Please review and accept the policies of this homeserver:": "Please review and accept the policies of this homeserver:",
|
||||
"An email has been sent to %(emailAddress)s": "An email has been sent to %(emailAddress)s",
|
||||
"Please check your email to continue registration.": "Please check your email to continue registration.",
|
||||
"Token incorrect": "Token incorrect",
|
||||
"A text message has been sent to %(msisdn)s": "A text message has been sent to %(msisdn)s",
|
||||
"Please enter the code it contains:": "Please enter the code it contains:",
|
||||
"Code": "Code",
|
||||
"Start authentication": "Start authentication",
|
||||
"powered by Matrix": "powered by Matrix",
|
||||
"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 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",
|
||||
"Mobile phone number": "Mobile phone number",
|
||||
"Forgot your password?": "Forgot your password?",
|
||||
"%(serverName)s Matrix ID": "%(serverName)s Matrix ID",
|
||||
"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)",
|
||||
"You are registering with %(SelectedTeamName)s": "You are registering with %(SelectedTeamName)s",
|
||||
"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?",
|
||||
"Sorry, your browser is <b>not</b> able to run Riot.": "Sorry, your browser is <b>not</b> 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 <chromeLink>Chrome</chromeLink> or <firefoxLink>Firefox</firefoxLink> for the best experience.": "Please install <chromeLink>Chrome</chromeLink> or <firefoxLink>Firefox</firefoxLink> for the best experience.",
|
||||
|
@ -1284,7 +1293,6 @@
|
|||
"VoIP": "VoIP",
|
||||
"Email": "Email",
|
||||
"Add email address": "Add email address",
|
||||
"Profile": "Profile",
|
||||
"Display name": "Display name",
|
||||
"Account": "Account",
|
||||
"To return to your account in future you need to set a password": "To return to your account in future you need to set a password",
|
||||
|
|
|
@ -84,6 +84,12 @@ export const SETTINGS = {
|
|||
supportedLevels: LEVELS_FEATURE,
|
||||
default: false,
|
||||
},
|
||||
"feature_tabbed_settings": {
|
||||
isFeature: true,
|
||||
displayName: _td("Tabbed settings"),
|
||||
supportedLevels: LEVELS_FEATURE,
|
||||
default: false,
|
||||
},
|
||||
"feature_custom_status": {
|
||||
isFeature: true,
|
||||
displayName: _td("Custom user status messages"),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue