Skip to content

Commit 1f3df73

Browse files
committed
feat(Account): Enable a user to delete their own account
1 parent 5a4a7f2 commit 1f3df73

File tree

9 files changed

+84
-4
lines changed

9 files changed

+84
-4
lines changed

src/actions/user.js

+1
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,5 @@ export default {
2727
importLegacyServices: PropTypes.arrayOf(PropTypes.shape({
2828
recipe: PropTypes.string.isRequired,
2929
})).isRequired,
30+
delete: {},
3031
};

src/api/UserApi.js

+4
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,8 @@ export default class UserApi {
4646
getLegacyServices() {
4747
return this.server.getLegacyServices();
4848
}
49+
50+
delete() {
51+
return this.server.deleteAccount();
52+
}
4953
}

src/api/server/ServerApi.js

+13
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,19 @@ export default class ServerApi {
125125
return user;
126126
}
127127

128+
async deleteAccount() {
129+
const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/me`, this._prepareAuthRequest({
130+
method: 'DELETE',
131+
}));
132+
if (!request.ok) {
133+
throw request;
134+
}
135+
const data = await request.json();
136+
137+
console.debug('ServerApi::deleteAccount resolves', data);
138+
return data;
139+
}
140+
128141
// Services
129142
async getServices() {
130143
const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/me/services`, this._prepareAuthRequest({

src/components/settings/account/AccountDashboard.js

+46-3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ const messages = defineMessages({
2828
id: 'settings.account.headlineInvoices',
2929
defaultMessage: '!!Invoices',
3030
},
31+
headlineDangerZone: {
32+
id: 'settings.account.headlineDangerZone',
33+
defaultMessage: '!!Danger Zone',
34+
},
3135
manageSubscriptionButtonLabel: {
3236
id: 'settings.account.manageSubscription.label',
3337
defaultMessage: '!!!Manage your subscription',
@@ -72,6 +76,18 @@ const messages = defineMessages({
7276
id: 'settings.account.mining.cancel',
7377
defaultMessage: '!!!Cancel mining',
7478
},
79+
deleteAccount: {
80+
id: 'settings.account.deleteAccount',
81+
defaultMessage: '!!!Delete account',
82+
},
83+
deleteInfo: {
84+
id: 'settings.account.deleteInfo',
85+
defaultMessage: '!!!If you don\'t need your Franz account any longer, you can delete your account and all related data here.',
86+
},
87+
deleteEmailSent: {
88+
id: 'settings.account.deleteEmailSent',
89+
defaultMessage: '!!!You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!',
90+
},
7591
});
7692

7793
@observer
@@ -90,6 +106,9 @@ export default class AccountDashboard extends Component {
90106
openExternalUrl: PropTypes.func.isRequired,
91107
onCloseSubscriptionWindow: PropTypes.func.isRequired,
92108
stopMiner: PropTypes.func.isRequired,
109+
deleteAccount: PropTypes.func.isRequired,
110+
isLoadingDeleteAccount: PropTypes.bool.isRequired,
111+
isDeleteAccountSuccessful: PropTypes.bool.isRequired,
93112
};
94113

95114
static contextTypes = {
@@ -111,6 +130,9 @@ export default class AccountDashboard extends Component {
111130
retryUserInfoRequest,
112131
onCloseSubscriptionWindow,
113132
stopMiner,
133+
deleteAccount,
134+
isLoadingDeleteAccount,
135+
isDeleteAccountSuccessful,
114136
} = this.props;
115137
const { intl } = this.context;
116138

@@ -201,7 +223,7 @@ export default class AccountDashboard extends Component {
201223
/>
202224
</div>
203225
</div>
204-
<div className="account__box account__box--last">
226+
<div className="account__box">
205227
<h2>{intl.formatMessage(messages.headlineInvoices)}</h2>
206228
<table className="invoices">
207229
<tbody>
@@ -230,7 +252,7 @@ export default class AccountDashboard extends Component {
230252

231253
{user.isMiner && (
232254
<div className="account franz-form">
233-
<div className="account__box">
255+
<div className="account__box account__box--last">
234256
<h2>{intl.formatMessage(messages.headlineSubscription)}</h2>
235257
<div className="account__subscription">
236258
<div>
@@ -267,7 +289,7 @@ export default class AccountDashboard extends Component {
267289
<Loader />
268290
) : (
269291
<div className="account franz-form">
270-
<div className="account__box account__box--last">
292+
<div className="account__box">
271293
<h2>{intl.formatMessage(messages.headlineUpgrade)}</h2>
272294
<SubscriptionForm
273295
onCloseWindow={onCloseSubscriptionWindow}
@@ -276,8 +298,29 @@ export default class AccountDashboard extends Component {
276298
</div>
277299
)
278300
)}
301+
302+
<div className="account franz-form">
303+
<div className="account__box">
304+
<h2>{intl.formatMessage(messages.headlineDangerZone)}</h2>
305+
{!isDeleteAccountSuccessful && (
306+
<div className="account__subscription">
307+
<p>{intl.formatMessage(messages.deleteInfo)}</p>
308+
<Button
309+
label={intl.formatMessage(messages.deleteAccount)}
310+
buttonType="danger"
311+
onClick={() => deleteAccount()}
312+
loaded={!isLoadingDeleteAccount}
313+
/>
314+
</div>
315+
)}
316+
{isDeleteAccountSuccessful && (
317+
<p>{intl.formatMessage(messages.deleteEmailSent)}</p>
318+
)}
319+
</div>
320+
</div>
279321
</div>
280322
)}
323+
281324
</div>
282325
<ReactTooltip place="right" type="dark" effect="solid" />
283326
</div>

src/components/ui/Button.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export default class Button extends Component {
6868
loaded={loaded}
6969
lines={10}
7070
scale={0.4}
71-
color={buttonType === '' ? '#FFF' : '#373a3c'}
71+
color={buttonType !== 'secondary' ? '#FFF' : '#373a3c'}
7272
component="span"
7373
/>
7474
{label}

src/containers/settings/AccountScreen.js

+5
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export default class AccountScreen extends Component {
6969
render() {
7070
const { user, payment, app } = this.props.stores;
7171
const { openExternalUrl } = this.props.actions.app;
72+
const { user: userActions } = this.props.actions;
7273

7374
const isLoadingUserInfo = user.getUserInfoRequest.isExecuting;
7475
const isLoadingOrdersInfo = payment.ordersDataRequest.isExecuting;
@@ -89,6 +90,9 @@ export default class AccountScreen extends Component {
8990
openExternalUrl={url => openExternalUrl({ url })}
9091
onCloseSubscriptionWindow={() => this.onCloseWindow()}
9192
stopMiner={() => this.stopMiner()}
93+
deleteAccount={userActions.delete}
94+
isLoadingDeleteAccount={user.deleteAccountRequest.isExecuting}
95+
isDeleteAccountSuccessful={user.deleteAccountRequest.wasExecuted && !user.deleteAccountRequest.isError}
9296
/>
9397
);
9498
}
@@ -109,6 +113,7 @@ AccountScreen.wrappedComponent.propTypes = {
109113
}).isRequired,
110114
user: PropTypes.shape({
111115
update: PropTypes.func.isRequired,
116+
delete: PropTypes.func.isRequired,
112117
}).isRequired,
113118
}).isRequired,
114119
};

src/i18n/locales/en-US.json

+4
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
"settings.account.headlineSubscription": "Your subscription",
7171
"settings.account.headlineUpgrade": "Upgrade your account & support Franz",
7272
"settings.account.headlineInvoices": "Invoices",
73+
"settings.account.headlineDangerZone": "Danger Zone",
7374
"settings.account.manageSubscription.label": "Manage your subscription",
7475
"settings.account.accountType.basic": "Basic Account",
7576
"settings.account.accountType.premium": "Premium Supporter Account",
@@ -86,6 +87,9 @@
8687
"settings.account.mining.active": "You are right now performing {hashes} calculations per second.",
8788
"settings.account.mining.moreInformation": "Get more information",
8889
"settings.account.mining.cancel": "Cancel mining",
90+
"settings.account.deleteAccount": "Delete account",
91+
"settings.account.deleteInfo": "If you don't need your Franz account any longer, you can delete your account and all related data here.",
92+
"settings.account.deleteEmailSent": "You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!",
8993
"settings.navigation.availableServices": "Available services",
9094
"settings.navigation.yourServices": "Your services",
9195
"settings.navigation.account": "Account",

src/stores/UserStore.js

+6
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export default class UserStore extends Store {
2626
@observable getUserInfoRequest = new CachedRequest(this.api.user, 'getInfo');
2727
@observable updateUserInfoRequest = new Request(this.api.user, 'updateInfo');
2828
@observable getLegacyServicesRequest = new CachedRequest(this.api.user, 'getLegacyServices');
29+
@observable deleteAccountRequest = new CachedRequest(this.api.user, 'delete');
2930

3031
@observable isImportLegacyServicesExecuting = false;
3132
@observable isImportLegacyServicesCompleted = false;
@@ -57,6 +58,7 @@ export default class UserStore extends Store {
5758
this.actions.user.update.listen(this._update.bind(this));
5859
this.actions.user.resetStatus.listen(this._resetStatus.bind(this));
5960
this.actions.user.importLegacyServices.listen(this._importLegacyServices.bind(this));
61+
this.actions.user.delete.listen(this._delete.bind(this));
6062

6163
// Reactions
6264
this.registerReactions([
@@ -212,6 +214,10 @@ export default class UserStore extends Store {
212214
this.isImportLegacyServicesCompleted = true;
213215
}
214216

217+
@action async _delete() {
218+
this.deleteAccountRequest.execute();
219+
}
220+
215221
// This is a mobx autorun which forces the user to login if not authenticated
216222
_requireAuthenticatedUser = () => {
217223
if (this.isTokenExpired) {

src/styles/settings.scss

+4
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,10 @@
281281
margin-left: auto;
282282
}
283283

284+
.franz-form__button {
285+
white-space: nowrap;
286+
}
287+
284288
div {
285289
height: auto;
286290
}

0 commit comments

Comments
 (0)