Skip to content

feat(lemmyv1): surface read_comments_at on PostView#43

Merged
aeharding merged 1 commit into
mainfrom
feat/post-read-comments-at
May 31, 2026
Merged

feat(lemmyv1): surface read_comments_at on PostView#43
aeharding merged 1 commit into
mainfrom
feat/post-read-comments-at

Conversation

@aeharding
Copy link
Copy Markdown
Owner

What

Surface read_comments_at as an optional field on PostView.

Lemmy v1 records the timestamp of the last time the current user read a post's comments in post_actions.read_comments_at. Until now the compat layer only consumed it inside toUnreadComments (to derive unread_comments) and discarded the raw value.

// schemas/PostView.ts
read_comments_at: z.optional(z.string()),
// providers/lemmyv1/compat.ts (toPostView)
read_comments_at: nullToUndef(v.post_actions?.read_comments_at),

Why

It unlocks two client features (e.g. Voyager) that the count alone can't:

  • Distinguish never-opened posts from opened-with-new-comments. toUnreadComments returns the total comment count when read_comments_at is unset, so unread_comments > 0 alone can't tell "5 brand-new comments since I last read" from "5 comments, never opened". The raw timestamp lets clients gate the "N new" badge on read_comments_at != null.
  • Highlight individual unread comments — comments whose published_at is newer than read_comments_at.

Provider coverage

  • Lemmy v1 — populated from post_actions.read_comments_at.
  • Lemmy v0 / PieFed — left undefined; neither exposes an equivalent read-comments timestamp.

Notes

  • Normalized via nullToUndef: v1 sends JSON null (not an omitted key) for unset fields on an existing post_actions row, and the schema is z.optional(z.string()) (no null). This matches the existing read_at / vote / notification-id handling in the same file — toUnreadComments already guards read_comments_at == null for the same reason.

Verification

  • pnpm test (lint + types + vitest): 25 passed, including 2 new lemmyv1-compat cases — surfaces the timestamp, and maps null/absent → undefined.
  • Confirmed against a live 1.0.0-nightly instance: get_post returns the pre-update read_comments_at (the boundary) and /post/list carries post_actions.read_comments_at per post.

Lemmy v1 records the timestamp of the last time the current user read a
post's comments in post_actions.read_comments_at. Until now compat only
consumed it (via toUnreadComments) to derive unread_comments and threw the
raw value away.

Surface it as an optional PostView.read_comments_at so clients can
distinguish never-opened posts (undefined) from opened-with-new-comments,
and highlight individual comments published after the boundary. v0/PieFed
leave it undefined (no equivalent timestamp).

Normalized through nullToUndef since v1 sends JSON null (not omitted) for
unset fields on an existing post_actions row, matching the existing
read_at/vote handling.
@aeharding aeharding merged commit 825d257 into main May 31, 2026
1 check passed
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