Add changelog feature to ACP menu#978
Conversation
WalkthroughA new "Recent Updates Dialog" feature is introduced that displays GitHub releases in a dialog component, fetches release data via a new API client and React Query hook, tracks unseen updates in local storage, and integrates into the application's navigation UI alongside existing controls. Changes
Sequence DiagramsequenceDiagram
actor User
participant Dialog as RecentUpdatesDialog
participant Query as useGitHubReleases
participant API as GitHub API
participant Storage as localStorage
User->>Dialog: Click trigger button
Dialog->>Storage: Read 'acp-last-seen-updates'
Dialog->>Query: Fetch releases (if not cached)
Query->>API: GET /repos/.../releases
API-->>Query: Release data
Query-->>Dialog: Return releases
Dialog->>Dialog: Compare latest release.published_at<br/>vs lastSeen timestamp
Dialog->>Storage: Write current ISO time<br/>to 'acp-last-seen-updates'
Dialog->>Dialog: Show notification badge<br/>if unseen releases exist
Dialog->>User: Render dialog with<br/>release list & details
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/frontend/src/components/__tests__/recent-updates-dialog.test.tsx`:
- Around line 61-78: Replace brittle DOM class selection in the
RecentUpdatesDialog tests with a stable test id: add a data-testid="unseen-dot"
to the notification span in the RecentUpdatesDialog component (the element that
currently has the red dot) and update the two tests in
recent-updates-dialog.test.tsx to use screen.queryByTestId('unseen-dot') (or
getByTestId when expecting presence) instead of
button.querySelector('.bg-red-500'); keep the same assertions (presence/absence)
and leave mockUseLocalStorage/mockSetLastSeen behavior unchanged.
In `@components/frontend/src/components/recent-updates-dialog.tsx`:
- Around line 27-30: The current hasUnseen check assumes releases[0] is the
newest; instead compute the latest release by published_at before comparing to
lastSeen: find the release with the max published_at (e.g., via
Array.prototype.reduce or sorting) from the releases array, then compare new
Date(latest.published_at) to new Date(lastSeen) to set hasUnseen (referencing
the hasUnseen variable, releases array, and lastSeen variable); update the logic
so it handles empty arrays and null/undefined published_at safely.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 451d7ee8-ab64-44db-95d4-43462f47d78f
📒 Files selected for processing (6)
components/frontend/src/app/projects/[name]/layout.tsxcomponents/frontend/src/components/__tests__/recent-updates-dialog.test.tsxcomponents/frontend/src/components/navigation.tsxcomponents/frontend/src/components/recent-updates-dialog.tsxcomponents/frontend/src/services/api/github-releases.tscomponents/frontend/src/services/queries/use-github-releases.ts
| it('shows red dot when unseen releases exist', () => { | ||
| render(<RecentUpdatesDialog />); | ||
| const button = screen.getByRole('button', { name: /recent updates/i }); | ||
| const dot = button.querySelector('.bg-red-500'); | ||
| expect(dot).not.toBeNull(); | ||
| }); | ||
|
|
||
| it('hides red dot when all releases are seen', () => { | ||
| mockUseLocalStorage.mockReturnValue([ | ||
| '2026-03-19T00:00:00Z', | ||
| mockSetLastSeen, | ||
| vi.fn(), | ||
| ]); | ||
| render(<RecentUpdatesDialog />); | ||
| const button = screen.getByRole('button', { name: /recent updates/i }); | ||
| const dot = button.querySelector('.bg-red-500'); | ||
| expect(dot).toBeNull(); | ||
| }); |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Tests look good; consider using a data-testid for the notification dot.
Using .querySelector('.bg-red-500') is brittle—if the Tailwind class changes, these tests will break silently. Consider adding a data-testid="unseen-dot" attribute to the notification span for more resilient testing.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/frontend/src/components/__tests__/recent-updates-dialog.test.tsx`
around lines 61 - 78, Replace brittle DOM class selection in the
RecentUpdatesDialog tests with a stable test id: add a data-testid="unseen-dot"
to the notification span in the RecentUpdatesDialog component (the element that
currently has the red dot) and update the two tests in
recent-updates-dialog.test.tsx to use screen.queryByTestId('unseen-dot') (or
getByTestId when expecting presence) instead of
button.querySelector('.bg-red-500'); keep the same assertions (presence/absence)
and leave mockUseLocalStorage/mockSetLastSeen behavior unchanged.
| const hasUnseen = | ||
| releases && | ||
| releases.length > 0 && | ||
| (!lastSeen || new Date(releases[0].published_at) > new Date(lastSeen)); |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Minor: Assumption about release ordering.
The hasUnseen logic assumes releases[0] is the most recent release. GitHub's API sorts releases by created_at (not published_at) by default. These timestamps are usually identical, but could differ if a release is created as a draft and published later.
This is low-risk since it only affects edge cases, but worth noting if the notification dot behavior seems incorrect for some releases.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/frontend/src/components/recent-updates-dialog.tsx` around lines 27
- 30, The current hasUnseen check assumes releases[0] is the newest; instead
compute the latest release by published_at before comparing to lastSeen: find
the release with the max published_at (e.g., via Array.prototype.reduce or
sorting) from the releases array, then compare new Date(latest.published_at) to
new Date(lastSeen) to set hasUnseen (referencing the hasUnseen variable,
releases array, and lastSeen variable); update the logic so it handles empty
arrays and null/undefined published_at safely.
As per ticket, changelog button with modal and a gift box icon. Demo: <img width="429" height="70" alt="image" src="https://github.com/user-attachments/assets/50a575c8-46b8-44f3-a647-b08a8458f253" /> <img width="827" height="1352" alt="image" src="https://github.com/user-attachments/assets/8a167d24-7144-4696-acf1-cd44f51794e5" /> Changelog comes from Github release changelog - and in future can be reconsidered.
As per ticket, changelog button with modal and a gift box icon.
Demo:
Changelog comes from Github release changelog - and in future can be reconsidered.