feat(notices): host-driven notice banners on the dashboard home - #2667
Open
camielvs wants to merge 3 commits into
Open
feat(notices): host-driven notice banners on the dashboard home#2667camielvs wants to merge 3 commits into
camielvs wants to merge 3 commits into
Conversation
This was referenced Aug 26, 2026
Collaborator
Author
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
This was referenced Aug 26, 2026
🎩 PreviewA preview build has been created at: |
camielvs
commented
Aug 26, 2026
camielvs
commented
Aug 26, 2026
camielvs
commented
Aug 26, 2026
camielvs
commented
Aug 26, 2026
camielvs
commented
Aug 26, 2026
camielvs
commented
Aug 26, 2026
camielvs
force-pushed
the
banners-03-contract
branch
from
August 26, 2026 23:45
a100783 to
eb0c6de
Compare
camielvs
force-pushed
the
banners-04-strip
branch
2 times, most recently
from
August 27, 2026 00:44
91b5f62 to
45113d5
Compare
Renders notices from the host contract as a strip directly under the top nav, so they are visible on every page rather than only on the dashboard home. The strip scrolls horizontally when there are more notices than fit, sorts them most-severe-first, and publishes its own height so full-height layouts stay inside the viewport. "Hide notices" clears the strip. A notice the host marked dismissible stays hidden across reloads; one it did not comes back on the next load, since the host is still asking for it to be shown. Replaces the old dashboard-only AnnouncementBanners, which read a plain array off the window and is deleted here along with its config module. Note for reviewers: the store lands complete, but its read-tracking and per-notice dismiss are unused until the header UI in the next PR.
Every notice in the strip can now be taken off it, whatever the host said about dismissibility, because the inbox keeps it reachable. Hiding is persisted for a dismissible notice and session-only for a mandatory one, the same split hideStrip already used in bulk. The strip's control is an eye-off "Hide notice", distinct from the inbox's destructive "Dismiss", so the two actions do not look alike. isStripHidden becomes hasHiddenBanners, since a partly-hidden strip must still offer to bring the rest back. Register both new hooks in react-compiler.config.js — src/hooks is still opt-out, so a new top-level hook file gets no coverage by default. The compiler also takes care of the identity of the object useBannerInbox returns, so a consumer can safely put it in a dep array. dismissBanner now enforces the same rule hideStrip does: only a notice the host marked dismissible is retired permanently, everything else is session-only. handleStorage ignores the synthetic same-tab event typedStorage dispatches on every write, so an action publishes once instead of twice. A real cross-tab event still refreshes, which is now pinned by a test. isOpen moves into the cached snapshot rather than being read off module state during render, and the tab-focus refresh is throttled. BannerCard replaces bodyClassName with a clampBody flag, so the card owns the classes and the strip only states its intent. The body keeps scrolling inside the card with the action pinned below it. resetBannerStateForTests clears the snapshot cache, the session sets, the open flag and the refresh watcher, so the suites stop reserving ids to avoid each other. With banners.ts fully wired, its knip ignore comes off. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Follows the contract rename below it. Notices are the domain concept; the banner row is one surface they appear on, so the store exposes both: `notices` is everything the reader still has, `banners` the subset showing on the page. Storage keys move with it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
camielvs
force-pushed
the
banners-03-contract
branch
from
August 27, 2026 01:18
d9ad21b to
4fb20f2
Compare
camielvs
force-pushed
the
banners-04-strip
branch
from
August 27, 2026 01:18
45113d5 to
69c580a
Compare
camielvs
marked this pull request as ready for review
August 28, 2026 00:02
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Why
The dashboard banners this replaces were fed by a hardcoded config module, showed on one page, and had no way to look back at anything you'd dismissed. They should be host-driven — fed by the contract from PR 3 — and they should keep the placement people already know.
What
Notice banners inline at the top of the dashboard home, in the slot the old
AnnouncementBannersoccupied.Deletes the old
AnnouncementBannersand its config module.The old system read a plain array from
window.__TANGLE_ANNOUNCEMENTS__. The new one reads a versioned source from a different key. Any embedding page still injecting the old shape goes dark the moment this merges. Either land this after the host ships the new source, or say the word and I'll add a fallback that reads the old array for one release.Reviewer notes
Per-notice hiding is the only inline control; clearing them all, and reading one back after you've hidden it, belong to the notices button in PR 5 — which is also the only notice surface on v2 pages, since those have no home page.
The store lands complete here, but its read-tracking and per-notice dismiss have no caller until PR 5 — they're covered by that PR's tests, not this one. I kept it whole because splitting it in half made the banners' own tests depend on an artificial reset hook, which seemed a worse trade than a little unused surface for one PR.