backstage / backstage/community-plugins
🚀 Announcements: Per-announcement dismiss tracking for multiple banners
- Dominant language
- TypeScript
- Stars
- 422
- Forks
- 697
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 286
Description
### Workspace
announcements
### 🔖 Feature description
The current banner dismiss mechanism uses a single lastSeenDate timestamp stored in localStorage. This makes it impossible to reliably display and independently dismiss multiple announcement banners. Dismissing any announcement updates the timestamp to that announcement's created_at, which inadvertently hides all older announcements — even if the user never saw them.
The feature request is to replace the single-timestamp approach with per-announcement dismiss tracking (e.g., a Set of dismissed announcement IDs), so that each banner can be dismissed independently without affecting the visibility of others.
### 🎤 Context
When using NewAnnouncementBanner with max > 1, the component fetches multiple announcements and renders a banner for each unseen one. However, the dismiss behavior is broken:
1. User sees 3 unseen banners (announcements A, B, C ordered oldest to newest)
2. User dismisses C (the newest) → markLastSeenDate is set to C's created_at
3. A and B immediately disappear because their created_at < lastSeenDate
Additionally, since lastSeenDate is stored in browser localStorage, the "seen" state is lost entirely in incognito mode, new devices, or when clearing browser data — causing all banners to reappear.
This makes the max prop on NewAnnouncementBanner effectively useless for showing multiple banners with independent dismiss behavior.
### ✌️ Possible Implementation
Replace the single timestamp with a Set of dismissed announcement IDs in WebStorage:
1. AnnouncementsApi interface — Add dismissAnnouncement(id: string): void and isAnnouncementDismissed(id: string): boolean. Deprecate markLastSeenDate / lastSeenDate (keep for backward compat).
2. AnnouncementsClient — Store a dismissed_announcement_ids array in WebStorage. Cap at a reasonable size (e.g., last 50 IDs) to prevent localStorage bloat.
3. NewAnnouncementBanner — Filter by !isAnnouncementDismissed(id) instead of lastSeen < created_at.
4. AnnouncementsCard — Use the same logic for the "new" megaphone icon.
5. Migration — On first load, if user_last_seen_date exists, convert it: mark all announcements with created_at <= lastSeenDate as dismissed, then remove the old key.
### 👀 Have you spent some time to check if this feature request has been raised before?
- [x] I checked and didn't find similar issue
### 🏢 Have you read the Code of Conduct?
- [x] I have read the [Code of Conduct](https://github.com/backstage/community-plugins/blob/main/CODE_OF_CONDUCT.md)
### Are you willing to submit PR?
Yes I am willing to submit a PR!
Contributor guide
Assessment
This issue has not been assessed yet.