diff --git a/functions/index.js b/functions/index.js index 65aaa652..ccab9d2c 100644 --- a/functions/index.js +++ b/functions/index.js @@ -10,41 +10,49 @@ function timeout(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } -async function asyncForEach(array, callback) { - for (let index = 0; index < array.length; index++) { - // eslint-disable-next-line no-await-in-loop - await callback(array[index], index, array); +const deleteUserFunctionHandler = async (_, { auth }) => { + if (!auth) { + throw new functions.https.HttpsError( + 'failed-precondition', + 'The function must be called while authenticated.', + ); } -} + + try { + // 1. Delete user resumes + const userId = auth.uid; + const userResumesDataSnapshot = await admin + .database() + .ref('resumes') + .orderByChild('user') + .equalTo(userId) + .once('value'); + const userResumes = userResumesDataSnapshot.val(); + if (userResumes) { + Object.keys(userResumes).forEach(async (resumeId) => { + await admin.database().ref(`resumes/${resumeId}`).remove(); + }); + } + + // 2. Delete user + const userDataSnapshot = await admin + .database() + .ref(`users/${userId}`) + .once('value'); + const user = userDataSnapshot.val(); + if (user) { + await admin.database().ref(`users/${userId}`).remove(); + } + + return true; + } catch (error) { + throw new functions.https.HttpsError('internal', error.message); + } +}; exports.deleteUser = functions .runWith({ memory: '256MB' }) - .https.onCall((_, { auth }) => { - if (!auth) { - throw new functions.https.HttpsError( - 'failed-precondition', - 'The function must be called while authenticated.', - ); - } - - return new Promise((resolve) => { - const resumesRef = admin.database().ref('resumes'); - - resumesRef.once('value', async (snapshot) => { - const data = snapshot.val(); - - const resumes = Object.keys(data).filter( - (x) => data[x].user === auth.uid, - ); - - await asyncForEach(resumes, async (id) => { - await admin.database().ref(`resumes/${id}`).remove(); - }); - - resolve(); - }); - }); - }); + .https.onCall(deleteUserFunctionHandler); exports.printResume = functions .runWith({ memory: '1GB' }) diff --git a/src/components/builder/right/sections/Settings.js b/src/components/builder/right/sections/Settings.js index bf18ae3b..cd442bad 100644 --- a/src/components/builder/right/sections/Settings.js +++ b/src/components/builder/right/sections/Settings.js @@ -1,6 +1,7 @@ import React, { memo, useContext, useState } from 'react'; import { FaAngleDown } from 'react-icons/fa'; import { useTranslation, Trans } from 'react-i18next'; +import { toast } from 'react-toastify'; import UserContext from '../../../../contexts/UserContext'; import Button from '../../../shared/Button'; import Heading from '../../../shared/Heading'; @@ -40,10 +41,20 @@ const Settings = ({ id }) => { return; } - setDeleteText('Buh bye! :('); - setTimeout(() => { - deleteAccount(); - }, 500); + setDeleteText(t('shared.buttons.loading')); + + setTimeout( + () => + deleteAccount() + .then(() => { + setDeleteText('Buh bye! :('); + }) + .catch((error) => { + toast.error(error.message); + setDeleteText(t('builder.settings.dangerZone.button')); + }), + 500, + ); }; return ( diff --git a/src/contexts/UserContext.js b/src/contexts/UserContext.js index a6a215b1..95dc558e 100644 --- a/src/contexts/UserContext.js +++ b/src/contexts/UserContext.js @@ -76,13 +76,13 @@ const UserProvider = ({ children }) => { const deleteAccount = async () => { const { currentUser } = firebase.auth(); const deleteUser = firebase.functions().httpsCallable('deleteUser'); - deleteUser(); + + await deleteUser(); try { - deleteUser(); await currentUser.delete(); - } catch (e) { - if (e.code === 'auth/requires-recent-login') { + } catch (error) { + if (error.code === 'auth/requires-recent-login') { await loginWithGoogle(); await currentUser.delete(); }