From 4517717a4dac69b2536a7554684d21590853e4ee Mon Sep 17 00:00:00 2001 From: Ole-Martin Bratteng <1681525+omBratteng@users.noreply.github.com> Date: Thu, 6 Nov 2025 12:38:55 +0100 Subject: [PATCH 1/2] fix: show spinner on account deletion --- .../shared/src/components/profile/AccountDangerZone.tsx | 7 ++++++- packages/shared/src/components/widgets/DangerZone.tsx | 3 +++ .../components/layouts/SettingsLayout/Security/index.tsx | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/shared/src/components/profile/AccountDangerZone.tsx b/packages/shared/src/components/profile/AccountDangerZone.tsx index c94b47566e8..16fd4105a0f 100644 --- a/packages/shared/src/components/profile/AccountDangerZone.tsx +++ b/packages/shared/src/components/profile/AccountDangerZone.tsx @@ -9,6 +9,8 @@ interface AccountDangerZoneProps { onDelete: () => void; className?: string; children?: ReactNode; + buttonDisabled?: boolean; + buttonLoading?: boolean; } const Important = () => ( @@ -48,6 +50,8 @@ const ImportantActiveAppleSubscription = () => { function AccountDangerZone({ onDelete, className, + buttonDisabled = false, + buttonLoading = false, }: AccountDangerZoneProps): ReactElement { const { isPlus, status, plusProvider } = usePlusSubscription(); @@ -70,7 +74,8 @@ function AccountDangerZone({ important={ disableDeletion ? : } - buttonDisabled={disableDeletion} + buttonDisabled={disableDeletion || buttonDisabled} + buttonLoading={buttonLoading} /> ); } diff --git a/packages/shared/src/components/widgets/DangerZone.tsx b/packages/shared/src/components/widgets/DangerZone.tsx index c71974c8c5e..ddd77d52b96 100644 --- a/packages/shared/src/components/widgets/DangerZone.tsx +++ b/packages/shared/src/components/widgets/DangerZone.tsx @@ -18,6 +18,7 @@ interface DangerZoneProps { onClick: () => void; className?: string; buttonDisabled?: boolean; + buttonLoading?: boolean; } export function DangerZone({ @@ -29,6 +30,7 @@ export function DangerZone({ onClick, children, buttonDisabled = false, + buttonLoading = false, }: DangerZoneProps): ReactElement { return (
{cta} diff --git a/packages/webapp/components/layouts/SettingsLayout/Security/index.tsx b/packages/webapp/components/layouts/SettingsLayout/Security/index.tsx index 2057ea55729..15657104a22 100644 --- a/packages/webapp/components/layouts/SettingsLayout/Security/index.tsx +++ b/packages/webapp/components/layouts/SettingsLayout/Security/index.tsx @@ -124,6 +124,7 @@ function AccountSecurityDefault({ const [linkProvider, setLinkProvider] = useState(null); const hasPassword = userProviders?.result?.includes('password'); const { showPrompt } = usePrompt(); + const [isDeleting, setIsDeleting] = useState(false); const manageSocialProviders = async ({ type, @@ -155,8 +156,10 @@ function AccountSecurityDefault({ const deleteAccountPrompt = async () => { if (await showPrompt(deleteAccountPromptOptions)) { try { + setIsDeleting(true); await deleteAccount(); } catch (error) { + setIsDeleting(false); displayToast(DEFAULT_ERROR); return; } @@ -287,6 +290,7 @@ function AccountSecurityDefault({ deleteAccountPrompt()} className="mt-6" + buttonLoading={isDeleting} /> From d1eb8c17712187495bf4b0414a586e6cf4dfc892 Mon Sep 17 00:00:00 2001 From: Ole-Martin Bratteng <1681525+omBratteng@users.noreply.github.com> Date: Thu, 6 Nov 2025 12:47:18 +0100 Subject: [PATCH 2/2] refactor: use useMutation instead of manual state handling --- .../layouts/SettingsLayout/Security/index.tsx | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/packages/webapp/components/layouts/SettingsLayout/Security/index.tsx b/packages/webapp/components/layouts/SettingsLayout/Security/index.tsx index 15657104a22..827fdb07c16 100644 --- a/packages/webapp/components/layouts/SettingsLayout/Security/index.tsx +++ b/packages/webapp/components/layouts/SettingsLayout/Security/index.tsx @@ -1,3 +1,5 @@ +import React, { useState } from 'react'; +import type { FormEvent, MutableRefObject, ReactElement } from 'react'; import { providerMap } from '@dailydotdev/shared/src/components/auth/common'; import { Button, @@ -7,9 +9,7 @@ import { } from '@dailydotdev/shared/src/components/buttons/Button'; import { LockIcon, MailIcon } from '@dailydotdev/shared/src/components/icons'; import AccountDangerZone from '@dailydotdev/shared/src/components/profile/AccountDangerZone'; -import AuthContext from '@dailydotdev/shared/src/contexts/AuthContext'; -import type { FormEvent, MutableRefObject, ReactElement } from 'react'; -import React, { useContext, useState } from 'react'; +import { useAuthContext } from '@dailydotdev/shared/src/contexts/AuthContext'; import type { AuthSession, KratosProviderData, @@ -33,6 +33,7 @@ import { capitalize } from '@dailydotdev/shared/src/lib/strings'; import { BOOT_LOCAL_KEY } from '@dailydotdev/shared/src/contexts/common'; import { DEFAULT_ERROR } from '@dailydotdev/shared/src/graphql/common'; import { Tooltip } from '@dailydotdev/shared/src/components/tooltip/Tooltip'; +import { useMutation } from '@tanstack/react-query'; import AccountContentSection from '../AccountContentSection'; import { AccountPageContainer } from '../AccountPageContainer'; import type { ManageSocialProvidersProps } from '../common'; @@ -118,13 +119,12 @@ function AccountSecurityDefault({ onUpdatePassword, onUpdateProviders, }: AccountSecurityDefaultProps): ReactElement { - const { deleteAccount } = useContext(AuthContext); + const { deleteAccount } = useAuthContext(); const { displayToast } = useToastNotification(); const { onUpdateSignBack } = useSignBack(); const [linkProvider, setLinkProvider] = useState(null); const hasPassword = userProviders?.result?.includes('password'); const { showPrompt } = usePrompt(); - const [isDeleting, setIsDeleting] = useState(false); const manageSocialProviders = async ({ type, @@ -153,21 +153,22 @@ function AccountSecurityDefault({ manageSocialProviders({ type: 'unlink', provider }); } }; - const deleteAccountPrompt = async () => { - if (await showPrompt(deleteAccountPromptOptions)) { - try { - setIsDeleting(true); + const { mutate: deleteAccountPrompt, isPending: isDeleting } = useMutation({ + mutationKey: ['deleteAccount'], + mutationFn: async () => { + if (await showPrompt(deleteAccountPromptOptions)) { await deleteAccount(); - } catch (error) { - setIsDeleting(false); - displayToast(DEFAULT_ERROR); - return; } + }, + onError: () => { + displayToast(DEFAULT_ERROR); + }, + onSuccess: async () => { await onUpdateSignBack(null, null); globalThis?.localStorage.removeItem(BOOT_LOCAL_KEY); window.location.replace('/'); - } - }; + }, + }); useEventListener(globalThis, 'message', async (e) => { if (e.data?.eventKey !== AuthEvent.SocialRegistration) {