Skip to content

fix(notifications): read the tray with scope=all so read notifications stay in it - #89

Merged
Adron merged 1 commit into
devfrom
fix/notifications-tray-scope
Sep 17, 2026
Merged

Adron merged 1 commit into
devfrom
fix/notifications-tray-scope

Conversation

@Adron

@Adron Adron commented Sep 15, 2026

Copy link
Copy Markdown
Member

Summary

Closes #80.

scope=tray returns unread rows only and ignores limit, so the macOS tray emptied as you read it while the web bell retained recent history. A notification you glanced at simply vanished, with no way back to it.

The issue asked to probe before concluding anything. The probe found the cheap answer — candidate 1, not a backend ask.

The probe

?scope=tray            → 1 item   (the single unread one)
?scope=tray&limit=5    → 1 item   (limit ignored)
?scope=all&limit=3     → 3 items  (read and unread, newest first)
?scope=all&limit=10    → 10 items
?scope=all             → 20 items (the account's stored notificationTrayLimit)

unreadCount is returned under both scopes, so the badge is unaffected. Nothing folds into #58.

The change

  1. Notifications.tray(scope:limit:) now defaults to "all". The unread-only scope stays reachable for anything that genuinely wants it — it is just not what the tray asks for.

  2. The client-side prefix(limit) is deleted. It existed only because scope=tray ignored the parameter, and it made notificationTrayLimit appear to work while the rows it trimmed were unread-only — so the preference could only ever shrink an already-wrong list.

    It is deliberately not re-added as a belt-and-braces cap: a client-side trim over a correctly-limited response is invisible when it agrees with the server and wrong when it does not. There is a test pinning that choice.

  3. Two stale notes that described scope=tray ignoring limit as a standing constraint are corrected — NotificationsEndpoint.swift and the G35 correction block in NotificationsService.swift.

No workaround of the kind the issue ruled out. Nothing fetches a wider page and filters client-side; the server does the limiting. That is the failure mode PR #69 fixed for the DM inbox.

On PR #73's notificationTrayLimit

That preference was close to inert against a route that ignored limit. It now works because the server honours it, rather than because the client hides rows after the fact.

Verification

  • xcodebuild build → ** BUILD SUCCEEDED **
  • xcodebuild test (App) → Executed 968 tests, with 0 failures · ** TEST SUCCEEDED **
  • swift test InterlinedDomain → Executed 1014 tests, with 0 failures (was 1012)
  • swift test InterlinedKit → NotificationsEndpointTests: Executed 12 tests, with 0 failures (was 9)
  • swift test InterlinedPersistence → Executed 140 tests, with 0 failures

New tests, including the one that is the point of the whole change:

func test_givenAllRowsRead_whenLoadingTray_thenTheTrayStillShowsThem()
func test_givenTheServerOverAnswers_whenLoadingTray_thenRowsAreNotTrimmedClientSide()
func test_givenTheDefaultScope_whenBuildingTheTrayRequest_thenItIsReadInclusive()
func test_givenALimit_whenBuildingTheTrayRequest_thenItTravelsAlongsideTheScope()
func test_givenNoLimit_whenBuildingTheTrayRequest_thenTheParameterIsOmitted()

🤖 Generated with Claude Code

https://claude.ai/code/session_016gSWb3scYobtxLJioV1qF9

…s stay in it

`scope=tray` returns unread rows only and ignores `limit`, so the macOS tray
emptied as the user read it while the web bell retained recent history. Same
feature, different behaviour — and the macOS one was the surprising one: a
notification you glanced at vanished, with no way back to it from the tray.

The issue asked for a probe before concluding anything, and the probe found the
cheap answer. A read-inclusive scope already exists and honours the limit:

    ?scope=tray&limit=5  -> 1 item   (the single unread one)
    ?scope=all&limit=3   -> 3 items
    ?scope=all&limit=10  -> 10 items
    ?scope=all           -> 20 items (the account's notificationTrayLimit)

`unreadCount` is returned under both scopes, so the badge is untouched. No
backend ask is needed.

The client-side `prefix(limit)` goes with it. It existed only because the tray
scope ignored the parameter, and it made `notificationTrayLimit` appear to work
while the rows it trimmed were unread-only — so the preference could only ever
shrink an already-wrong list. It is deliberately not re-added as a belt-and-
braces cap: a client-side trim over a correctly-limited response is invisible
when it agrees with the server and wrong when it does not.

Nothing here fetches a wider page and filters locally, which is the workaround
the issue ruled out and the failure mode PR #69 fixed for the DM inbox.

Refs #80

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016gSWb3scYobtxLJioV1qF9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant