diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss index b888aa287b..86149020b8 100644 --- a/res/css/structures/_TabbedView.scss +++ b/res/css/structures/_TabbedView.scss @@ -45,6 +45,11 @@ limitations under the License. color: $tab-label-active-fg-color; } +// TODO: Remove temporary hack alongside "visit old settings" tab +.mx_TabbedView_tabLabel_TEMP_HACK { + background-color: orange; +} + .mx_TabbedView_tabLabel_icon { width: 14px; height: 14px; @@ -89,4 +94,5 @@ limitations under the License. .mx_TabbedView_tabPanelContent { flex-grow: 1; + min-width: 560px; } \ No newline at end of file diff --git a/res/css/views/elements/_AccessibleButton.scss b/res/css/views/elements/_AccessibleButton.scss index d6702a232c..23445f5f6f 100644 --- a/res/css/views/elements/_AccessibleButton.scss +++ b/res/css/views/elements/_AccessibleButton.scss @@ -21,3 +21,24 @@ limitations under the License. .mx_AccessibleButton { cursor: pointer; } + +.mx_AccessibleButton_disabled { + cursor: default; +} + +.mx_AccessibleButton_hasKind { + padding: 10px 25px; + text-align: center; + border-radius: 4px; + display: inline-block; +} + +.mx_AccessibleButton_kind_primary { + color: $button-primary-fg-color; + background-color: $button-primary-bg-color; +} + +.mx_AccessibleButton_kind_primary.mx_AccessibleButton_disabled { + color: $button-primary-disabled-fg-color; + background-color: $button-primary-disabled-bg-color; +} diff --git a/res/css/views/settings/tabs/_GeneralSettingsTab.scss b/res/css/views/settings/tabs/_GeneralSettingsTab.scss index b04975fc2d..8bc3de8689 100644 --- a/res/css/views/settings/tabs/_GeneralSettingsTab.scss +++ b/res/css/views/settings/tabs/_GeneralSettingsTab.scss @@ -1,6 +1,25 @@ -.mx_GeneralSettingsTab_profile input { +.mx_GeneralSettingsTab_profile { + display: flex; +} + +.mx_GeneralSettingsTab_profileControls { + flex-grow: 1; +} + +.mx_GeneralSettingsTab_profileControls .mx_Field #profileDisplayName { + width: calc(100% - 20px); // subtract 10px padding on left and right +} + +.mx_GeneralSettingsTab_profileAvatar { + width: 88px; + height: 88px; + margin-left: 13px; +} + +.mx_GeneralSettingsTab_profileAvatar div { display: block; - font-size: 14px; - padding: 5px; + width: 88px; + height: 88px; border-radius: 4px; + background-color: #ccc; } \ No newline at end of file diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index 6d907d17be..c3f7b661ab 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -200,6 +200,12 @@ $tab-label-active-bg-color: #7ac9a1; $tab-label-icon-bg-color: #454545; $tab-label-active-icon-bg-color: #ffffff; +// Buttons +$button-primary-fg-color: #ffffff; +$button-primary-bg-color: #7ac9a1; +$button-primary-disabled-fg-color: #ffffff; +$button-primary-disabled-bg-color: #bce4d0; + // unused? $progressbar-color: #000; diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index ec36f15b89..7f08b6d1c2 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -196,6 +196,12 @@ $tab-label-active-bg-color: #7ac9a1; $tab-label-icon-bg-color: #454545; $tab-label-active-icon-bg-color: #ffffff; +// Buttons +$button-primary-fg-color: #ffffff; +$button-primary-bg-color: #7ac9a1; +$button-primary-disabled-fg-color: #ffffff; +$button-primary-disabled-bg-color: #bce4d0; + // unused? $progressbar-color: #000; diff --git a/src/components/structures/TabbedView.js b/src/components/structures/TabbedView.js index 48f8c7a4d8..ab1f5d76de 100644 --- a/src/components/structures/TabbedView.js +++ b/src/components/structures/TabbedView.js @@ -39,7 +39,7 @@ export class Tab { export class TabbedView extends React.Component { static propTypes = { // The tabs to show - tabs: PropTypes.arrayOf(Tab).isRequired, + tabs: PropTypes.arrayOf(PropTypes.instanceOf(Tab)).isRequired, }; constructor() { @@ -74,6 +74,7 @@ export class TabbedView extends React.Component { 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) { diff --git a/src/components/views/elements/AccessibleButton.js b/src/components/views/elements/AccessibleButton.js index e30ceb85fa..938426d5bc 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 index 433b12a674..513d33fabe 100644 --- a/src/components/views/settings/tabs/GeneralSettingsTab.js +++ b/src/components/views/settings/tabs/GeneralSettingsTab.js @@ -16,32 +16,82 @@ 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() { + const form = ( +
+
+
+

{this.state.userId}

+ +
+
+ {/*TODO: Ditch avatar placeholder and use the real thing*/} +
+
+
+ + {_t("Save")} + + + ); + + return ( +
+ {_t("Profile")} + {form} +
+ ); + } + render() { return (
{_t("General")}
-
- {_t("Profile")} - - -
-
- {_t("Profile")} - - -
-
- {_t("Profile")} - - -
-
- {_t("Profile")} - - -
+ {this._renderProfileSection()}
); } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 6a06811fb3..defc77e3dd 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -403,6 +403,7 @@ "Noisy": "Noisy", "General": "General", "Profile": "Profile", + "Display Name": "Display Name", "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",