From 1bf1634c81a77d315b0391dd6772836830fb4a9e Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 11 Sep 2019 10:47:10 +0100 Subject: [PATCH] add profile delete methods and ui --- src/components/Modal/Modal.tsx | 12 ++- src/pages/Settings/SettingsPage.tsx | 9 +++ src/pages/Settings/content/ProfileDelete.tsx | 82 ++++++++++++++++++++ src/pages/common/Login/SignUp.form.tsx | 2 +- src/stores/User/user.store.ts | 22 +++++- 5 files changed, 122 insertions(+), 5 deletions(-) create mode 100644 src/pages/Settings/content/ProfileDelete.tsx diff --git a/src/components/Modal/Modal.tsx b/src/components/Modal/Modal.tsx index 7446d8a9c5..f3ec7f4e3b 100644 --- a/src/components/Modal/Modal.tsx +++ b/src/components/Modal/Modal.tsx @@ -4,7 +4,8 @@ import styled from 'styled-components' interface IProps { // provide onDidDismiss function to enable backdrop click dismiss - onDidDismiss?: () => void + onDidDismiss: (data?: any) => void + height?: number } interface IState { isOpen: boolean @@ -27,7 +28,6 @@ const ModalContent = styled.div` justify-content: space-between; width: 300px; max-width: 100%; - height: 200px; max-height: 100%; position: fixed; z-index: 10; @@ -51,13 +51,19 @@ export class Modal extends React.Component { render() { const isOpen = this.state + const { height, children } = this.props return ( isOpen && ( this.dismiss()} /> - {this.props.children} + + {children} + ) ) } + static defaultProps: IProps = { + onDidDismiss: () => null, + } } diff --git a/src/pages/Settings/SettingsPage.tsx b/src/pages/Settings/SettingsPage.tsx index 830d99a051..f33e26577b 100644 --- a/src/pages/Settings/SettingsPage.tsx +++ b/src/pages/Settings/SettingsPage.tsx @@ -14,6 +14,7 @@ import { Flex } from 'rebass' import { Avatar } from 'src/components/Avatar' import Text from 'src/components/Text' import { UserMapPinEdit } from './content/UserMapPinEdit' +import { ProfileDelete } from './content/ProfileDelete' interface IProps { user: IUser @@ -23,6 +24,7 @@ interface IState { editMode: boolean user: IUser showNotification: boolean + showDeleteDialog?: boolean } export class UserSettings extends React.Component { constructor(props: IProps) { @@ -34,6 +36,10 @@ export class UserSettings extends React.Component { this.setState({ showNotification: true }) } + public deleteProfile(reauthPw: string) { + this.props.userStore.deleteUser(reauthPw) + } + public render() { const readOnly = !this.state.editMode @@ -93,6 +99,9 @@ export class UserSettings extends React.Component { /> + this.deleteProfile(reauthPw)} + /> ) } diff --git a/src/pages/Settings/content/ProfileDelete.tsx b/src/pages/Settings/content/ProfileDelete.tsx new file mode 100644 index 0000000000..18a869d61a --- /dev/null +++ b/src/pages/Settings/content/ProfileDelete.tsx @@ -0,0 +1,82 @@ +import * as React from 'react' +import { Modal } from 'src/components/Modal/Modal' +import { Button } from 'src/components/Button' +import Text from 'src/components/Text' +import { FlexContainer } from 'src/components/Layout/FlexContainer' +import { Form, Field } from 'react-final-form' +import { InputField } from 'src/components/Form/Fields' + +interface IState { + showDeleteDialog: boolean +} +interface IProps { + onConfirmation: (reauthPw: string) => void +} +export class ProfileDelete extends React.Component { + constructor(props: IProps) { + super(props) + this.state = { + showDeleteDialog: false, + } + } + onModalDismiss(values: any) { + const reauthPw = values.password + this.setState({ showDeleteDialog: false }) + if (reauthPw) { + this.props.onConfirmation(reauthPw) + } + } + + render() { + return ( + <> + + {this.state.showDeleteDialog && ( + this.onModalDismiss(confirm)}> + Confirm your password to delete your account +
this.onModalDismiss(values)} + render={({ values, handleSubmit }) => { + return ( + + + + + + + + ) + }} + /> +
+ )} + + ) + } + + static defaultProps: IProps = { + onConfirmation: () => null, + } +} diff --git a/src/pages/common/Login/SignUp.form.tsx b/src/pages/common/Login/SignUp.form.tsx index 2fba3b8cb9..235834b775 100644 --- a/src/pages/common/Login/SignUp.form.tsx +++ b/src/pages/common/Login/SignUp.form.tsx @@ -70,7 +70,7 @@ export class SignUpForm extends React.Component { public async checkUserNameUnique(userName: string) { const user = await this.props.userStore.getUserProfile(userName) - return user ? false : true + return user && !user._deleted ? false : true } public render = () => { diff --git a/src/stores/User/user.store.ts b/src/stores/User/user.store.ts index d5180e5081..f085ac1b3e 100644 --- a/src/stores/User/user.store.ts +++ b/src/stores/User/user.store.ts @@ -131,6 +131,26 @@ export class UserStore { return auth.signOut() } + public async deleteUser(reauthPw: string) { + // as delete operation is sensitive requires user to revalidate credentials first + const authUser = auth.currentUser as firebase.User + const credential = EmailAuthProvider.credential( + authUser.email as string, + reauthPw, + ) + try { + await authUser.reauthenticateAndRetrieveDataWithCredential(credential) + const user = this.user as IUser + await Database.deleteDoc(`v2_users/${user.userName}`) + await authUser.delete() + // TODO - delete user avatar + // TODO - show deleted notification + } catch (error) { + // TODO show notification if invalid credential + throw error + } + } + private async _createUserProfile(userName: string) { const authUser = auth.currentUser as firebase.User const user: IUser = { @@ -139,7 +159,7 @@ export class UserStore { userName, verified: false, } - await Database.setDoc(`v2_users${userName}`, user) + await Database.setDoc(`v2_users/${userName}`, user) this.updateUser(user) }