From 5d6cdbef891b558900a0017aabe4c5090ee9b264 Mon Sep 17 00:00:00 2001 From: Anik Dhabal Babu Date: Fri, 16 Feb 2024 20:46:27 +0000 Subject: [PATCH] feat: ability to download all the 2FA recovery codes --- .../forms/2fa/view-recovery-codes-dialog.tsx | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/forms/2fa/view-recovery-codes-dialog.tsx b/apps/web/src/components/forms/2fa/view-recovery-codes-dialog.tsx index 18714332a..323bc7198 100644 --- a/apps/web/src/components/forms/2fa/view-recovery-codes-dialog.tsx +++ b/apps/web/src/components/forms/2fa/view-recovery-codes-dialog.tsx @@ -1,4 +1,4 @@ -import { useMemo } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; @@ -41,6 +41,7 @@ export type ViewRecoveryCodesDialogProps = { export const ViewRecoveryCodesDialog = ({ open, onOpenChange }: ViewRecoveryCodesDialogProps) => { const { toast } = useToast(); + const [recoveryCodesUrl, setRecoveryCodesUrl] = useState(''); const { mutateAsync: viewRecoveryCodes, data: viewRecoveryCodesData } = trpc.twoFactorAuthentication.viewRecoveryCodes.useMutation(); @@ -62,6 +63,16 @@ export const ViewRecoveryCodesDialog = ({ open, onOpenChange }: ViewRecoveryCode return 'view'; }, [viewRecoveryCodesData, isViewRecoveryCodesSubmitting]); + useEffect(() => { + if (viewRecoveryCodesData && viewRecoveryCodesData.recoveryCodes) { + const textBlob = new Blob([viewRecoveryCodesData.recoveryCodes.join('\n')], { + type: 'text/plain', + }); + if (recoveryCodesUrl) URL.revokeObjectURL(recoveryCodesUrl); + setRecoveryCodesUrl(URL.createObjectURL(textBlob)); + } + }, [viewRecoveryCodesData]); + const onViewRecoveryCodesFormSubmit = async ({ password }: TViewRecoveryCodesForm) => { try { await viewRecoveryCodes({ password }); @@ -139,8 +150,11 @@ export const ViewRecoveryCodesDialog = ({ open, onOpenChange }: ViewRecoveryCode )} -
+
+ + +
))