Skip to content

bug(notifications): scope=tray returns unread-only, where the web bell shows the last N #80

Description

@Adron

Found during PR #73 (G35), outside that issue's scope. Recorded during the 2026-09-13 tracker reconciliation.

The defect

GET /api/notifications?scope=tray returns unread notifications only. The web bell shows the last N regardless of read state.

So on macOS the tray empties as you read it. On the web it retains recent history. Same feature, different behaviour — and the macOS one is the surprising one: a notification you glance at vanishes, with no way to get back to it from the tray.

Already documented at the endpoint:

  • Packages/InterlinedKit/Sources/InterlinedKit/Endpoints/NotificationsEndpoint.swift:27 — "with scope=tray the server currently ignores limit and returns unread…"

So the client knows; nothing acts on it.

Compounding: the tray limit is now user-configurable

PR #73 shipped notificationTrayLimit in View Preferences — the user can now set how many tray items they want. Against a route that ignores limit and filters to unread, that preference is currently close to inert. Shipping a control the server does not honour is its own small defect.

Two candidate fixes — probe first

  1. A different scope or parameter already does this. Probe GET /api/notifications with other scope values and with limit under each before concluding anything. If a read-inclusive scope exists, this is a one-line change.
  2. No such scope exists → backend ask: the web bell shows the last N regardless of read state; which parameter produces that, and is limit honoured under it? Folds into blocked: backend asks - following feed, session-only routes, article_series 502, shape confirmations #58.

Do not work around it by fetching a wider page and filtering client-side. That is exactly the failure mode PR #69 fixed for the DM inbox — client-side reconstruction is only ever as complete as the page fetched.

Acceptance

  • The tray matches the web bell: last N, read and unread.
  • notificationTrayLimit visibly changes what the tray shows.
  • If the route cannot do it, the backend ask is filed and the preference is honestly labelled until then.

Implementation plan (added 2026-09-15) — probe done, it is candidate 1

The issue said "probe first" before concluding anything. Done, and the answer is the cheap one: a read-inclusive scope already exists and honours limit. Live, on the test account:

?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 comes back under both scopes, so the badge is unaffected.

No backend ask is needed, and nothing folds into #58.

The work

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

  2. Delete the client-side prefix(limit) in NotificationsService.tray(limit:). 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. Not re-added as a belt-and-braces cap either: a client-side trim over a correctly-limited response is invisible when it agrees with the server and wrong when it does not.

  3. Correct the two stale notes that describe scope=tray ignoring limit as a standing constraint (NotificationsEndpoint.swift:27, and the G35 correction block in NotificationsService.swift).

No workaround of the kind the issue warned against: nothing fetches a wider page and filters client-side. The server does the limiting.

Tests

  • The endpoint sends scope=all by default and still accepts scope: "tray" explicitly.
  • limit travels alongside the scope; absent limit omits the parameter so the server applies the stored preference.
  • A fully-read account still sees its tray — the behaviour this whole issue is about.
  • The server's page is rendered as-is rather than second-guessed.
  • unreadCount stays server-authoritative regardless of row count.

Acceptance

  • ✅ The tray matches the web bell: last N, read and unread.
  • ✅ notificationTrayLimit visibly changes what the tray shows — now because the server honours it.
  • n/a — no backend ask needed.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingparityWeb-parity gap with the InterlinedList web app

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions