From 12f0212aef109c3be888edc67d4ca540e3c7d5d4 Mon Sep 17 00:00:00 2001 From: alex-dembele Date: Fri, 2 Oct 2026 13:14:13 +0100 Subject: [PATCH] chore(frontend): clear the two eslint errors in the auth feature (#878) Signed-off-by: alex-dembele --- .../src/features/auth/MFAEnrollmentBanner.tsx | 2 +- .../src/features/auth/MFAPostAhaPrompt.tsx | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/frontend/src/features/auth/MFAEnrollmentBanner.tsx b/frontend/src/features/auth/MFAEnrollmentBanner.tsx index c5c58503..92ff8bf3 100644 --- a/frontend/src/features/auth/MFAEnrollmentBanner.tsx +++ b/frontend/src/features/auth/MFAEnrollmentBanner.tsx @@ -58,7 +58,7 @@ interface Copy { * implementations of "are they required?" is how the banner and the guard end * up disagreeing, and the one the user believes is the wrong one. */ -export function copyFor( +function copyFor( status: MFAStatus, tr: (fr: string, en: string) => string, /** Locale for the day count's plural form. Optional so existing callers hold. */ diff --git a/frontend/src/features/auth/MFAPostAhaPrompt.tsx b/frontend/src/features/auth/MFAPostAhaPrompt.tsx index 24bf98bb..7d65aeb4 100644 --- a/frontend/src/features/auth/MFAPostAhaPrompt.tsx +++ b/frontend/src/features/auth/MFAPostAhaPrompt.tsx @@ -57,13 +57,21 @@ export function MFAPostAhaPrompt() { // alert and the server is already refusing requests. A modal on top of that // is noise stacked on a blocker, not urgency. const alreadyBlocked = status?.state === 'required'; + const eligible = ahaReached && notConfigured && !alreadyBlocked; + // Decided while rendering, not in an effect (#878): the prompt opens on the + // render where the user becomes eligible, without a second render pass. The + // decision is taken once per transition into eligibility, as the effect did. + const [wasEligible, setWasEligible] = useState(false); + if (eligible !== wasEligible) { + setWasEligible(eligible); + if (eligible && !withinCooldown()) setOpen(true); + } + + // Starting the cooldown is the side effect, and it stays in an effect. useEffect(() => { - if (!ahaReached || !notConfigured || alreadyBlocked) return; - if (withinCooldown()) return; - setOpen(true); - localStorage.setItem(COOLDOWN_KEY, String(Date.now())); - }, [ahaReached, notConfigured, alreadyBlocked]); + if (open) localStorage.setItem(COOLDOWN_KEY, String(Date.now())); + }, [open]); useEffect(() => { if (!open) return;