Raised from the client side while fixing a badge bug (tdn-client#99). That PR stops the count from being corrupted, but it cannot make it correct — the API does not expose enough for that.
Where the count comes from today
There is no unread-count endpoint, so the client derives one:
GET /notifications?page=1&limit=20 on boot → count the entries with isRead: false
+1 for each realtime frame on /realtime/ws
0 after mark-all-read
Two consequences
1. More than 20 unread undercounts. The badge only ever sees the first page, so a user with 35 unread sees at most 20. Paginating cannot fix it: the client now deliberately ignores appended pages, because recounting across them was destroying the realtime increments (that was the bug in the linked PR).
2. Realtime notifications cannot enter the list. The socket frame is:
{ type, issuerId, postId? }
but rendering a notification needs username, avatarUrl, createdAt, isRead, referenceId, recipientId. So an arriving notification can only move a counter — it cannot be shown until the list is refetched. The client has an unused addNotification action sitting there waiting for a payload it can actually use.
Either of these would resolve it
Option A — an unread-count endpoint. GET /notifications/unread-count → { count: number }. Smallest change. Given that notifications are only ever marked read in bulk, this should be a single count of rows newer than the user's last mark-all-read timestamp. The client would call it on boot and after mark-all-read, and stop deriving the number from a page of results.
Option B — a full notification in the realtime frame. Send the same shape GET /notifications returns. The client can then insert it into the list and derive the count honestly, and the badge stays right without polling. More useful long term, since it also makes new notifications appear live instead of only after a refetch.
A gets the badge correct; B gets the badge correct and makes the feed live. If only one is worth doing, A is the cheaper fix and B is the better one.
Not urgent
The client-side corruption is fixed, so the badge is stable and correct for the common case (≤20 unread). This is about the remaining inaccuracy — no rush, just recording it so it is not rediscovered later.
Context: notifications currently support bulk mark-as-read only, which was a deliberate scope decision — this issue assumes that stays.
Raised from the client side while fixing a badge bug (tdn-client#99). That PR stops the count from being corrupted, but it cannot make it correct — the API does not expose enough for that.
Where the count comes from today
There is no unread-count endpoint, so the client derives one:
GET /notifications?page=1&limit=20on boot → count the entries withisRead: false+1for each realtime frame on/realtime/ws0after mark-all-readTwo consequences
1. More than 20 unread undercounts. The badge only ever sees the first page, so a user with 35 unread sees at most 20. Paginating cannot fix it: the client now deliberately ignores appended pages, because recounting across them was destroying the realtime increments (that was the bug in the linked PR).
2. Realtime notifications cannot enter the list. The socket frame is:
but rendering a notification needs
username,avatarUrl,createdAt,isRead,referenceId,recipientId. So an arriving notification can only move a counter — it cannot be shown until the list is refetched. The client has an unusedaddNotificationaction sitting there waiting for a payload it can actually use.Either of these would resolve it
Option A — an unread-count endpoint.
GET /notifications/unread-count→{ count: number }. Smallest change. Given that notifications are only ever marked read in bulk, this should be a single count of rows newer than the user's last mark-all-read timestamp. The client would call it on boot and after mark-all-read, and stop deriving the number from a page of results.Option B — a full notification in the realtime frame. Send the same shape
GET /notificationsreturns. The client can then insert it into the list and derive the count honestly, and the badge stays right without polling. More useful long term, since it also makes new notifications appear live instead of only after a refetch.A gets the badge correct; B gets the badge correct and makes the feed live. If only one is worth doing, A is the cheaper fix and B is the better one.
Not urgent
The client-side corruption is fixed, so the badge is stable and correct for the common case (≤20 unread). This is about the remaining inaccuracy — no rush, just recording it so it is not rediscovered later.
Context: notifications currently support bulk mark-as-read only, which was a deliberate scope decision — this issue assumes that stays.