+ );
+ }
+}
diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js
new file mode 100644
index 0000000000..dd404ce280
--- /dev/null
+++ b/src/components/views/dialogs/UserSettingsDialog.js
@@ -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
+ );
+ }
+}
diff --git a/src/components/views/elements/AccessibleButton.js b/src/components/views/elements/AccessibleButton.js
index e30ceb85fa..1c39ba4f49 100644
--- a/src/components/views/elements/AccessibleButton.js
+++ b/src/components/views/elements/AccessibleButton.js
@@ -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 = {
diff --git a/src/components/views/settings/tabs/GeneralSettingsTab.js b/src/components/views/settings/tabs/GeneralSettingsTab.js
new file mode 100644
index 0000000000..c8816325c0
--- /dev/null
+++ b/src/components/views/settings/tabs/GeneralSettingsTab.js
@@ -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 = (
+
+ );
+
+ return (
+
+ {_t("Profile")}
+ {form}
+
+ );
+ }
+
+ render() {
+ return (
+
+
{_t("General")}
+ {this._renderProfileSection()}
+
+ );
+ }
+}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index caa51a1349..6bf857be07 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -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 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.",
- "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 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.",
+ "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 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.",
@@ -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",
diff --git a/src/settings/Settings.js b/src/settings/Settings.js
index 5a9bd9e455..4871ee92f9 100644
--- a/src/settings/Settings.js
+++ b/src/settings/Settings.js
@@ -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"),