From 3eb6032f96adcccad5e28a06d2c801daf247e1d4 Mon Sep 17 00:00:00 2001 From: shin-core <153108882+shin-core@users.noreply.github.com> Date: Wed, 22 Jul 2026 01:30:03 +0900 Subject: [PATCH] fix(ui): restore the pre-rebrand gittensory_ legacy localStorage keys #5743's blanket gittensory->loopover rename overwrote two localStorage legacy-key string literals that were supposed to keep the OLD prefix, so useLocalStorage's migration fallback read the same key it had just missed under -- silently dropping a pre-rebrand preference. Restore LEGACY_DISMISS_KEY in onboarding-preview-card and the notification opt-in legacy key in notification-readiness-card to their gittensory_-prefixed values (matching the still-correct try-it.tsx pattern), so a maintainer's pre-rebrand onboarding dismissal / notification opt-in migrates forward instead of appearing lost. Regenerate scripts/branding-drift-baseline.json so the two intentionally-restored gittensory_ literals are recorded, not flagged as drift. Adds a regression test per component that seeds the legacy key and asserts the value is read forward to the current key. --- .../onboarding-preview-card.test.tsx | 24 +++++++++++++++++++ .../app-panels/onboarding-preview-card.tsx | 6 +++-- .../site/notification-readiness-card.test.tsx | 23 ++++++++++++++++++ .../site/notification-readiness-card.tsx | 4 +++- scripts/branding-drift-baseline.json | 2 ++ 5 files changed, 56 insertions(+), 3 deletions(-) diff --git a/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.test.tsx b/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.test.tsx index 4a6beac325..40c533c4b8 100644 --- a/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.test.tsx +++ b/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.test.tsx @@ -128,6 +128,30 @@ describe("OnboardingPreviewCard", () => { expect(screen.getByText("503 Service Unavailable")).toBeTruthy(); }); + it("migrates a pre-rebrand dismissal stored under the legacy gittensory_ key (#7782)", async () => { + // A maintainer who dismissed this card before the rebrand has the flag under the OLD gittensory_-prefixed + // key. #5743's blanket rename had corrupted the legacyKey to equal the current key, so this value was + // silently dropped and the card reappeared. With the legacy key restored, the pre-rebrand dismissal is read + // forward: the card stays hidden and no demo API call runs. + window.localStorage.clear(); + // useLocalStorage stores the `{ dismissed }` object shape, so the legacy value mirrors it. + window.localStorage.setItem( + "gittensory_maintainer_onboarding_preview_dismissed", + JSON.stringify({ dismissed: true }), + ); + apiFetch.mockResolvedValue({ ok: true, data: preview() }); + + render(); + await waitFor(() => + expect(window.localStorage.getItem("loopover_maintainer_onboarding_preview_dismissed")).toBe( + JSON.stringify({ dismissed: true }), + ), + ); + // The migrated legacy dismissal keeps the card hidden and skips the demo API call entirely. + expect(screen.queryByText(/Here's what LoopOver would have flagged/)).toBeNull(); + expect(apiFetch).not.toHaveBeenCalled(); + }); + it("dismisses the card and keeps it hidden (and skips the API call) across a remount", async () => { apiFetch.mockResolvedValue({ ok: true, data: preview() }); const { unmount } = render(); diff --git a/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.tsx b/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.tsx index 7e754f20e3..e02902ae67 100644 --- a/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.tsx +++ b/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.tsx @@ -18,8 +18,10 @@ import { useLocalStorage } from "@/lib/use-local-storage"; type ReviewabilityRow = { pr: string; title: string; reason: string }; const DISMISS_KEY = "loopover_maintainer_onboarding_preview_dismissed"; -// One-time rebrand migration fallback -- see useLocalStorage's legacyKey param. -const LEGACY_DISMISS_KEY = "loopover_maintainer_onboarding_preview_dismissed"; +// One-time rebrand migration fallback -- see useLocalStorage's legacyKey param. This must stay the OLD +// gittensory_-prefixed literal; #5743's blanket gittensory->loopover rename corrupted it to equal DISMISS_KEY, +// silently dropping a pre-rebrand dismissal (#7782). +const LEGACY_DISMISS_KEY = "gittensory_maintainer_onboarding_preview_dismissed"; /** Builds a settings-preview form from a REAL cached PR (title, and a linked-issue number scraped from * `reason` when present) — everything else (author identity, labels, body) isn't in the reviewability diff --git a/apps/loopover-ui/src/components/site/notification-readiness-card.test.tsx b/apps/loopover-ui/src/components/site/notification-readiness-card.test.tsx index c5a42c53a1..f971ace191 100644 --- a/apps/loopover-ui/src/components/site/notification-readiness-card.test.tsx +++ b/apps/loopover-ui/src/components/site/notification-readiness-card.test.tsx @@ -94,4 +94,27 @@ describe("NotificationReadinessCard loading/error states (#6985)", () => { expect(container.textContent).toContain("No content leaves the browser without consent."); expect(screen.queryByText("Loading notification model…")).toBeNull(); }); + + it("migrates a pre-rebrand opt-in stored under the legacy gittensory_ key (#7782)", () => { + // A maintainer who opted in before the rebrand has their preference under the OLD gittensory_-prefixed + // key. #5743's blanket rename had corrupted the legacyKey to equal the current key, so this value was + // silently dropped and the card defaulted back to "opt-in required". With the legacy key restored, the + // pre-rebrand opt-in is read forward and the pill shows "opt-in enabled". + window.localStorage.clear(); + window.localStorage.setItem("gittensory_notification_opt_in", JSON.stringify(true)); + useApiResource.mockReturnValue({ + status: "ready", + data: notificationModelFixture, + error: null, + loadedAt: Date.now(), + reload: () => {}, + }); + + render(); + + expect(screen.getByText("opt-in enabled")).toBeTruthy(); + // The legacy value was written forward to the current key (useLocalStorage's migration contract). + expect(window.localStorage.getItem("loopover_notification_opt_in")).toBe(JSON.stringify(true)); + window.localStorage.clear(); + }); }); diff --git a/apps/loopover-ui/src/components/site/notification-readiness-card.tsx b/apps/loopover-ui/src/components/site/notification-readiness-card.tsx index 36459045c2..552f9f5310 100644 --- a/apps/loopover-ui/src/components/site/notification-readiness-card.tsx +++ b/apps/loopover-ui/src/components/site/notification-readiness-card.tsx @@ -33,7 +33,9 @@ export function NotificationReadinessCard() { const [optIn, setOptIn] = useLocalStorage( "loopover_notification_opt_in", false, - "loopover_notification_opt_in", + // Legacy fallback must stay the OLD gittensory_-prefixed literal; #5743's blanket gittensory->loopover + // rename corrupted it to equal the current key, silently dropping a pre-rebrand opt-in (#7782). + "gittensory_notification_opt_in", ); const [busy, setBusy] = useState(false); diff --git a/scripts/branding-drift-baseline.json b/scripts/branding-drift-baseline.json index b161c01de0..704b782d0c 100644 --- a/scripts/branding-drift-baseline.json +++ b/scripts/branding-drift-baseline.json @@ -1,5 +1,7 @@ { "apps/loopover-ui/src/components/site/api/try-it.tsx": 1, + "apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.tsx": 2, + "apps/loopover-ui/src/components/site/notification-readiness-card.tsx": 2, "apps/loopover-ui/src/routes/app.index.tsx": 1, "apps/loopover-ui/src/routes/app.runs.tsx": 1, "apps/loopover-ui/src/routes/app.workbench.tsx": 1,