From ad77a1751e6507c7e3b041a99d25ebafbfb3cfc8 Mon Sep 17 00:00:00 2001 From: npeter83 Date: Fri, 26 Jun 2026 01:19:01 +0200 Subject: [PATCH] fix(notifications): access-requests nudge auto-clears on approval MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The admin access-requests notice was issued from the on-user-load effect (keyed on user id), so it only re-resolved on a fresh load — it lingered until F5 after an approval dropped the pending count to 0. Move it to its own effect keyed on the pending count, so approving/denying (which refetches /api/me) clears or re-counts the notice live. --- frontend/src/App.tsx | 53 ++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 502e2eb..9489035 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -365,30 +365,6 @@ export default function App() { saveLayoutLocal(l); } if (isSupported(prefs.language)) setLanguage(prefs.language); - // Nudge admins when access requests are waiting (in lieu of a server-push bell). - const pending = meQuery.data?.pending_invites ?? 0; - if (meQuery.data?.role === "admin") { - // Keep a single, current nudge: drop any prior one (persisted copies reload as dismissed - // history, so they'd otherwise pile up one-per-reload) before re-issuing the live notice. - removeByMetaKind("access-requests"); - if (pending > 0) { - notify({ - level: "warning", - title: t("common.accessRequestsTitle"), - message: t("common.accessRequestsMessage", { count: pending }), - // Durable inbox link (survives reload, unlike a live action callback); the toast also - // gets a click via the action. - meta: { kind: "access-requests" }, - action: { - label: t("common.accessRequestsReview"), - onClick: () => { - focusAccessRequestsTab(); - setPage("users"); - }, - }, - }); - } - } // The demo account is shared: there are no subscriptions, so default it to the whole // library (the "my" feed would be empty). The communal-state warning is a permanent // banner (see DemoBanner below), not a toast that re-pops on every reload. @@ -398,6 +374,35 @@ export default function App() { // eslint-disable-next-line react-hooks/exhaustive-deps }, [meQuery.data?.id]); + // Nudge admins when access requests are waiting (in lieu of a server-push bell). Keyed on the + // pending COUNT so it re-resolves the moment an approval/denial changes it — not only on a + // fresh load (which is why the notice used to linger until F5 after an approval). + useEffect(() => { + if (meQuery.data?.role !== "admin") return; + // Keep a single, current nudge: drop any prior one (persisted copies reload as dismissed + // history, so they'd otherwise pile up one-per-reload) before re-issuing the live notice. + removeByMetaKind("access-requests"); + const pending = meQuery.data.pending_invites ?? 0; + if (pending > 0) { + notify({ + level: "warning", + title: t("common.accessRequestsTitle"), + message: t("common.accessRequestsMessage", { count: pending }), + // Durable inbox link (survives reload, unlike a live action callback); the toast also + // gets a click via the action. + meta: { kind: "access-requests" }, + action: { + label: t("common.accessRequestsReview"), + onClick: () => { + focusAccessRequestsTab(); + setPage("users"); + }, + }, + }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [meQuery.data?.role, meQuery.data?.pending_invites]); + // Theme is part of the Settings draft: apply locally for preview, persist on Save. function setTheme(next: ThemePrefs) { setThemeState(next);