diff --git a/.changeset/clever-shirts-talk.md b/.changeset/clever-shirts-talk.md new file mode 100644 index 0000000000000..39d0973fab9ca --- /dev/null +++ b/.changeset/clever-shirts-talk.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixes 2FA status change not reflected in UI when enabling/disabling TOTP/emailcode diff --git a/apps/meteor/client/views/account/security/TwoFactorEmail.tsx b/apps/meteor/client/views/account/security/TwoFactorEmail.tsx index c77c395cdefc2..152b2e45f385e 100644 --- a/apps/meteor/client/views/account/security/TwoFactorEmail.tsx +++ b/apps/meteor/client/views/account/security/TwoFactorEmail.tsx @@ -1,7 +1,7 @@ import { Box, Button, Margins } from '@rocket.chat/fuselage'; -import { useUser } from '@rocket.chat/ui-contexts'; +import { useUser, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import type { ComponentProps } from 'react'; -import { useCallback } from 'react'; +import { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useEndpointAction } from '../../../hooks/useEndpointAction'; @@ -9,8 +9,10 @@ import { useEndpointAction } from '../../../hooks/useEndpointAction'; const TwoFactorEmail = (props: ComponentProps) => { const { t } = useTranslation(); const user = useUser(); + const dispatchToastMessage = useToastMessageDispatch(); - const isEnabled = user?.services?.email2fa?.enabled; + const [isEmail2faEnabled, setIsEmail2faEnabled] = useState(user?.services?.email2fa?.enabled); + const [registeringEmail2fa, setRegisteringEmail2fa] = useState(false); const enable2faAction = useEndpointAction('POST', '/v1/users.2fa.enableEmail', { successMessage: t('Two-factor_authentication_enabled'), @@ -20,25 +22,45 @@ const TwoFactorEmail = (props: ComponentProps) => { }); const handleEnable = useCallback(async () => { - await enable2faAction(); - }, [enable2faAction]); + if (registeringEmail2fa) return; + setRegisteringEmail2fa(true); + + try { + await enable2faAction(); + setIsEmail2faEnabled(true); + } catch (error) { + dispatchToastMessage({ type: 'error', message: error }); + } finally { + setRegisteringEmail2fa(false); + } + }, [enable2faAction, registeringEmail2fa, dispatchToastMessage, t]); + const handleDisable = useCallback(async () => { - await disable2faAction(); - }, [disable2faAction]); + if (registeringEmail2fa) return; + setRegisteringEmail2fa(true); + + try { + await disable2faAction(); + setIsEmail2faEnabled(false); + } catch (error) { + dispatchToastMessage({ type: 'error', message: error }); + } finally { + setRegisteringEmail2fa(false); + } + }, [disable2faAction, registeringEmail2fa, dispatchToastMessage, t]); return ( {t('Two-factor_authentication_email')} - {isEnabled && ( - - )} - {!isEnabled && ( + ) : ( <> {t('Two-factor_authentication_email_is_currently_disabled')} - diff --git a/apps/meteor/client/views/account/security/TwoFactorTOTP.tsx b/apps/meteor/client/views/account/security/TwoFactorTOTP.tsx index cc282c2ba64eb..587d9ea720378 100644 --- a/apps/meteor/client/views/account/security/TwoFactorTOTP.tsx +++ b/apps/meteor/client/views/account/security/TwoFactorTOTP.tsx @@ -36,20 +36,20 @@ const TwoFactorTOTP = (props: TwoFactorTOTPProps): ReactElement => { const { register, handleSubmit } = useForm({ defaultValues: { authCode: '' } }); - const totpEnabled = user?.services?.totp?.enabled; + const [isTotpEnabled, setIsTotpEnabled] = useState(user?.services?.totp?.enabled); const closeModal = useCallback(() => setModal(null), [setModal]); useEffect(() => { const updateCodesRemaining = async (): Promise => { - if (!totpEnabled) { + if (!isTotpEnabled) { return false; } const result = await checkCodesRemainingFn(); setCodesRemaining(result.remaining); }; updateCodesRemaining(); - }, [checkCodesRemainingFn, setCodesRemaining, totpEnabled]); + }, [checkCodesRemainingFn, setCodesRemaining, isTotpEnabled]); const handleEnableTotp = useCallback(async () => { try { @@ -73,6 +73,9 @@ const TwoFactorTOTP = (props: TwoFactorTOTPProps): ReactElement => { return dispatchToastMessage({ type: 'error', message: t('Invalid_two_factor_code') }); } + setIsTotpEnabled(false); + setRegisteringTotp(false); + dispatchToastMessage({ type: 'success', message: t('Two-factor_authentication_disabled') }); } catch (error) { dispatchToastMessage({ type: 'error', message: error }); @@ -93,6 +96,7 @@ const TwoFactorTOTP = (props: TwoFactorTOTPProps): ReactElement => { } setModal(); + setIsTotpEnabled(true); } catch (error) { dispatchToastMessage({ type: 'error', message: error }); } @@ -121,7 +125,7 @@ const TwoFactorTOTP = (props: TwoFactorTOTPProps): ReactElement => { {t('Two-factor_authentication_via_TOTP')} - {!totpEnabled && !registeringTotp && ( + {!isTotpEnabled && !registeringTotp && ( <> {t('Two-factor_authentication_is_currently_disabled')} )} - {!totpEnabled && registeringTotp && ( + {!isTotpEnabled && registeringTotp && ( <> {t('Scan_QR_code')} {t('Scan_QR_code_alternative_s')} @@ -143,7 +147,7 @@ const TwoFactorTOTP = (props: TwoFactorTOTPProps): ReactElement => { )} - {totpEnabled && ( + {isTotpEnabled && ( <>