Merge pull request #381 from matrix-org/dbkr/deactivate_account
Implement account deactivationpull/21833/head
commit
0351ab0a3d
|
@ -42,11 +42,11 @@ function logout() {
|
||||||
// logout doesn't work for guest sessions
|
// logout doesn't work for guest sessions
|
||||||
// Also we sometimes want to re-log in a guest session
|
// Also we sometimes want to re-log in a guest session
|
||||||
// if we abort the login
|
// if we abort the login
|
||||||
_onLoggedOut();
|
onLoggedOut();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return MatrixClientPeg.get().logout().then(_onLoggedOut,
|
return MatrixClientPeg.get().logout().then(onLoggedOut,
|
||||||
(err) => {
|
(err) => {
|
||||||
// Just throwing an error here is going to be very unhelpful
|
// Just throwing an error here is going to be very unhelpful
|
||||||
// if you're trying to log out because your server's down and
|
// 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,
|
// tokens expire (and if you really think you've been compromised,
|
||||||
// change your password).
|
// change your password).
|
||||||
console.log("Failed to call logout API: token will not be invalidated");
|
console.log("Failed to call logout API: token will not be invalidated");
|
||||||
_onLoggedOut();
|
onLoggedOut();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,11 @@ function startMatrixClient() {
|
||||||
MatrixClientPeg.start();
|
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) {
|
if (window.localStorage) {
|
||||||
const hsUrl = window.localStorage.getItem("mx_hs_url");
|
const hsUrl = window.localStorage.getItem("mx_hs_url");
|
||||||
const isUrl = window.localStorage.getItem("mx_is_url");
|
const isUrl = window.localStorage.getItem("mx_is_url");
|
||||||
|
@ -95,7 +99,9 @@ function _onLoggedOut() {
|
||||||
dis.dispatch({action: 'on_logged_out'});
|
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() {
|
function _stopMatrixClient() {
|
||||||
Notifier.stop();
|
Notifier.stop();
|
||||||
UserActivity.stop();
|
UserActivity.stop();
|
||||||
|
@ -106,5 +112,5 @@ function _stopMatrixClient() {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
setLoggedIn, logout, startMatrixClient
|
setLoggedIn, logout, startMatrixClient, onLoggedOut
|
||||||
};
|
};
|
||||||
|
|
|
@ -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.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.Presets'] = require('./components/views/create_room/Presets');
|
||||||
module.exports.components['views.create_room.RoomAlias'] = require('./components/views/create_room/RoomAlias');
|
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.ErrorDialog'] = require('./components/views/dialogs/ErrorDialog');
|
||||||
module.exports.components['views.dialogs.LogoutPrompt'] = require('./components/views/dialogs/LogoutPrompt');
|
module.exports.components['views.dialogs.LogoutPrompt'] = require('./components/views/dialogs/LogoutPrompt');
|
||||||
module.exports.components['views.dialogs.NeedToRegisterDialog'] = require('./components/views/dialogs/NeedToRegisterDialog');
|
module.exports.components['views.dialogs.NeedToRegisterDialog'] = require('./components/views/dialogs/NeedToRegisterDialog');
|
||||||
|
|
|
@ -262,6 +262,11 @@ module.exports = React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onDeactivateAccountClicked: function() {
|
||||||
|
const DeactivateAccountDialog = sdk.getComponent("dialogs.DeactivateAccountDialog");
|
||||||
|
Modal.createDialog(DeactivateAccountDialog, {});
|
||||||
|
},
|
||||||
|
|
||||||
_renderUserInterfaceSettings: function() {
|
_renderUserInterfaceSettings: function() {
|
||||||
var client = MatrixClientPeg.get();
|
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() {
|
render: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
var Loader = sdk.getComponent("elements.Spinner");
|
var Loader = sdk.getComponent("elements.Spinner");
|
||||||
|
@ -548,6 +567,8 @@ module.exports = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{this._renderDeactivateAccount()}
|
||||||
|
|
||||||
</GeminiScrollbar>
|
</GeminiScrollbar>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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,
|
||||||
|
};
|
Loading…
Reference in New Issue