Skip to content

fix(sidebar): stop loading the activity list twice and applying stale… - #2837

Open
karlitschek wants to merge 2 commits into
masterfrom
fix/sidebar-tab-duplicate-and-stale-requests
Open

fix(sidebar): stop loading the activity list twice and applying stale…#2837
karlitschek wants to merge 2 commits into
masterfrom
fix/sidebar-tab-duplicate-and-stale-requests

Conversation

@karlitschek

@karlitschek karlitschek commented Aug 1, 2026

Copy link
Copy Markdown
Member

… responses

The activity panel had two independent triggers for the initial load: a node watcher declared immediate and a mounted() hook doing the same thing. Every time the sidebar opened, both fired, so the panel issued two identical requests for the same file. Removing the hook is enough; the immediate watcher already covers the first load as well as later changes.

The requests were also unabortable, and getActivities() assigned to this.activities unconditionally. Moving between files faster than the server answered therefore applied responses in arrival order rather than request order, and the panel could end up showing one file's activity while another was selected. Worse, once a request failed or was superseded the panel surfaced "Unable to load the activity list" even though the newer request was still on its way.

Each load now supersedes the one before it through an AbortController, passes the signal to axios, and checks the signal before touching any component state, so a superseded request can neither overwrite the activities nor raise an error banner. Unmounting aborts the last request too. The abort is detected via the signal rather than the error type, which keeps it independent of how axios reports cancellation.

The same fix already exists in the stream view; only the sidebar was missing it.

🤖 AI (if applicable)

  • The content of this PR was partly or fully generated using AI

… responses

The activity panel had two independent triggers for the initial load: a
`node` watcher declared `immediate` and a `mounted()` hook doing the same
thing. Every time the sidebar opened, both fired, so the panel issued two
identical requests for the same file. Removing the hook is enough; the
immediate watcher already covers the first load as well as later changes.

The requests were also unabortable, and `getActivities()` assigned to
`this.activities` unconditionally. Moving between files faster than the
server answered therefore applied responses in arrival order rather than
request order, and the panel could end up showing one file's activity
while another was selected. Worse, once a request failed or was
superseded the panel surfaced "Unable to load the activity list" even
though the newer request was still on its way.

Each load now supersedes the one before it through an AbortController,
passes the signal to axios, and checks the signal before touching any
component state, so a superseded request can neither overwrite the
activities nor raise an error banner. Unmounting aborts the last request
too. The abort is detected via the signal rather than the error type,
which keeps it independent of how axios reports cancellation.

The same fix already exists in the stream view; only the sidebar was
missing it.

Signed-off-by: Frank Karlitschek <karlitschek@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@karlitschek
karlitschek requested review from artonge and susnux August 1, 2026 15:38
@codecov

codecov Bot commented Aug 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

The panel sent no `limit` and no `since`, so it received the API default of
50 activities and had no way to ask for the rest. For any file with a
longer history the list simply ended, with nothing indicating that it had
been cut short.

The page size is now requested explicitly rather than inherited from the
API default, and a "Load older activities" action at the end of the list
fetches the next page and appends it.

Whether more activities remain is taken from the `Link: rel="next"` header
rather than guessed from the size of the page. The server builds that
header only when its `has_more` probe found an extra row, so it is exact,
whereas a full page is not evidence that another one exists. The cursor
comes from `X-Activity-Last-Given`, as in the stream view. A 304 on a
later page is treated as the end of the list, not as a failure.

Activities contributed by other apps through `registerSidebarEntries()`
describe the node rather than a page, so they are collected once per file
and re-merged into each page instead of being requested again per page.
Selecting another file drops the cursor along with the rest of the state,
since carrying it over would skip the newest activities of the new file.

The axios test mock now returns response headers alongside the body, which
is what the real client does and what pagination reads.

Signed-off-by: Frank Karlitschek <karlitschek@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants