diff --git a/res/css/_components.scss b/res/css/_components.scss index ee34eeb524..89573ee865 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -127,6 +127,7 @@ @import "./views/rooms/_TopUnreadMessagesBar.scss"; @import "./views/rooms/_WhoIsTypingTile.scss"; @import "./views/settings/_DevicesPanel.scss"; +@import "./views/settings/_EmailAddresses.scss"; @import "./views/settings/_IntegrationsManager.scss"; @import "./views/settings/_KeyBackupPanel.scss"; @import "./views/settings/_Notifications.scss"; diff --git a/res/css/views/elements/_AccessibleButton.scss b/res/css/views/elements/_AccessibleButton.scss index 23445f5f6f..27c3a152ba 100644 --- a/res/css/views/elements/_AccessibleButton.scss +++ b/res/css/views/elements/_AccessibleButton.scss @@ -42,3 +42,35 @@ limitations under the License. color: $button-primary-disabled-fg-color; background-color: $button-primary-disabled-bg-color; } + +.mx_AccessibleButton_kind_primary_sm { + padding: 5px 12px !important; + color: $button-primary-fg-color; + background-color: $button-primary-bg-color; +} + +.mx_AccessibleButton_kind_primary_sm.mx_AccessibleButton_disabled { + color: $button-primary-disabled-fg-color; + background-color: $button-primary-disabled-bg-color; +} + +.mx_AccessibleButton_kind_danger { + color: $button-danger-fg-color; + background-color: $button-danger-bg-color; +} + +.mx_AccessibleButton_kind_danger.mx_AccessibleButton_disabled { + color: $button-danger-disabled-fg-color; + background-color: $button-danger-disabled-bg-color; +} + +.mx_AccessibleButton_kind_danger_sm { + padding: 5px 12px !important; + color: $button-danger-fg-color; + background-color: $button-danger-bg-color; +} + +.mx_AccessibleButton_kind_danger_sm.mx_AccessibleButton_disabled { + color: $button-danger-disabled-fg-color; + background-color: $button-danger-disabled-bg-color; +} \ No newline at end of file diff --git a/res/css/views/settings/_EmailAddresses.scss b/res/css/views/settings/_EmailAddresses.scss new file mode 100644 index 0000000000..1b61d2a9b5 --- /dev/null +++ b/res/css/views/settings/_EmailAddresses.scss @@ -0,0 +1,41 @@ +/* +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. +*/ + +.mx_ExistingEmailAddress { + margin-bottom: 5px; +} + +.mx_ExistingEmailAddress_delete { + margin-right: 5px; + cursor: pointer; + vertical-align: middle; +} + +.mx_ExistingEmailAddress_email { + vertical-align: middle; +} + +.mx_ExistingEmailAddress_promptText { + margin-right: 10px; +} + +.mx_ExistingEmailAddress_confirmBtn { + margin-right: 5px; +} + +.mx_EmailAddresses_new .mx_Field input { + width: calc(100% - 20px); +} diff --git a/res/css/views/settings/tabs/_GeneralSettingsTab.scss b/res/css/views/settings/tabs/_GeneralSettingsTab.scss index d1a31e37f8..2cfaebfb4d 100644 --- a/res/css/views/settings/tabs/_GeneralSettingsTab.scss +++ b/res/css/views/settings/tabs/_GeneralSettingsTab.scss @@ -30,4 +30,8 @@ limitations under the License. .mx_GeneralSettingsTab_changePassword .mx_Field:first-child { margin-top: 0; +} + +.mx_GeneralSettingsTab_accountSection > .mx_EmailAddresses { + margin-right: 100px; // Align with the other fields on the page } \ No newline at end of file diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index f54c058220..03d59cfc9d 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -211,6 +211,10 @@ $button-primary-fg-color: #ffffff; $button-primary-bg-color: #7ac9a1; $button-primary-disabled-fg-color: #ffffff; $button-primary-disabled-bg-color: #bce4d0; +$button-danger-fg-color: #ffffff; +$button-danger-bg-color: #f56679; +$button-danger-disabled-fg-color: #ffffff; +$button-danger-disabled-bg-color: #f5b6bb; // TODO: Verify color // unused? $progressbar-color: #000; diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index fce4e93112..d9d9bff2d3 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -207,6 +207,10 @@ $button-primary-fg-color: #ffffff; $button-primary-bg-color: #7ac9a1; $button-primary-disabled-fg-color: #ffffff; $button-primary-disabled-bg-color: #bce4d0; +$button-danger-fg-color: #ffffff; +$button-danger-bg-color: #f56679; +$button-danger-disabled-fg-color: #ffffff; +$button-danger-disabled-bg-color: #f5b6bb; // TODO: Verify color // unused? $progressbar-color: #000; diff --git a/src/AddThreepid.js b/src/AddThreepid.js index 337e38d867..8de7e9e21c 100644 --- a/src/AddThreepid.js +++ b/src/AddThreepid.js @@ -26,7 +26,7 @@ import { _t } from './languageHandler'; * the client owns the given email address, which is then passed to the * add threepid API on the homeserver. */ -class AddThreepid { +export default class AddThreepid { constructor() { this.clientSecret = MatrixClientPeg.get().generateClientSecret(); } @@ -124,5 +124,3 @@ class AddThreepid { }); } } - -module.exports = AddThreepid; diff --git a/src/components/views/elements/Field.js b/src/components/views/elements/Field.js index bbbd1e6bf7..42521120b9 100644 --- a/src/components/views/elements/Field.js +++ b/src/components/views/elements/Field.js @@ -38,6 +38,13 @@ export default class Field extends React.PureComponent { return this.refs.fieldInput.value; } + set value(newValue) { + if (!this.refs.fieldInput) { + throw new Error("No field input reference"); + } + this.refs.fieldInput.value = newValue; + } + render() { const extraProps = Object.assign({}, this.props); diff --git a/src/components/views/settings/EmailAddresses.js b/src/components/views/settings/EmailAddresses.js new file mode 100644 index 0000000000..24d1547fd7 --- /dev/null +++ b/src/components/views/settings/EmailAddresses.js @@ -0,0 +1,231 @@ +/* +Copyright 2019 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import PropTypes from 'prop-types'; +import {_t} from "../../../languageHandler"; +import MatrixClientPeg from "../../../MatrixClientPeg"; +import Field from "../elements/Field"; +import AccessibleButton from "../elements/AccessibleButton"; +import * as Email from "../../../email"; +import AddThreepid from "../../../AddThreepid"; +const sdk = require('../../../index'); +const Modal = require("../../../Modal"); + +/* +TODO: Improve the UX for everything in here. +It's very much placeholder, but it gets the job done. The old way of handling +email addresses in user settings was to use dialogs to communicate state, however +due to our dialog system overriding dialogs (causing unmounts) this creates problems +for a sane UX. For instance, the user could easily end up entering an email address +and receive a dialog to verify the address, which then causes the component here +to forget what it was doing and ultimately fail. Dialogs are still used in some +places to communicate errors - these should be replaced with inline validation when +that is available. + */ + +export class ExistingEmailAddress extends React.Component { + static propTypes = { + email: PropTypes.object.isRequired, + onRemoved: PropTypes.func.isRequired, + }; + + constructor() { + super(); + + this.state = { + verifyRemove: false, + }; + } + + _onRemove = (e) => { + e.stopPropagation(); + e.preventDefault(); + + this.setState({verifyRemove: true}); + }; + + _onDontRemove = (e) => { + e.stopPropagation(); + e.preventDefault(); + + this.setState({verifyRemove: false}); + }; + + _onActuallyRemove = (e) => { + e.stopPropagation(); + e.preventDefault(); + + MatrixClientPeg.get().deleteThreePid(this.props.email.medium, this.props.email.address).then(() => { + return this.props.onRemoved(this.props.email); + }).catch((err) => { + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + console.error("Unable to remove contact information: " + err); + Modal.createTrackedDialog('Remove 3pid failed', '', ErrorDialog, { + title: _t("Unable to remove contact information"), + description: ((err && err.message) ? err.message : _t("Operation failed")), + }); + }); + }; + + render() { + if (this.state.verifyRemove) { + return ( +
{_t("Set a new account password...")}
{passwordChangeForm} + + {_t("Email addresses")} +