From 0e42c0892e046ce202871604eee3b546e247d026 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 18 Jan 2019 13:09:17 -0700 Subject: [PATCH 01/15] Early modalization of UserSettings Basically just shove it into a modal and call it good. --- src/components/structures/MatrixChat.js | 8 ++- .../views/dialogs/UserSettingsDialog.js | 49 +++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 src/components/views/dialogs/UserSettingsDialog.js diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 7acceb4bb7..25ba980bcf 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -611,8 +611,12 @@ export default React.createClass({ this._viewIndexedRoom(payload.roomIndex); break; case 'view_user_settings': - this._setPage(PageTypes.UserSettings); - this.notifyNewScreen('settings'); + const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog"); + Modal.createTrackedDialog('User settings', '', UserSettingsDialog, { + title: _t("Settings"), + }); + //this._setPage(PageTypes.UserSettings); + //this.notifyNewScreen('settings'); break; case 'close_settings': this.setState({ diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js new file mode 100644 index 0000000000..d12895010c --- /dev/null +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -0,0 +1,49 @@ +/* +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 sdk from '../../../index'; +import {_t} from '../../../languageHandler'; +import SdkConfig from "../../../SdkConfig"; + +export default React.createClass({ + propTypes: { + onFinished: PropTypes.func.isRequired, + }, + + render: function () { + const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); + const UserSettings = sdk.getComponent('structures.UserSettings'); + + return ( + +
+ +
+
+ ); + }, +}); From 5adfc09237cb0eead02a8b2a9cf8fb2f2332a5fc Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 18 Jan 2019 13:43:17 -0700 Subject: [PATCH 02/15] Bring in TabbedView nearly verbatim from prior work Sourced from https://github.com/matrix-org/matrix-react-sdk/pull/1644 and related PRs. --- res/css/_components.scss | 1 + res/css/structures/_TabbedView.scss | 76 ++++++++ res/themes/dharma/css/_dharma.scss | 8 + res/themes/light/css/_base.scss | 9 + src/components/structures/MatrixChat.js | 4 +- src/components/structures/TabbedView.js | 165 ++++++++++++++++++ .../views/dialogs/UserSettingsDialog.js | 45 +++-- src/i18n/strings/en_EN.json | 4 +- 8 files changed, 284 insertions(+), 28 deletions(-) create mode 100644 res/css/structures/_TabbedView.scss create mode 100644 src/components/structures/TabbedView.js diff --git a/res/css/_components.scss b/res/css/_components.scss index a1b575a0a1..1b35332711 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -18,6 +18,7 @@ @import "./structures/_RoomSubList.scss"; @import "./structures/_RoomView.scss"; @import "./structures/_SearchBox.scss"; +@import "./structures/_TabbedView.scss"; @import "./structures/_TagPanel.scss"; @import "./structures/_TopLeftMenuButton.scss"; @import "./structures/_UploadBar.scss"; diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss new file mode 100644 index 0000000000..7d42823d17 --- /dev/null +++ b/res/css/structures/_TabbedView.scss @@ -0,0 +1,76 @@ +/* +Copyright 2017 Travis Ralston + +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. +*/ + +.mx_TabbedView { + margin: 0; + padding: 0; + display: flex; + width: 100%; + height: 100%; + background-color: $tab-panel-bg-color; +} + +.mx_TabbedView_tabLabels { + width: 300px; + height: 100%; + background-color: $tab-list-bg-color; + color: $tab-list-fg-color; + border-right: 1px solid $tab-border-color; + border-left: 1px solid $tab-border-color; +} + +.mx_TabbedView_tabPanels { + width: calc(100% - 320px); + display: inline-block; + height: 100%; + padding-left: 20px; + scroll-snap-type: block; +} + +.mx_TabbedView_tabLabel { + text-align: center; + vertical-align: middle; + text-transform: uppercase; + cursor: pointer; + display: block; + padding: 20px; + width: calc(100% - 40px); + border-bottom: 1px solid $tab-border-color; +} + +.mx_TabbedView_exit { + padding-top: 10px; + padding-bottom: 10px; +} + +.mx_TabbedView_tabLabel:hover { + font-weight: 700; +} + +.mx_TabbedView_tabLabel_active { + font-weight: 700; + background-color: $tab-list-active-bg-color; + color: $tab-list-active-fg-color; +} + +.mx_TabbedView_tabPanel { + height: 100vh; // 100% of viewport height + scroll-snap-align: start; +} + +.mx_TabbedView_tabPanelContent { + width: 600px; +} \ No newline at end of file diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index c9f62fbe6b..8f369a3b33 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -186,6 +186,14 @@ $lightbox-bg-color: #454545; $lightbox-fg-color: #ffffff; $lightbox-border-color: #ffffff; +// Tabbed views +$tab-list-bg-color: $secondary-accent-color; +$tab-list-fg-color: $accent-color; +$tab-list-active-bg-color: $tertiary-accent-color; +$tab-list-active-fg-color: $accent-color; +$tab-border-color: $tertiary-accent-color; +$tab-panel-bg-color: #fff; + // unused? $progressbar-color: #000; diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index dbab909f13..996fe965cf 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -66,6 +66,7 @@ $primary-hairline-color: #e5e5e5; // used for the border of input text fields $input-border-color: #f0f0f0; +$input-border-dark-color: #b8b8b8; $input-darker-bg-color: #c1c9d6; $input-darker-fg-color: #9fa9ba; @@ -181,6 +182,14 @@ $imagebody-giflabel: rgba(0, 0, 0, 0.7); $imagebody-giflabel-border: rgba(0, 0, 0, 0.2); $imagebody-giflabel-color: rgba(255, 255, 255, 1); +// Tabbed views +$tab-list-bg-color: $secondary-accent-color; +$tab-list-fg-color: $accent-color; +$tab-list-active-bg-color: $tertiary-accent-color; +$tab-list-active-fg-color: $accent-color; +$tab-border-color: $tertiary-accent-color; +$tab-panel-bg-color: #fff; + // unused? $progressbar-color: #000; diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 25ba980bcf..9733576bd0 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -612,9 +612,7 @@ export default React.createClass({ break; case 'view_user_settings': const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog"); - Modal.createTrackedDialog('User settings', '', UserSettingsDialog, { - title: _t("Settings"), - }); + Modal.createTrackedDialog('User settings', '', UserSettingsDialog, {}); //this._setPage(PageTypes.UserSettings); //this.notifyNewScreen('settings'); break; diff --git a/src/components/structures/TabbedView.js b/src/components/structures/TabbedView.js new file mode 100644 index 0000000000..44ecee7a95 --- /dev/null +++ b/src/components/structures/TabbedView.js @@ -0,0 +1,165 @@ +/* +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, _td} from '../../languageHandler'; +import GeminiScrollbar from 'react-gemini-scrollbar'; +import PropTypes from "prop-types"; +//import scrollSnapPolyfill from 'css-scroll-snap-polyfill'; + +const DEFAULT_EXIT_STRING = _td("Return to app"); + +/** + * Represents a tab for the TabbedView + */ +export class Tab { + /** + * Creates a new tab + * @param {string} tabLabel The untranslated tab label + * @param {string} tabJsx The JSX for the tab container. + */ + constructor(tabLabel, tabJsx) { + this.label = tabLabel; + this.body = tabJsx; + } +} + +export class TabbedView extends React.Component { + constructor() { + super(); + + // This is used to track when the user has scrolled all the way up or down so we + // don't immediately start flipping between tabs. + this._reachedEndAt = 0; + } + + getInitialState() { + return { + activeTabIndex: 0, + }; + } + + _getActiveTabIndex() { + return this.state ? this.state.activeTabIndex : 0; + } + + /** + * 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}); + this._reachedEndAt = 0; // reset scroll timer + } + else console.error("Could not find tab " + tab.label + " in tabs"); + } + + _nextTab() { + let targetIndex = this._getActiveTabIndex() + 1; + if (targetIndex < this.props.tabs.length) { + this.setState({activeTabIndex: targetIndex}); + this._reachedEndAt = 0; // reset scroll timer + } + } + + _previousTab() { + let targetIndex = this._getActiveTabIndex() - 1; + if (targetIndex >= 0) { + this.setState({activeTabIndex: targetIndex}); + this._reachedEndAt = 0; // reset scroll timer + } + } + + _getTabLabel(tab) { + let classes = "mx_TabbedView_tabLabel "; + + const idx = this.props.tabs.indexOf(tab); + if (idx === this._getActiveTabIndex()) classes += "mx_TabbedView_tabLabel_active"; + + return ( + this._setActiveTab(tab)}> + {_t(tab.label)} + + ); + } + + _getTabPanel(tab) { + return ( +
+ {tab.body} +
+ ); + } + + componentDidUpdate() { + window.requestAnimationFrame(() => { + console.log("SCROLL SNAP POLYFILL: UPDATE"); + //scrollSnapPolyfill(); + }); + } + + componentDidMount() { + window.requestAnimationFrame(() => { + console.log("SCROLL SNAP POLYFILL: MOUNT"); + //scrollSnapPolyfill(); + }); + } + + render() { + const labels = []; + const tabs = []; + + for (const tab of this.props.tabs) { + labels.push(this._getTabLabel(tab)); + tabs.push(this._getTabPanel(tab)); + } + + const returnToApp = ( + + {_t(this.props.exitLabel || DEFAULT_EXIT_STRING)} + + ); + + return ( +
+
+ {returnToApp} + {labels} +
+
+ {tabs} +
+
+ ); + } +} + +TabbedView.PropTypes = { + // Called when the user clicks the "Exit" or "Return to app" button + onExit: PropTypes.func.isRequired, + + // The untranslated label for the "Return to app" button. + // Default: "Return to app" + exitLabel: PropTypes.string, + + // The tabs to show + tabs: PropTypes.arrayOf(Tab).isRequired, +}; \ No newline at end of file diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index d12895010c..f04b92b6cc 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -17,33 +17,30 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; import sdk from '../../../index'; -import {_t} from '../../../languageHandler'; -import SdkConfig from "../../../SdkConfig"; +import {Tab, TabbedView} from "../../structures/TabbedView"; +import {_td} from "../../../languageHandler"; -export default React.createClass({ - propTypes: { +export default class UserSettingsDialog extends React.Component { + static propTypes = { onFinished: PropTypes.func.isRequired, - }, + }; - render: function () { - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const UserSettings = sdk.getComponent('structures.UserSettings'); + _getTabs() { + return [ + new Tab(_td("General"),
General Test
), + new Tab(_td("Account"),
Account Test
), + ]; + } + render() { return ( - -
- -
-
+ + // ); - }, -}); + } +} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index cfd5168152..cc18731ee0 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1043,6 +1043,8 @@ "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", + "General": "General", + "Account": "Account", "Unable to load backup status": "Unable to load backup status", "Unable to restore backup": "Unable to restore backup", "No backup found!": "No backup found!", @@ -1222,6 +1224,7 @@ "Click to unmute audio": "Click to unmute audio", "Click to mute audio": "Click to mute audio", "Filter room names": "Filter room names", + "Return to app": "Return to app", "Clear filter": "Clear filter", "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.", "Tried to load a specific point in this room's timeline, but was unable to find it.": "Tried to load a specific point in this room's timeline, but was unable to find it.", @@ -1286,7 +1289,6 @@ "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", "Logged in as:": "Logged in as:", "Access Token:": "Access Token:", From 15709040e7b5c821ea04fdca76b4a4df05771a28 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 18 Jan 2019 15:07:11 -0700 Subject: [PATCH 03/15] Make tabs be their own panels --- res/css/structures/_TabbedView.scss | 42 +++---- res/themes/dharma/css/_dharma.scss | 10 +- res/themes/light/css/_base.scss | 10 +- src/components/structures/TabbedView.js | 103 ++++-------------- .../views/dialogs/UserSettingsDialog.js | 7 +- 5 files changed, 46 insertions(+), 126 deletions(-) diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss index 7d42823d17..81a3af06a9 100644 --- a/res/css/structures/_TabbedView.scss +++ b/res/css/structures/_TabbedView.scss @@ -1,5 +1,6 @@ /* 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. @@ -20,40 +21,22 @@ limitations under the License. display: flex; width: 100%; height: 100%; - background-color: $tab-panel-bg-color; } .mx_TabbedView_tabLabels { - width: 300px; + width: 136px; height: 100%; - background-color: $tab-list-bg-color; - color: $tab-list-fg-color; - border-right: 1px solid $tab-border-color; - border-left: 1px solid $tab-border-color; -} - -.mx_TabbedView_tabPanels { - width: calc(100% - 320px); - display: inline-block; - height: 100%; - padding-left: 20px; - scroll-snap-type: block; + color: $tab-label-fg-color; } .mx_TabbedView_tabLabel { - text-align: center; vertical-align: middle; - text-transform: uppercase; cursor: pointer; display: block; - padding: 20px; - width: calc(100% - 40px); - border-bottom: 1px solid $tab-border-color; -} - -.mx_TabbedView_exit { - padding-top: 10px; - padding-bottom: 10px; + border-radius: 3px; + font-size: 12px; + font-weight: 600; + height: 20px; } .mx_TabbedView_tabLabel:hover { @@ -61,16 +44,17 @@ limitations under the License. } .mx_TabbedView_tabLabel_active { - font-weight: 700; - background-color: $tab-list-active-bg-color; - color: $tab-list-active-fg-color; + background-color: $tab-label-active-bg-color; + color: $tab-label-active-fg-color; } .mx_TabbedView_tabPanel { + width: calc(100% - 320px); + display: inline-block; height: 100vh; // 100% of viewport height - scroll-snap-align: start; + margin-left: 20px; } .mx_TabbedView_tabPanelContent { - width: 600px; + flex-grow: 1; } \ No newline at end of file diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index 8f369a3b33..e95ad82daf 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -187,12 +187,10 @@ $lightbox-fg-color: #ffffff; $lightbox-border-color: #ffffff; // Tabbed views -$tab-list-bg-color: $secondary-accent-color; -$tab-list-fg-color: $accent-color; -$tab-list-active-bg-color: $tertiary-accent-color; -$tab-list-active-fg-color: $accent-color; -$tab-border-color: $tertiary-accent-color; -$tab-panel-bg-color: #fff; +$tab-label-fg-color: #45474a; +$tab-label-active-fg-color: #ffffff; +$tab-label-bg-color: transparent; +$tab-label-active-bg-color: #7ac9a1; // unused? $progressbar-color: #000; diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index 996fe965cf..852442c4d1 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -183,12 +183,10 @@ $imagebody-giflabel-border: rgba(0, 0, 0, 0.2); $imagebody-giflabel-color: rgba(255, 255, 255, 1); // Tabbed views -$tab-list-bg-color: $secondary-accent-color; -$tab-list-fg-color: $accent-color; -$tab-list-active-bg-color: $tertiary-accent-color; -$tab-list-active-fg-color: $accent-color; -$tab-border-color: $tertiary-accent-color; -$tab-panel-bg-color: #fff; +$tab-label-fg-color: #45474a; +$tab-label-active-fg-color: #ffffff; +$tab-label-bg-color: transparent; +$tab-label-active-bg-color: #7ac9a1; // unused? $progressbar-color: #000; diff --git a/src/components/structures/TabbedView.js b/src/components/structures/TabbedView.js index 44ecee7a95..047d48e808 100644 --- a/src/components/structures/TabbedView.js +++ b/src/components/structures/TabbedView.js @@ -16,45 +16,42 @@ limitations under the License. */ import * as React from "react"; -import {_t, _td} from '../../languageHandler'; -import GeminiScrollbar from 'react-gemini-scrollbar'; +import {_t} from '../../languageHandler'; import PropTypes from "prop-types"; -//import scrollSnapPolyfill from 'css-scroll-snap-polyfill'; - -const DEFAULT_EXIT_STRING = _td("Return to app"); /** - * Represents a tab for the TabbedView + * Represents a tab for the TabbedView. */ export class Tab { /** - * Creates a new tab - * @param {string} tabLabel The untranslated tab label + * Creates a new tab. + * @param {string} tabLabel The untranslated tab label. + * @param {string} tabIconRef The relative path to the tab's icon. * @param {string} tabJsx The JSX for the tab container. */ - constructor(tabLabel, tabJsx) { + constructor(tabLabel, tabIconRef, tabJsx) { this.label = tabLabel; this.body = tabJsx; } } export class TabbedView extends React.Component { + static propTypes = { + // The tabs to show + tabs: PropTypes.arrayOf(Tab).isRequired, + }; + constructor() { super(); - // This is used to track when the user has scrolled all the way up or down so we - // don't immediately start flipping between tabs. - this._reachedEndAt = 0; - } - - getInitialState() { - return { + this.state = { activeTabIndex: 0, }; } _getActiveTabIndex() { - return this.state ? this.state.activeTabIndex : 0; + if (!this.state || !this.state.activeTabIndex) return 0; + return this.state.activeTabIndex; } /** @@ -66,28 +63,12 @@ export class TabbedView extends React.Component { const idx = this.props.tabs.indexOf(tab); if (idx !== -1) { this.setState({activeTabIndex: idx}); - this._reachedEndAt = 0; // reset scroll timer - } - else console.error("Could not find tab " + tab.label + " in tabs"); - } - - _nextTab() { - let targetIndex = this._getActiveTabIndex() + 1; - if (targetIndex < this.props.tabs.length) { - this.setState({activeTabIndex: targetIndex}); - this._reachedEndAt = 0; // reset scroll timer + } else { + console.error("Could not find tab " + tab.label + " in tabs"); } } - _previousTab() { - let targetIndex = this._getActiveTabIndex() - 1; - if (targetIndex >= 0) { - this.setState({activeTabIndex: targetIndex}); - this._reachedEndAt = 0; // reset scroll timer - } - } - - _getTabLabel(tab) { + _renderTabLabel(tab) { let classes = "mx_TabbedView_tabLabel "; const idx = this.props.tabs.indexOf(tab); @@ -101,7 +82,7 @@ export class TabbedView extends React.Component { ); } - _getTabPanel(tab) { + _renderTabPanel(tab) { return (
{tab.body} @@ -109,57 +90,17 @@ export class TabbedView extends React.Component { ); } - componentDidUpdate() { - window.requestAnimationFrame(() => { - console.log("SCROLL SNAP POLYFILL: UPDATE"); - //scrollSnapPolyfill(); - }); - } - - componentDidMount() { - window.requestAnimationFrame(() => { - console.log("SCROLL SNAP POLYFILL: MOUNT"); - //scrollSnapPolyfill(); - }); - } - render() { - const labels = []; - const tabs = []; - - for (const tab of this.props.tabs) { - labels.push(this._getTabLabel(tab)); - tabs.push(this._getTabPanel(tab)); - } - - const returnToApp = ( - - {_t(this.props.exitLabel || DEFAULT_EXIT_STRING)} - - ); + const labels = this.props.tabs.map(tab => this._renderTabLabel(tab)); + const panel = this._renderTabPanel(this.props.tabs[this._getActiveTabIndex()]); return (
- {returnToApp} {labels}
-
- {tabs} -
+ {panel}
); } -} - -TabbedView.PropTypes = { - // Called when the user clicks the "Exit" or "Return to app" button - onExit: PropTypes.func.isRequired, - - // The untranslated label for the "Return to app" button. - // Default: "Return to app" - exitLabel: PropTypes.string, - - // The tabs to show - tabs: PropTypes.arrayOf(Tab).isRequired, -}; \ No newline at end of file +} \ No newline at end of file diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index f04b92b6cc..e54bc9c857 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -16,7 +16,6 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; -import sdk from '../../../index'; import {Tab, TabbedView} from "../../structures/TabbedView"; import {_td} from "../../../languageHandler"; @@ -27,14 +26,14 @@ export default class UserSettingsDialog extends React.Component { _getTabs() { return [ - new Tab(_td("General"),
General Test
), - new Tab(_td("Account"),
Account Test
), + new Tab(_td("General"), "",
General Test
), + new Tab(_td("Account"), "",
Account Test
), ]; } render() { return ( - + // Date: Fri, 18 Jan 2019 19:40:21 -0700 Subject: [PATCH 04/15] Make the tabs look like the design --- res/css/_components.scss | 1 + res/css/structures/_TabbedView.scss | 39 +++++++++++++++++-- .../views/dialogs/_UserSettingsDialog.scss | 26 +++++++++++++ res/themes/dharma/css/_dharma.scss | 2 + res/themes/light/css/_base.scss | 2 + src/components/structures/TabbedView.js | 15 +++++-- .../views/dialogs/UserSettingsDialog.js | 20 ++++++++-- src/i18n/strings/en_EN.json | 7 +++- 8 files changed, 99 insertions(+), 13 deletions(-) create mode 100644 res/css/views/dialogs/_UserSettingsDialog.scss diff --git a/res/css/_components.scss b/res/css/_components.scss index 1b35332711..e60e74383f 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -52,6 +52,7 @@ @import "./views/dialogs/_SetPasswordDialog.scss"; @import "./views/dialogs/_ShareDialog.scss"; @import "./views/dialogs/_UnknownDeviceDialog.scss"; +@import "./views/dialogs/_UserSettingsDialog.scss"; @import "./views/dialogs/keybackup/_CreateKeyBackupDialog.scss"; @import "./views/dialogs/keybackup/_KeyBackupFailedDialog.scss"; @import "./views/dialogs/keybackup/_RestoreKeyBackupDialog.scss"; diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss index 81a3af06a9..32174a0ef5 100644 --- a/res/css/structures/_TabbedView.scss +++ b/res/css/structures/_TabbedView.scss @@ -39,15 +39,46 @@ limitations under the License. height: 20px; } -.mx_TabbedView_tabLabel:hover { - font-weight: 700; -} - .mx_TabbedView_tabLabel_active { background-color: $tab-label-active-bg-color; color: $tab-label-active-fg-color; } +.mx_TabbedView_tabLabel_icon { + width: 12px; + height: 12px; + margin-left: 6px; + margin-right: 9px; + position: relative; +} + +.mx_TabbedView_tabLabel_icon > .mx_TabbedView_maskedIcon { + width: 12px; + height: 12px; + display: inline-block; +} + +.mx_TabbedView_tabLabel_icon > .mx_TabbedView_maskedIcon:before { + background-color: $tab-label-icon-bg-color; + mask-repeat: no-repeat; + mask-size: 12px; + mask-position: center; + content: ''; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; +} + +.mx_TabbedView_tabLabel_active .mx_TabbedView_tabLabel_icon > .mx_TabbedView_maskedIcon:before { + background-color: $tab-label-active-icon-bg-color; +} + +.mx_TabbedView_tabLabel_text { + vertical-align: middle; +} + .mx_TabbedView_tabPanel { width: calc(100% - 320px); display: inline-block; diff --git a/res/css/views/dialogs/_UserSettingsDialog.scss b/res/css/views/dialogs/_UserSettingsDialog.scss new file mode 100644 index 0000000000..7d8c80e5bd --- /dev/null +++ b/res/css/views/dialogs/_UserSettingsDialog.scss @@ -0,0 +1,26 @@ +.mx_UserSettingsDialog_settingsIcon:before { + mask: url('$(res)/img/feather-icons/settings.svg'); +} + +.mx_UserSettingsDialog_voiceIcon:before { + mask: url('$(res)/img/feather-icons/phone.svg'); +} + +.mx_UserSettingsDialog_bellIcon:before { + mask: url('$(res)/img/feather-icons/notifications.svg'); +} + +.mx_UserSettingsDialog_preferencesIcon:before { + // TODO: Use real icon + mask: url('$(res)/img/feather-icons/paperclip.svg'); +} + +.mx_UserSettingsDialog_securityIcon:before { + // TODO: Use real icon + mask: url('$(res)/img/feather-icons/life-buoy.svg'); +} + +.mx_UserSettingsDialog_helpIcon:before { + // TODO: Use real icon + mask: url('$(res)/img/feather-icons/share.svg'); +} diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index e95ad82daf..5f405e49e9 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -191,6 +191,8 @@ $tab-label-fg-color: #45474a; $tab-label-active-fg-color: #ffffff; $tab-label-bg-color: transparent; $tab-label-active-bg-color: #7ac9a1; +$tab-label-icon-bg-color: #454545; +$tab-label-active-icon-bg-color: #ffffff; // unused? $progressbar-color: #000; diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index 852442c4d1..69f1b61f5f 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -187,6 +187,8 @@ $tab-label-fg-color: #45474a; $tab-label-active-fg-color: #ffffff; $tab-label-bg-color: transparent; $tab-label-active-bg-color: #7ac9a1; +$tab-label-icon-bg-color: #454545; +$tab-label-active-icon-bg-color: #ffffff; // unused? $progressbar-color: #000; diff --git a/src/components/structures/TabbedView.js b/src/components/structures/TabbedView.js index 047d48e808..48f8c7a4d8 100644 --- a/src/components/structures/TabbedView.js +++ b/src/components/structures/TabbedView.js @@ -26,11 +26,12 @@ export class Tab { /** * Creates a new tab. * @param {string} tabLabel The untranslated tab label. - * @param {string} tabIconRef The relative path to the tab's icon. + * @param {string} tabIconJsx The JSX for the tab icon. This should be a plain img element or null. * @param {string} tabJsx The JSX for the tab container. */ - constructor(tabLabel, tabIconRef, tabJsx) { + constructor(tabLabel, tabIconJsx, tabJsx) { this.label = tabLabel; + this.icon = tabIconJsx; this.body = tabJsx; } } @@ -74,10 +75,18 @@ export class TabbedView extends React.Component { const idx = this.props.tabs.indexOf(tab); if (idx === this._getActiveTabIndex()) classes += "mx_TabbedView_tabLabel_active"; + let tabIcon = null; + if (tab.icon) { + tabIcon = {tab.icon}; + } + return ( this._setActiveTab(tab)}> - {_t(tab.label)} + {tabIcon} + + {_t(tab.label)} + ); } diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index e54bc9c857..189d5871e3 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -17,7 +17,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; import {Tab, TabbedView} from "../../structures/TabbedView"; -import {_td} from "../../../languageHandler"; +import {_t, _td} from "../../../languageHandler"; export default class UserSettingsDialog extends React.Component { static propTypes = { @@ -26,14 +26,26 @@ export default class UserSettingsDialog extends React.Component { _getTabs() { return [ - new Tab(_td("General"), "",
General Test
), - new Tab(_td("Account"), "",
Account Test
), + new Tab(_td("General"), ,
General Test
), + new Tab(_td("Notifications"), ,
Notifications Test
), + new Tab(_td("Preferences"), ,
Preferences Test
), + new Tab(_td("Voice & Video"), ,
Voice Test
), + new Tab(_td("Security & Privacy"), ,
Security Test
), + new Tab(_td("Help & About"), ,
Help Test
), ]; } render() { return ( - +
+

+ {_t("Settings")} +

+ + X + + +
// Date: Fri, 18 Jan 2019 20:09:23 -0700 Subject: [PATCH 05/15] Make the dialog look more like a new dialog --- res/css/_common.scss | 4 +- res/css/structures/_TabbedView.scss | 2 +- .../views/dialogs/_UserSettingsDialog.scss | 43 ++++++++++++++++++- res/themes/dharma/css/_dharma.scss | 5 +++ res/themes/light/css/_base.scss | 5 +++ .../views/dialogs/UserSettingsDialog.js | 11 ++--- 6 files changed, 61 insertions(+), 9 deletions(-) diff --git a/res/css/_common.scss b/res/css/_common.scss index bec4c02c18..306834bcde 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -160,7 +160,7 @@ textarea { padding: 0 58px 36px; width: 60%; max-width: 704px; - box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.2); + box-shadow: 2px 15px 30px 0 $dialog-shadow-color; max-height: 80%; overflow-y: auto; } @@ -171,7 +171,7 @@ textarea { left: 0; width: 100%; height: 100%; - background-color: $dialog-background-bg-color; + background-color: $dialog-backdrop-color; opacity: 0.8; } diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss index 32174a0ef5..957e989f53 100644 --- a/res/css/structures/_TabbedView.scss +++ b/res/css/structures/_TabbedView.scss @@ -82,8 +82,8 @@ limitations under the License. .mx_TabbedView_tabPanel { width: calc(100% - 320px); display: inline-block; - height: 100vh; // 100% of viewport height margin-left: 20px; + flex-grow: 1; } .mx_TabbedView_tabPanelContent { diff --git a/res/css/views/dialogs/_UserSettingsDialog.scss b/res/css/views/dialogs/_UserSettingsDialog.scss index 7d8c80e5bd..e98001fea4 100644 --- a/res/css/views/dialogs/_UserSettingsDialog.scss +++ b/res/css/views/dialogs/_UserSettingsDialog.scss @@ -1,3 +1,44 @@ +.mx_UserSettingsDialog_header { + font-size: 24px; + display: block; + text-align: center; + color: $dialog-title-fg-color; + margin-top: 23px; + margin-bottom: 32px; + padding: 0; +} + +.mx_UserSettingsDialog_close { + position: absolute; + top: 23px; + right: 25px; +} + +.mx_UserSettingsDialog_closeIcon { + width: 14px; + height: 14px; + display: inline-block; +} + +.mx_UserSettingsDialog_closeIcon:before { + // TODO: Use real icon + mask: url('$(res)/img/feather-icons/video.svg'); + background-color: $dialog-close-fg-color; + mask-repeat: no-repeat; + mask-size: 14px; + mask-position: center; + content: ''; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; +} + + +// ICONS +// ========================================================== + .mx_UserSettingsDialog_settingsIcon:before { mask: url('$(res)/img/feather-icons/settings.svg'); } @@ -23,4 +64,4 @@ .mx_UserSettingsDialog_helpIcon:before { // TODO: Use real icon mask: url('$(res)/img/feather-icons/share.svg'); -} +} \ No newline at end of file diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index 5f405e49e9..cf3738a1bb 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -96,6 +96,11 @@ $avatar-bg-color: #ffffff; $h3-color: #3d3b39; +$dialog-title-fg-color: #454545; +$dialog-backdrop-color: rgba(46, 48, 51, 0.38); +$dialog-shadow-color: rgba(0, 0, 0, 0.48); +$dialog-close-fg-color: #9fa9ba; + $dialog-background-bg-color: #e9e9e9; $lightbox-background-bg-color: #000; diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index 69f1b61f5f..45b64243c6 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -93,6 +93,11 @@ $avatar-bg-color: #ffffff; $h3-color: #3d3b39; +$dialog-title-fg-color: #454545; +$dialog-backdrop-color: rgba(46, 48, 51, 0.38); +$dialog-shadow-color: rgba(0, 0, 0, 0.48); +$dialog-close-fg-color: #9fa9ba; + $dialog-background-bg-color: #e9e9e9; $lightbox-background-bg-color: #000; diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index 189d5871e3..1896bd38e0 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -18,6 +18,7 @@ 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"; export default class UserSettingsDialog extends React.Component { static propTypes = { @@ -38,12 +39,12 @@ export default class UserSettingsDialog extends React.Component { render() { return (
-

+
{_t("Settings")} -

- - X - + + + +
// Date: Fri, 18 Jan 2019 20:22:36 -0700 Subject: [PATCH 06/15] Very early work on the "General" tab --- res/css/_components.scss | 2 + res/css/structures/_TabbedView.scss | 2 +- .../views/dialogs/_UserSettingsDialog.scss | 2 +- .../settings/tabs/_GeneralSettingsTab.scss | 6 +++ res/css/views/settings/tabs/_SettingsTab.scss | 13 +++++ .../views/dialogs/UserSettingsDialog.js | 3 +- .../views/settings/tabs/GeneralSettingsTab.js | 53 +++++++++++++++++++ 7 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 res/css/views/settings/tabs/_GeneralSettingsTab.scss create mode 100644 res/css/views/settings/tabs/_SettingsTab.scss create mode 100644 src/components/views/settings/tabs/GeneralSettingsTab.js diff --git a/res/css/_components.scss b/res/css/_components.scss index e60e74383f..c1e2cc29cd 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -124,6 +124,8 @@ @import "./views/settings/_IntegrationsManager.scss"; @import "./views/settings/_KeyBackupPanel.scss"; @import "./views/settings/_Notifications.scss"; +@import "./views/settings/tabs/_GeneralSettingsTab.scss"; +@import "./views/settings/tabs/_SettingsTab.scss"; @import "./views/voip/_CallView.scss"; @import "./views/voip/_IncomingCallbox.scss"; @import "./views/voip/_VideoView.scss"; diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss index 957e989f53..322e763000 100644 --- a/res/css/structures/_TabbedView.scss +++ b/res/css/structures/_TabbedView.scss @@ -82,7 +82,7 @@ limitations under the License. .mx_TabbedView_tabPanel { width: calc(100% - 320px); display: inline-block; - margin-left: 20px; + margin-left: 70px; flex-grow: 1; } diff --git a/res/css/views/dialogs/_UserSettingsDialog.scss b/res/css/views/dialogs/_UserSettingsDialog.scss index e98001fea4..75d89a1d06 100644 --- a/res/css/views/dialogs/_UserSettingsDialog.scss +++ b/res/css/views/dialogs/_UserSettingsDialog.scss @@ -4,7 +4,7 @@ text-align: center; color: $dialog-title-fg-color; margin-top: 23px; - margin-bottom: 32px; + margin-bottom: 24px; padding: 0; } diff --git a/res/css/views/settings/tabs/_GeneralSettingsTab.scss b/res/css/views/settings/tabs/_GeneralSettingsTab.scss new file mode 100644 index 0000000000..b04975fc2d --- /dev/null +++ b/res/css/views/settings/tabs/_GeneralSettingsTab.scss @@ -0,0 +1,6 @@ +.mx_GeneralSettingsTab_profile input { + display: block; + font-size: 14px; + padding: 5px; + border-radius: 4px; +} \ No newline at end of file diff --git a/res/css/views/settings/tabs/_SettingsTab.scss b/res/css/views/settings/tabs/_SettingsTab.scss new file mode 100644 index 0000000000..13e8b0ab51 --- /dev/null +++ b/res/css/views/settings/tabs/_SettingsTab.scss @@ -0,0 +1,13 @@ +.mx_SettingsTab_heading { + font-size: 20px; + font-weight: 600; +} + +.mx_SettingsTab_subheading { + font-size: 14px; + display: block; +} + +.mx_SettingsTab_section { + margin-top: 10px; +} \ No newline at end of file diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index 1896bd38e0..b5450c01b5 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -19,6 +19,7 @@ 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"; export default class UserSettingsDialog extends React.Component { static propTypes = { @@ -27,7 +28,7 @@ export default class UserSettingsDialog extends React.Component { _getTabs() { return [ - new Tab(_td("General"), ,
General Test
), + new Tab(_td("General"), , ), new Tab(_td("Notifications"), ,
Notifications Test
), new Tab(_td("Preferences"), ,
Preferences Test
), new Tab(_td("Voice & Video"), ,
Voice Test
), diff --git a/src/components/views/settings/tabs/GeneralSettingsTab.js b/src/components/views/settings/tabs/GeneralSettingsTab.js new file mode 100644 index 0000000000..60f557d918 --- /dev/null +++ b/src/components/views/settings/tabs/GeneralSettingsTab.js @@ -0,0 +1,53 @@ +/* +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"; + +export default class GeneralSettingsTab extends React.Component { + static propTypes = { + onFinished: PropTypes.func.isRequired, + }; + + render() { + return ( +
+
{_t("General")}
+
+ {_t("Profile")} + + +
+
+ {_t("Profile")} + + +
+
+ {_t("Profile")} + + +
+
+ {_t("Profile")} + + +
+
+ ); + } +} From d907647e61b8d3e5664c2a4c6a8eb6e8964edfbf Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 18 Jan 2019 20:25:44 -0700 Subject: [PATCH 07/15] Spread the tabs out a bit --- res/css/structures/_TabbedView.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss index 322e763000..ecb7193c18 100644 --- a/res/css/structures/_TabbedView.scss +++ b/res/css/structures/_TabbedView.scss @@ -37,6 +37,7 @@ limitations under the License. font-size: 12px; font-weight: 600; height: 20px; + margin-bottom: 6px; } .mx_TabbedView_tabLabel_active { From a8ec40a8b06f6e6472a24b49f6aedab3b5efa4ef Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 18 Jan 2019 20:36:02 -0700 Subject: [PATCH 08/15] Add a temporary tab for visiting the old settings For debugging purposes --- src/components/structures/MatrixChat.js | 4 ++++ .../views/dialogs/UserSettingsDialog.js | 18 ++++++++++++++++ .../views/settings/tabs/GeneralSettingsTab.js | 21 +++++++------------ src/i18n/strings/en_EN.json | 5 +++-- 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 9733576bd0..2b585506ae 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -616,6 +616,10 @@ export default React.createClass({ //this._setPage(PageTypes.UserSettings); //this.notifyNewScreen('settings'); break; + case 'view_old_user_settings': + this._setPage(PageTypes.UserSettings); + this.notifyNewScreen('settings'); + break; case 'close_settings': this.setState({ leftDisabled: false, diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index b5450c01b5..d355eb77bc 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -20,6 +20,23 @@ 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'; + +export class TempTab extends React.Component { + // TODO: Ditch this + static propTypes = { + onClose: PropTypes.func.isRequired, + }; + + componentDidMount(): void { + dis.dispatch({action: "view_old_user_settings"}); + this.props.onClose(); + } + + render() { + return
Hello World
; + } +} export default class UserSettingsDialog extends React.Component { static propTypes = { @@ -34,6 +51,7 @@ export default class UserSettingsDialog extends React.Component { new Tab(_td("Voice & Video"), ,
Voice Test
), new Tab(_td("Security & Privacy"), ,
Security Test
), new Tab(_td("Help & About"), ,
Help Test
), + new Tab(_td("Visit old settings"), , ), ]; } diff --git a/src/components/views/settings/tabs/GeneralSettingsTab.js b/src/components/views/settings/tabs/GeneralSettingsTab.js index 60f557d918..433b12a674 100644 --- a/src/components/views/settings/tabs/GeneralSettingsTab.js +++ b/src/components/views/settings/tabs/GeneralSettingsTab.js @@ -15,37 +15,32 @@ limitations under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; import {_t} from "../../../../languageHandler"; export default class GeneralSettingsTab extends React.Component { - static propTypes = { - onFinished: PropTypes.func.isRequired, - }; - render() { return (
{_t("General")}
{_t("Profile")} - - + +
{_t("Profile")} - - + +
{_t("Profile")} - - + +
{_t("Profile")} - - + +
); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 2d547eda0c..6a06811fb3 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -401,6 +401,8 @@ "Off": "Off", "On": "On", "Noisy": "Noisy", + "General": "General", + "Profile": "Profile", "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", @@ -1043,11 +1045,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", - "General": "General", "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!", @@ -1289,7 +1291,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", From c3692aa9ae7237c1366712c9ff7c58e70ef38681 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 21 Jan 2019 16:03:44 -0700 Subject: [PATCH 09/15] Use the right icons, sizing, and font families for things --- res/css/structures/_TabbedView.scss | 12 +++++------ .../views/dialogs/_UserSettingsDialog.scss | 21 ++++++++----------- res/css/views/settings/tabs/_SettingsTab.scss | 6 +++++- res/img/feather-icons/cancel.svg | 10 +++++++++ res/img/feather-icons/help-circle.svg | 6 ++++++ res/img/feather-icons/lock.svg | 6 ++++++ res/img/feather-icons/sliders.svg | 5 +++++ res/themes/dharma/css/_dharma.scss | 1 + res/themes/light/css/_base.scss | 1 + 9 files changed, 49 insertions(+), 19 deletions(-) create mode 100644 res/img/feather-icons/cancel.svg create mode 100644 res/img/feather-icons/help-circle.svg create mode 100644 res/img/feather-icons/lock.svg create mode 100644 res/img/feather-icons/sliders.svg diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss index ecb7193c18..b888aa287b 100644 --- a/res/css/structures/_TabbedView.scss +++ b/res/css/structures/_TabbedView.scss @@ -30,7 +30,7 @@ limitations under the License. } .mx_TabbedView_tabLabel { - vertical-align: middle; + vertical-align: text-top; cursor: pointer; display: block; border-radius: 3px; @@ -46,23 +46,23 @@ limitations under the License. } .mx_TabbedView_tabLabel_icon { - width: 12px; - height: 12px; + width: 14px; + height: 14px; margin-left: 6px; margin-right: 9px; position: relative; } .mx_TabbedView_tabLabel_icon > .mx_TabbedView_maskedIcon { - width: 12px; - height: 12px; + width: 14px; + height: 14px; display: inline-block; } .mx_TabbedView_tabLabel_icon > .mx_TabbedView_maskedIcon:before { background-color: $tab-label-icon-bg-color; mask-repeat: no-repeat; - mask-size: 12px; + mask-size: 14px; mask-position: center; content: ''; position: absolute; diff --git a/res/css/views/dialogs/_UserSettingsDialog.scss b/res/css/views/dialogs/_UserSettingsDialog.scss index 75d89a1d06..25be25bd56 100644 --- a/res/css/views/dialogs/_UserSettingsDialog.scss +++ b/res/css/views/dialogs/_UserSettingsDialog.scss @@ -3,29 +3,29 @@ display: block; text-align: center; color: $dialog-title-fg-color; - margin-top: 23px; + margin-top: 16px; margin-bottom: 24px; padding: 0; } .mx_UserSettingsDialog_close { position: absolute; - top: 23px; + top: 16px; right: 25px; } .mx_UserSettingsDialog_closeIcon { - width: 14px; - height: 14px; + width: 16px; + height: 16px; display: inline-block; } .mx_UserSettingsDialog_closeIcon:before { // TODO: Use real icon - mask: url('$(res)/img/feather-icons/video.svg'); + mask: url('$(res)/img/feather-icons/cancel.svg'); background-color: $dialog-close-fg-color; mask-repeat: no-repeat; - mask-size: 14px; + mask-size: 16px; mask-position: center; content: ''; position: absolute; @@ -52,16 +52,13 @@ } .mx_UserSettingsDialog_preferencesIcon:before { - // TODO: Use real icon - mask: url('$(res)/img/feather-icons/paperclip.svg'); + mask: url('$(res)/img/feather-icons/sliders.svg'); } .mx_UserSettingsDialog_securityIcon:before { - // TODO: Use real icon - mask: url('$(res)/img/feather-icons/life-buoy.svg'); + mask: url('$(res)/img/feather-icons/lock.svg'); } .mx_UserSettingsDialog_helpIcon:before { - // TODO: Use real icon - mask: url('$(res)/img/feather-icons/share.svg'); + mask: url('$(res)/img/feather-icons/help-circle.svg'); } \ No newline at end of file diff --git a/res/css/views/settings/tabs/_SettingsTab.scss b/res/css/views/settings/tabs/_SettingsTab.scss index 13e8b0ab51..0753df56af 100644 --- a/res/css/views/settings/tabs/_SettingsTab.scss +++ b/res/css/views/settings/tabs/_SettingsTab.scss @@ -1,13 +1,17 @@ .mx_SettingsTab_heading { font-size: 20px; font-weight: 600; + color: $primary-fg-color; } .mx_SettingsTab_subheading { font-size: 14px; display: block; + font-family: $font-family-semibold; + color: $primary-fg-color; + margin-bottom: 10px; } .mx_SettingsTab_section { margin-top: 10px; -} \ No newline at end of file +} diff --git a/res/img/feather-icons/cancel.svg b/res/img/feather-icons/cancel.svg new file mode 100644 index 0000000000..6b734e4053 --- /dev/null +++ b/res/img/feather-icons/cancel.svg @@ -0,0 +1,10 @@ + + + + Slice 1 + Created with Sketch. + + + + + \ No newline at end of file diff --git a/res/img/feather-icons/help-circle.svg b/res/img/feather-icons/help-circle.svg new file mode 100644 index 0000000000..7ecb0a8f35 --- /dev/null +++ b/res/img/feather-icons/help-circle.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/res/img/feather-icons/lock.svg b/res/img/feather-icons/lock.svg new file mode 100644 index 0000000000..1330903b30 --- /dev/null +++ b/res/img/feather-icons/lock.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/res/img/feather-icons/sliders.svg b/res/img/feather-icons/sliders.svg new file mode 100644 index 0000000000..5b5ec8656c --- /dev/null +++ b/res/img/feather-icons/sliders.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index cf3738a1bb..6d907d17be 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -5,6 +5,7 @@ horizontal mess. Arial empirically gets it right, hence prioritising Arial here. */ $font-family: 'Nunito', Arial, Helvetica, Sans-Serif; +$font-family-semibold: 'Nunito SemiBold', Arial, Helvetica, Sans-Serif; // typical text (dark-on-white in light skin) $primary-fg-color: #454545; diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index 45b64243c6..ec36f15b89 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -4,6 +4,7 @@ horizontal mess. Arial empirically gets it right, hence prioritising Arial here. */ $font-family: 'Open Sans', Arial, Helvetica, Sans-Serif; +$font-family-semibold: 'Open Sans', Arial, Helvetica, Sans-Serif; // typical text (dark-on-white in light skin) $primary-fg-color: #454545; From 15a56fa90b50ec3d3fb64f7cf203e4ee4e41376e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 21 Jan 2019 17:27:43 -0700 Subject: [PATCH 10/15] Improve the profile section a bit and add a highlight to the temp tab --- res/css/structures/_TabbedView.scss | 6 ++ res/css/views/elements/_AccessibleButton.scss | 21 +++++ .../settings/tabs/_GeneralSettingsTab.scss | 25 +++++- res/themes/dharma/css/_dharma.scss | 6 ++ res/themes/light/css/_base.scss | 6 ++ src/components/structures/TabbedView.js | 3 +- .../views/elements/AccessibleButton.js | 83 ++++++++++------- .../views/settings/tabs/GeneralSettingsTab.js | 90 ++++++++++++++----- src/i18n/strings/en_EN.json | 1 + 9 files changed, 186 insertions(+), 55 deletions(-) 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", From b678e84272a2e0cbc0ca9a9098948046f6007827 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 21 Jan 2019 17:32:25 -0700 Subject: [PATCH 11/15] Remove extraneous TODO comment The thing it describes actually happened --- res/css/views/dialogs/_UserSettingsDialog.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/res/css/views/dialogs/_UserSettingsDialog.scss b/res/css/views/dialogs/_UserSettingsDialog.scss index 25be25bd56..def5d3f724 100644 --- a/res/css/views/dialogs/_UserSettingsDialog.scss +++ b/res/css/views/dialogs/_UserSettingsDialog.scss @@ -21,7 +21,6 @@ } .mx_UserSettingsDialog_closeIcon:before { - // TODO: Use real icon mask: url('$(res)/img/feather-icons/cancel.svg'); background-color: $dialog-close-fg-color; mask-repeat: no-repeat; From a488304410fc42fa5e6c30acf1c24d45db87a23b Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 21 Jan 2019 17:49:48 -0700 Subject: [PATCH 12/15] Appease the linter Appease the linter round 2 Appease the linter round 3 Appease the linter round 4 Appease the linter round 5 --- .eslintrc.js | 5 +- src/components/structures/MatrixChat.js | 3 +- src/components/structures/TabbedView.js | 8 +-- .../views/dialogs/UserSettingsDialog.js | 50 +++++++++++++------ .../views/elements/AccessibleButton.js | 4 +- .../views/settings/tabs/GeneralSettingsTab.js | 14 ++---- 6 files changed, 52 insertions(+), 32 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 971809f851..ec48f6b2ff 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -42,9 +42,8 @@ module.exports = { // bind or arrow function in props causes performance issues // (but we currently use them in some places) - "react/jsx-no-bind": ["warn", { - "ignoreRefs": true, - }], + // It's disabled here, but we should using it sparingly. + "react/jsx-no-bind": "off", "react/jsx-key": ["error"], // Components in JSX should always be defined. diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 2b585506ae..4e6ffbd2c8 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -610,12 +610,13 @@ export default React.createClass({ case 'view_indexed_room': this._viewIndexedRoom(payload.roomIndex); break; - case 'view_user_settings': + case 'view_user_settings': { const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog"); Modal.createTrackedDialog('User settings', '', UserSettingsDialog, {}); //this._setPage(PageTypes.UserSettings); //this.notifyNewScreen('settings'); break; + } case 'view_old_user_settings': this._setPage(PageTypes.UserSettings); this.notifyNewScreen('settings'); diff --git a/src/components/structures/TabbedView.js b/src/components/structures/TabbedView.js index ab1f5d76de..43979140a8 100644 --- a/src/components/structures/TabbedView.js +++ b/src/components/structures/TabbedView.js @@ -81,9 +81,11 @@ export class TabbedView extends React.Component { tabIcon = {tab.icon}; } + const onClickHandler = () => this._setActiveTab(tab); + return ( - this._setActiveTab(tab)}> + {tabIcon} {_t(tab.label)} @@ -113,4 +115,4 @@ export class TabbedView extends React.Component {
); } -} \ No newline at end of file +} diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index d355eb77bc..d0b3f436b8 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -22,8 +22,8 @@ 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 { - // TODO: Ditch this static propTypes = { onClose: PropTypes.func.isRequired, }; @@ -45,13 +45,41 @@ export default class UserSettingsDialog extends React.Component { _getTabs() { return [ - new Tab(_td("General"), , ), - new Tab(_td("Notifications"), ,
Notifications Test
), - new Tab(_td("Preferences"), ,
Preferences Test
), - new Tab(_td("Voice & Video"), ,
Voice Test
), - new Tab(_td("Security & Privacy"), ,
Security Test
), - new Tab(_td("Help & About"), ,
Help Test
), - new Tab(_td("Visit old settings"), , ), + new Tab( + _td("General"), + , + , + ), + new Tab( + _td("Notifications"), + , +
Notifications Test
, + ), + new Tab( + _td("Preferences"), + , +
Preferences Test
, + ), + new Tab( + _td("Voice & Video"), + , +
Voice Test
, + ), + new Tab( + _td("Security & Privacy"), + , +
Security Test
, + ), + new Tab( + _td("Help & About"), + , +
Help Test
, + ), + new Tab( + _td("Visit old settings"), + , + , + ), ]; } @@ -66,12 +94,6 @@ export default class UserSettingsDialog extends React.Component { - // ); } } diff --git a/src/components/views/elements/AccessibleButton.js b/src/components/views/elements/AccessibleButton.js index 938426d5bc..1c39ba4f49 100644 --- a/src/components/views/elements/AccessibleButton.js +++ b/src/components/views/elements/AccessibleButton.js @@ -39,7 +39,7 @@ export default function AccessibleButton(props) { // 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) { + restProps.onKeyDown = function(e) { if (e.keyCode === KeyCode.ENTER) { e.stopPropagation(); e.preventDefault(); @@ -50,7 +50,7 @@ export default function AccessibleButton(props) { e.preventDefault(); } }; - restProps.onKeyUp = function (e) { + restProps.onKeyUp = function(e) { if (e.keyCode === KeyCode.SPACE) { e.stopPropagation(); e.preventDefault(); diff --git a/src/components/views/settings/tabs/GeneralSettingsTab.js b/src/components/views/settings/tabs/GeneralSettingsTab.js index 513d33fabe..81501033ec 100644 --- a/src/components/views/settings/tabs/GeneralSettingsTab.js +++ b/src/components/views/settings/tabs/GeneralSettingsTab.js @@ -21,7 +21,6 @@ import Field from "../../elements/Field"; import AccessibleButton from "../../elements/AccessibleButton"; export default class GeneralSettingsTab extends React.Component { - constructor() { super(); @@ -56,6 +55,7 @@ export default class GeneralSettingsTab extends React.Component { }; _renderProfileSection() { + // TODO: Ditch avatar placeholder and use the real thing const form = (
@@ -63,24 +63,20 @@ export default class GeneralSettingsTab extends React.Component {

{this.state.userId}

+ onChange={this._onDisplayNameChanged} />
- {/*TODO: Ditch avatar placeholder and use the real thing*/} -
+
+ disabled={!this.state.enableProfileSave}> {_t("Save")} ); - return ( -
+ return (
{_t("Profile")} {form}
From feed17d9acc54d5e4d130b68640c4a4eda4ab359 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 21 Jan 2019 20:55:40 -0700 Subject: [PATCH 13/15] Minor code style fix --- src/components/views/settings/tabs/GeneralSettingsTab.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/GeneralSettingsTab.js b/src/components/views/settings/tabs/GeneralSettingsTab.js index 81501033ec..c8816325c0 100644 --- a/src/components/views/settings/tabs/GeneralSettingsTab.js +++ b/src/components/views/settings/tabs/GeneralSettingsTab.js @@ -76,7 +76,8 @@ export default class GeneralSettingsTab extends React.Component { ); - return (
+ return ( +
{_t("Profile")} {form}
From 0deb210fd89b91b41d147c4a08969ea9acc1b480 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 22 Jan 2019 09:54:35 -0700 Subject: [PATCH 14/15] Collapse DOM around tab label icon --- res/css/structures/_TabbedView.scss | 22 +++++++------------ .../views/dialogs/_UserSettingsDialog.scss | 12 +++++----- src/components/structures/TabbedView.js | 8 +++---- .../views/dialogs/UserSettingsDialog.js | 14 ++++++------ 4 files changed, 25 insertions(+), 31 deletions(-) diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss index 86149020b8..0f4b67ad71 100644 --- a/res/css/structures/_TabbedView.scss +++ b/res/css/structures/_TabbedView.scss @@ -38,6 +38,7 @@ limitations under the License. font-weight: 600; height: 20px; margin-bottom: 6px; + position: relative; } .mx_TabbedView_tabLabel_active { @@ -50,34 +51,27 @@ limitations under the License. background-color: orange; } -.mx_TabbedView_tabLabel_icon { - width: 14px; - height: 14px; +.mx_TabbedView_maskedIcon {; margin-left: 6px; margin-right: 9px; - position: relative; -} - -.mx_TabbedView_tabLabel_icon > .mx_TabbedView_maskedIcon { width: 14px; height: 14px; display: inline-block; } -.mx_TabbedView_tabLabel_icon > .mx_TabbedView_maskedIcon:before { +.mx_TabbedView_maskedIcon:before { + display: inline-block; background-color: $tab-label-icon-bg-color; mask-repeat: no-repeat; mask-size: 14px; + width: 14px; + height: 14px; mask-position: center; content: ''; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; + vertical-align: middle; } -.mx_TabbedView_tabLabel_active .mx_TabbedView_tabLabel_icon > .mx_TabbedView_maskedIcon:before { +.mx_TabbedView_tabLabel_active .mx_TabbedView_maskedIcon:before { background-color: $tab-label-active-icon-bg-color; } diff --git a/res/css/views/dialogs/_UserSettingsDialog.scss b/res/css/views/dialogs/_UserSettingsDialog.scss index def5d3f724..c4bd8a5110 100644 --- a/res/css/views/dialogs/_UserSettingsDialog.scss +++ b/res/css/views/dialogs/_UserSettingsDialog.scss @@ -39,25 +39,25 @@ // ========================================================== .mx_UserSettingsDialog_settingsIcon:before { - mask: url('$(res)/img/feather-icons/settings.svg'); + mask-image: url('$(res)/img/feather-icons/settings.svg'); } .mx_UserSettingsDialog_voiceIcon:before { - mask: url('$(res)/img/feather-icons/phone.svg'); + mask-image: url('$(res)/img/feather-icons/phone.svg'); } .mx_UserSettingsDialog_bellIcon:before { - mask: url('$(res)/img/feather-icons/notifications.svg'); + mask-image: url('$(res)/img/feather-icons/notifications.svg'); } .mx_UserSettingsDialog_preferencesIcon:before { - mask: url('$(res)/img/feather-icons/sliders.svg'); + mask-image: url('$(res)/img/feather-icons/sliders.svg'); } .mx_UserSettingsDialog_securityIcon:before { - mask: url('$(res)/img/feather-icons/lock.svg'); + mask-image: url('$(res)/img/feather-icons/lock.svg'); } .mx_UserSettingsDialog_helpIcon:before { - mask: url('$(res)/img/feather-icons/help-circle.svg'); + mask-image: url('$(res)/img/feather-icons/help-circle.svg'); } \ No newline at end of file diff --git a/src/components/structures/TabbedView.js b/src/components/structures/TabbedView.js index 43979140a8..2b136128f3 100644 --- a/src/components/structures/TabbedView.js +++ b/src/components/structures/TabbedView.js @@ -26,12 +26,12 @@ export class Tab { /** * Creates a new tab. * @param {string} tabLabel The untranslated tab label. - * @param {string} tabIconJsx The JSX for the tab icon. This should be a plain img element or null. + * @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, tabIconJsx, tabJsx) { + constructor(tabLabel, tabIconClass, tabJsx) { this.label = tabLabel; - this.icon = tabIconJsx; + this.icon = tabIconClass; this.body = tabJsx; } } @@ -78,7 +78,7 @@ export class TabbedView extends React.Component { let tabIcon = null; if (tab.icon) { - tabIcon = {tab.icon}; + tabIcon = ; } const onClickHandler = () => this._setActiveTab(tab); diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index d0b3f436b8..dd404ce280 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -47,37 +47,37 @@ export default class UserSettingsDialog extends React.Component { return [ new Tab( _td("General"), - , + "mx_UserSettingsDialog_settingsIcon", , ), new Tab( _td("Notifications"), - , + "mx_UserSettingsDialog_bellIcon",
Notifications Test
, ), new Tab( _td("Preferences"), - , + "mx_UserSettingsDialog_preferencesIcon",
Preferences Test
, ), new Tab( _td("Voice & Video"), - , + "mx_UserSettingsDialog_voiceIcon",
Voice Test
, ), new Tab( _td("Security & Privacy"), - , + "mx_UserSettingsDialog_securityIcon",
Security Test
, ), new Tab( _td("Help & About"), - , + "mx_UserSettingsDialog_helpIcon",
Help Test
, ), new Tab( _td("Visit old settings"), - , + "mx_UserSettingsDialog_helpIcon", , ), ]; From 5ef901513313d985ada89b8ec622c4a8c2732145 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 22 Jan 2019 10:28:33 -0700 Subject: [PATCH 15/15] Add labs flag for tabbed settings --- src/components/structures/MatrixChat.js | 11 ++-- src/i18n/strings/en_EN.json | 87 +++++++++++++------------ src/settings/Settings.js | 6 ++ 3 files changed, 57 insertions(+), 47 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 4e6ffbd2c8..4c783eedac 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -611,10 +611,13 @@ export default React.createClass({ this._viewIndexedRoom(payload.roomIndex); break; case 'view_user_settings': { - const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog"); - Modal.createTrackedDialog('User settings', '', UserSettingsDialog, {}); - //this._setPage(PageTypes.UserSettings); - //this.notifyNewScreen('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': diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index defc77e3dd..ae9eb2e9b4 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,9 +402,10 @@ "Off": "Off", "On": "On", "Noisy": "Noisy", - "General": "General", - "Profile": "Profile", "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", @@ -536,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", @@ -713,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?", @@ -889,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", @@ -1015,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 '=_-./'", @@ -1102,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.", 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"),