Merge pull request #381 from matrix-org/dbkr/deactivate_account

Implement account deactivation
pull/21833/head
Richard van der Hoff 2016-08-04 15:38:29 +01:00 committed by GitHub
commit 0351ab0a3d
4 changed files with 171 additions and 6 deletions

View File

@ -42,11 +42,11 @@ function logout() {
// logout doesn't work for guest sessions
// Also we sometimes want to re-log in a guest session
// if we abort the login
_onLoggedOut();
onLoggedOut();
return;
}
return MatrixClientPeg.get().logout().then(_onLoggedOut,
return MatrixClientPeg.get().logout().then(onLoggedOut,
(err) => {
// Just throwing an error here is going to be very unhelpful
// if you're trying to log out because your server's down and
@ -56,7 +56,7 @@ function logout() {
// tokens expire (and if you really think you've been compromised,
// change your password).
console.log("Failed to call logout API: token will not be invalidated");
_onLoggedOut();
onLoggedOut();
}
);
}
@ -79,7 +79,11 @@ function startMatrixClient() {
MatrixClientPeg.start();
}
function _onLoggedOut() {
/*
* Stops a running client and all related services, used after
* a session has been logged out / ended.
*/
function onLoggedOut() {
if (window.localStorage) {
const hsUrl = window.localStorage.getItem("mx_hs_url");
const isUrl = window.localStorage.getItem("mx_is_url");
@ -95,7 +99,9 @@ function _onLoggedOut() {
dis.dispatch({action: 'on_logged_out'});
}
// stop all the background processes related to the current client
/**
* Stop all the background processes related to the current client
*/
function _stopMatrixClient() {
Notifier.stop();
UserActivity.stop();
@ -106,5 +112,5 @@ function _stopMatrixClient() {
}
module.exports = {
setLoggedIn, logout, startMatrixClient
setLoggedIn, logout, startMatrixClient, onLoggedOut
};

View File

@ -45,6 +45,7 @@ module.exports.components['views.avatars.RoomAvatar'] = require('./components/vi
module.exports.components['views.create_room.CreateRoomButton'] = require('./components/views/create_room/CreateRoomButton');
module.exports.components['views.create_room.Presets'] = require('./components/views/create_room/Presets');
module.exports.components['views.create_room.RoomAlias'] = require('./components/views/create_room/RoomAlias');
module.exports.components['views.dialogs.DeactivateAccountDialog'] = require('./components/views/dialogs/DeactivateAccountDialog');
module.exports.components['views.dialogs.ErrorDialog'] = require('./components/views/dialogs/ErrorDialog');
module.exports.components['views.dialogs.LogoutPrompt'] = require('./components/views/dialogs/LogoutPrompt');
module.exports.components['views.dialogs.NeedToRegisterDialog'] = require('./components/views/dialogs/NeedToRegisterDialog');

View File

@ -262,6 +262,11 @@ module.exports = React.createClass({
});
},
_onDeactivateAccountClicked: function() {
const DeactivateAccountDialog = sdk.getComponent("dialogs.DeactivateAccountDialog");
Modal.createDialog(DeactivateAccountDialog, {});
},
_renderUserInterfaceSettings: function() {
var client = MatrixClientPeg.get();
@ -379,6 +384,20 @@ module.exports = React.createClass({
)
},
_renderDeactivateAccount: function() {
// We can't deactivate a guest account.
if (MatrixClientPeg.get().isGuest()) return null;
return <div>
<h3>Deactivate Account</h3>
<div className="mx_UserSettings_section">
<button className="mx_UserSettings_button danger"
onClick={this._onDeactivateAccountClicked}>Deactivate my account
</button>
</div>
</div>;
},
render: function() {
var self = this;
var Loader = sdk.getComponent("elements.Spinner");
@ -548,6 +567,8 @@ module.exports = React.createClass({
</div>
</div>
{this._renderDeactivateAccount()}
</GeminiScrollbar>
</div>
);

View File

@ -0,0 +1,137 @@
/*
Copyright 2016 OpenMarket 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 sdk from '../../../index';
import MatrixClientPeg from '../../../MatrixClientPeg';
import Lifecycle from '../../../Lifecycle';
import Velocity from 'velocity-vector';
export default class DeactivateAccountDialog extends React.Component {
constructor(props, context) {
super(props, context);
this._passwordField = null;
this._onOk = this._onOk.bind(this);
this._onCancel = this._onCancel.bind(this);
this._onPasswordFieldChange = this._onPasswordFieldChange.bind(this);
this.state = {
confirmButtonEnabled: false,
busy: false,
errStr: null,
};
}
_onPasswordFieldChange(ev) {
this.setState({
confirmButtonEnabled: Boolean(ev.target.value),
});
}
_onOk() {
// This assumes that the HS requires password UI auth
// for this endpoint. In reality it could be any UI auth.
this.setState({busy: true});
MatrixClientPeg.get().deactivateAccount({
type: 'm.login.password',
user: MatrixClientPeg.get().credentials.userId,
password: this._passwordField.value,
}).done(() => {
Lifecycle.onLoggedOut();
this.props.onFinished(false);
}, (err) => {
let errStr = 'Unknown error';
// https://matrix.org/jira/browse/SYN-744
if (err.httpStatus == 401 || err.httpStatus == 403) {
errStr = 'Incorrect password';
Velocity(this._passwordField, "callout.shake", 300);
}
this.setState({
busy: false,
errStr: errStr,
});
});
}
_onCancel() {
this.props.onFinished(false);
}
render() {
const Loader = sdk.getComponent("elements.Spinner");
let passwordBoxClass = '';
let error = null;
if (this.state.errStr) {
error = <div className="error">
{this.state.err_str}
</div>
passwordBoxClass = 'error';
}
const okLabel = this.state.busy ? <Loader /> : 'Deactivate Account';
const okEnabled = this.state.confirmButtonEnabled && !this.state.busy;
let cancelButton = null;
if (!this.state.busy) {
cancelButton = <button onClick={this._onCancel} autoFocus={true}>
Cancel
</button>
}
return (
<div className="mx_DeactivateAccountDialog">
<div className="mx_Dialog_title danger">
Deactivate Account
</div>
<div className="mx_Dialog_content">
<p>This will make your account permanently unusable. You will not be able to re-register the same user ID.</p>
<p>This action is irreversible.</p>
<p>To continue, please enter your password.</p>
<p>Password:</p>
<input
type="password"
onChange={this._onPasswordFieldChange}
ref={(e) => {this._passwordField = e;}}
className={passwordBoxClass}
/>
{error}
</div>
<div className="mx_Dialog_buttons">
<button
className="mx_Dialog_primary danger"
onClick={this._onOk}
disabled={!okEnabled}
>
{okLabel}
</button>
{cancelButton}
</div>
</div>
);
}
}
DeactivateAccountDialog.propTypes = {
onFinished: React.PropTypes.func.isRequired,
};