Permalink
Browse files

feat(Account): Enable a user to delete their own account

  • Loading branch information...
adlk committed Dec 4, 2017
1 parent 5a4a7f2 commit 1f3df7365bdbe34d1998adc30281a16b5a7b5e31
@@ -27,4 +27,5 @@ export default {
importLegacyServices: PropTypes.arrayOf(PropTypes.shape({
recipe: PropTypes.string.isRequired,
})).isRequired,
delete: {},
};
@@ -46,4 +46,8 @@ export default class UserApi {
getLegacyServices() {
return this.server.getLegacyServices();
}
delete() {
return this.server.deleteAccount();
}
}
@@ -125,6 +125,19 @@ export default class ServerApi {
return user;
}
async deleteAccount() {
const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/me`, this._prepareAuthRequest({
method: 'DELETE',
}));
if (!request.ok) {
throw request;
}
const data = await request.json();
console.debug('ServerApi::deleteAccount resolves', data);
return data;
}
// Services
async getServices() {
const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/me/services`, this._prepareAuthRequest({
@@ -28,6 +28,10 @@ const messages = defineMessages({
id: 'settings.account.headlineInvoices',
defaultMessage: '!!Invoices',
},
headlineDangerZone: {
id: 'settings.account.headlineDangerZone',
defaultMessage: '!!Danger Zone',
},
manageSubscriptionButtonLabel: {
id: 'settings.account.manageSubscription.label',
defaultMessage: '!!!Manage your subscription',
@@ -72,6 +76,18 @@ const messages = defineMessages({
id: 'settings.account.mining.cancel',
defaultMessage: '!!!Cancel mining',
},
deleteAccount: {
id: 'settings.account.deleteAccount',
defaultMessage: '!!!Delete account',
},
deleteInfo: {
id: 'settings.account.deleteInfo',
defaultMessage: '!!!If you don\'t need your Franz account any longer, you can delete your account and all related data here.',
},
deleteEmailSent: {
id: 'settings.account.deleteEmailSent',
defaultMessage: '!!!You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!',
},
});
@observer
@@ -90,6 +106,9 @@ export default class AccountDashboard extends Component {
openExternalUrl: PropTypes.func.isRequired,
onCloseSubscriptionWindow: PropTypes.func.isRequired,
stopMiner: PropTypes.func.isRequired,
deleteAccount: PropTypes.func.isRequired,
isLoadingDeleteAccount: PropTypes.bool.isRequired,
isDeleteAccountSuccessful: PropTypes.bool.isRequired,
};
static contextTypes = {
@@ -111,6 +130,9 @@ export default class AccountDashboard extends Component {
retryUserInfoRequest,
onCloseSubscriptionWindow,
stopMiner,
deleteAccount,
isLoadingDeleteAccount,
isDeleteAccountSuccessful,
} = this.props;
const { intl } = this.context;
@@ -201,7 +223,7 @@ export default class AccountDashboard extends Component {
/>
</div>
</div>
<div className="account__box account__box--last">
<div className="account__box">
<h2>{intl.formatMessage(messages.headlineInvoices)}</h2>
<table className="invoices">
<tbody>
@@ -230,7 +252,7 @@ export default class AccountDashboard extends Component {
{user.isMiner && (
<div className="account franz-form">
<div className="account__box">
<div className="account__box account__box--last">
<h2>{intl.formatMessage(messages.headlineSubscription)}</h2>
<div className="account__subscription">
<div>
@@ -267,7 +289,7 @@ export default class AccountDashboard extends Component {
<Loader />
) : (
<div className="account franz-form">
<div className="account__box account__box--last">
<div className="account__box">
<h2>{intl.formatMessage(messages.headlineUpgrade)}</h2>
<SubscriptionForm
onCloseWindow={onCloseSubscriptionWindow}
@@ -276,8 +298,29 @@ export default class AccountDashboard extends Component {
</div>
)
)}
<div className="account franz-form">
<div className="account__box">
<h2>{intl.formatMessage(messages.headlineDangerZone)}</h2>
{!isDeleteAccountSuccessful && (
<div className="account__subscription">
<p>{intl.formatMessage(messages.deleteInfo)}</p>
<Button
label={intl.formatMessage(messages.deleteAccount)}
buttonType="danger"
onClick={() => deleteAccount()}
loaded={!isLoadingDeleteAccount}
/>
</div>
)}
{isDeleteAccountSuccessful && (
<p>{intl.formatMessage(messages.deleteEmailSent)}</p>
)}
</div>
</div>
</div>
)}
</div>
<ReactTooltip place="right" type="dark" effect="solid" />
</div>
@@ -68,7 +68,7 @@ export default class Button extends Component {
loaded={loaded}
lines={10}
scale={0.4}
color={buttonType === '' ? '#FFF' : '#373a3c'}
color={buttonType !== 'secondary' ? '#FFF' : '#373a3c'}
component="span"
/>
{label}
@@ -69,6 +69,7 @@ export default class AccountScreen extends Component {
render() {
const { user, payment, app } = this.props.stores;
const { openExternalUrl } = this.props.actions.app;
const { user: userActions } = this.props.actions;
const isLoadingUserInfo = user.getUserInfoRequest.isExecuting;
const isLoadingOrdersInfo = payment.ordersDataRequest.isExecuting;
@@ -89,6 +90,9 @@ export default class AccountScreen extends Component {
openExternalUrl={url => openExternalUrl({ url })}
onCloseSubscriptionWindow={() => this.onCloseWindow()}
stopMiner={() => this.stopMiner()}
deleteAccount={userActions.delete}
isLoadingDeleteAccount={user.deleteAccountRequest.isExecuting}
isDeleteAccountSuccessful={user.deleteAccountRequest.wasExecuted && !user.deleteAccountRequest.isError}
/>
);
}
@@ -109,6 +113,7 @@ AccountScreen.wrappedComponent.propTypes = {
}).isRequired,
user: PropTypes.shape({
update: PropTypes.func.isRequired,
delete: PropTypes.func.isRequired,
}).isRequired,
}).isRequired,
};
@@ -70,6 +70,7 @@
"settings.account.headlineSubscription": "Your subscription",
"settings.account.headlineUpgrade": "Upgrade your account & support Franz",
"settings.account.headlineInvoices": "Invoices",
"settings.account.headlineDangerZone": "Danger Zone",
"settings.account.manageSubscription.label": "Manage your subscription",
"settings.account.accountType.basic": "Basic Account",
"settings.account.accountType.premium": "Premium Supporter Account",
@@ -86,6 +87,9 @@
"settings.account.mining.active": "You are right now performing {hashes} calculations per second.",
"settings.account.mining.moreInformation": "Get more information",
"settings.account.mining.cancel": "Cancel mining",
"settings.account.deleteAccount": "Delete account",
"settings.account.deleteInfo": "If you don't need your Franz account any longer, you can delete your account and all related data here.",
"settings.account.deleteEmailSent": "You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!",
"settings.navigation.availableServices": "Available services",
"settings.navigation.yourServices": "Your services",
"settings.navigation.account": "Account",
@@ -26,6 +26,7 @@ export default class UserStore extends Store {
@observable getUserInfoRequest = new CachedRequest(this.api.user, 'getInfo');
@observable updateUserInfoRequest = new Request(this.api.user, 'updateInfo');
@observable getLegacyServicesRequest = new CachedRequest(this.api.user, 'getLegacyServices');
@observable deleteAccountRequest = new CachedRequest(this.api.user, 'delete');
@observable isImportLegacyServicesExecuting = false;
@observable isImportLegacyServicesCompleted = false;
@@ -57,6 +58,7 @@ export default class UserStore extends Store {
this.actions.user.update.listen(this._update.bind(this));
this.actions.user.resetStatus.listen(this._resetStatus.bind(this));
this.actions.user.importLegacyServices.listen(this._importLegacyServices.bind(this));
this.actions.user.delete.listen(this._delete.bind(this));
// Reactions
this.registerReactions([
@@ -212,6 +214,10 @@ export default class UserStore extends Store {
this.isImportLegacyServicesCompleted = true;
}
@action async _delete() {
this.deleteAccountRequest.execute();
}
// This is a mobx autorun which forces the user to login if not authenticated
_requireAuthenticatedUser = () => {
if (this.isTokenExpired) {
@@ -281,6 +281,10 @@
margin-left: auto;
}
.franz-form__button {
white-space: nowrap;
}
div {
height: auto;
}

0 comments on commit 1f3df73

Please sign in to comment.