From d4a5d6c0d55adff74234d5b2ddf347c9fd16d757 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 23 Feb 2021 14:47:52 -0500 Subject: [PATCH 1/2] feat(user-account): add delete my account button --- lib/actions/user.js | 29 ++++++++++++++++++- .../user/existing-account-display.js | 5 ++++ lib/components/user/user-account-screen.js | 16 +++++++++- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/lib/actions/user.js b/lib/actions/user.js index 16157daa1..67fe1911b 100644 --- a/lib/actions/user.js +++ b/lib/actions/user.js @@ -8,7 +8,7 @@ import { createAction } from 'redux-actions' import { routingQuery } from './api' import { setQueryParam } from './form' import { routeTo } from './ui' -import { TRIPS_PATH } from '../util/constants' +import { TRIPS_PATH, URL_ROOT } from '../util/constants' import { secureFetch } from '../util/middleware' import { isBlank } from '../util/ui' import { isNewUser, positionHomeAndWorkFirst } from '../util/user' @@ -188,6 +188,33 @@ export function createOrUpdateUser (userData, silentOnSuccess = false) { } } +/** + * Deletes a user (and their corresponding trips, requests, etc.) from the + * middleware database. + * @param userData the user account to delete + * @param auth0 auth0 object (gives access to logout function) + */ +export function deleteUser (userData, auth0) { + return async function (dispatch, getState) { + const { accessToken, apiBaseUrl, apiKey } = getMiddlewareVariables(getState()) + const { id } = userData // Middleware ID, NOT auth0 (or similar) id. + const { data: deletedUser, message, status } = await secureFetch( + `${apiBaseUrl}${API_OTPUSER_PATH}/${id}`, + accessToken, + apiKey, + 'DELETE' + ) + // TODO: improve the UI feedback messages for this. + if (status === 'success' && deletedUser) { + alert(`Your user account (${userData.email}) has been deleted.`) + // Log out user and route them to the home page. + auth0.logout({returnTo: URL_ROOT}) + } else { + alert(`An error was encountered:\n${JSON.stringify(message)}`) + } + } +} + /** * Requests the verification email for the new user to be resent. */ diff --git a/lib/components/user/existing-account-display.js b/lib/components/user/existing-account-display.js index 8ee8b2107..b8573f54c 100644 --- a/lib/components/user/existing-account-display.js +++ b/lib/components/user/existing-account-display.js @@ -1,5 +1,6 @@ import React from 'react' +import DeleteUser from './delete-user' import NotificationPrefsPane from './notification-prefs-pane' import FavoritePlacesList from './places/favorite-places-list' import StackedPaneDisplay from './stacked-pane-display' @@ -29,6 +30,10 @@ const ExistingAccountDisplay = props => { pane: TermsOfUsePane, props: { ...props, disableCheckTerms: true }, title: 'Terms' + }, + { + pane: DeleteUser, + props } ] return ( diff --git a/lib/components/user/user-account-screen.js b/lib/components/user/user-account-screen.js index cc886a581..7ab7a4b23 100644 --- a/lib/components/user/user-account-screen.js +++ b/lib/components/user/user-account-screen.js @@ -50,6 +50,16 @@ class UserAccountScreen extends Component { this._updateUserPrefs(userData, true) } + _handleDeleteUser = (evt) => { + const {auth0, deleteUser, loggedInUser} = this.props + // Avoid triggering onsubmit with formik (which would result in a save user + // call). + evt.preventDefault() + if (confirm('Are you sure you would like to delete your user account? Once you do so, it cannot be recovered.')) { + deleteUser(loggedInUser, auth0) + } + } + _handleExit = () => { // On exit, route to default search route. this.props.routeTo('/') @@ -96,7 +106,9 @@ class UserAccountScreen extends Component { // We pass the Formik props below to the components rendered so that individual controls // can be wired to be managed by Formik. formikProps => { - const DisplayComponent = isCreating ? NewAccountWizard : ExistingAccountDisplay + const DisplayComponent = isCreating + ? NewAccountWizard + : ExistingAccountDisplay return (
@@ -107,6 +119,7 @@ class UserAccountScreen extends Component { loggedInUser={loggedInUser} onCancel={this._handleExit} onCreate={this._handleCreateNewUser} + onDelete={this._handleDeleteUser} onRequestPhoneVerificationCode={requestPhoneVerificationSms} onSendPhoneVerificationCode={verifyPhoneNumber} phoneFormatOptions={phoneFormatOptions} @@ -137,6 +150,7 @@ const mapStateToProps = (state, ownProps) => { const mapDispatchToProps = { createOrUpdateUser: userActions.createOrUpdateUser, + deleteUser: userActions.deleteUser, requestPhoneVerificationSms: userActions.requestPhoneVerificationSms, routeTo: uiActions.routeTo, verifyPhoneNumber: userActions.verifyPhoneNumber From 39fd5bcd1f1261a063b587ce1ee2891993b5d068 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 23 Feb 2021 14:56:59 -0500 Subject: [PATCH 2/2] refactor(delete-button): add delete-button file --- lib/components/user/delete-user.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 lib/components/user/delete-user.js diff --git a/lib/components/user/delete-user.js b/lib/components/user/delete-user.js new file mode 100644 index 000000000..7aaaec235 --- /dev/null +++ b/lib/components/user/delete-user.js @@ -0,0 +1,23 @@ +import React from 'react' +import { Button } from 'react-bootstrap' +import styled from 'styled-components' + +const DeleteButton = styled(Button)` + background-color: white; + color: #d9534f; + :hover, :focus, :active, :focus:active { + background-color: #f7f7f7; + color: #d9534f; + } +` + +/** + * Renders a delete user button for the account settings page. + */ +const DeleteUser = ({onDelete}) => ( + + Delete my account + +) + +export default DeleteUser