Skip to content

fix(timeline): a successful like reported a count of zero - #809

Merged
github-actions[bot] merged 1 commit into
mainfrom
fix/reaction-count
Aug 28, 2026
Merged

fix(timeline): a successful like reported a count of zero#809
github-actions[bot] merged 1 commit into
mainfrom
fix/reaction-count

Conversation

@catomean

Copy link
Copy Markdown
Collaborator

The four reaction RPCs are RETURNS TABLE(<name>_count integer), and PostgREST renders a set-returning function as an array of rows — [{ like_count: 1 }]. The client indexed that array as though it were the row, so the lookup was always undefined and the || 0 fallback turned every successful reaction into zero.

Confirmed in production immediately after #806 repaired the RPCs themselves: the like row is written, timeline_event_stats.like_count reads 1, and the button still renders a blank count.

It hid well. The filled heart comes from a boolean the client sets itself, so the action looks right while the number beside it stays empty — and for the eight months the RPC was also raising 42703 there was never a successful response to notice it in. The previous row in timeline_likes before today is dated December 2025.

readCount accepts either shape, so a future rewrite to a scalar cannot silently zero it again.

Proven by mutation: restoring the old read fails the array-shape test, and only that one.

🤖 Generated with Claude Code

https://claude.ai/code/session_012dpTLxh5GJWeWTF1UEvcD5

The four reaction RPCs are `RETURNS TABLE(<name>_count integer)`, and
PostgREST renders a set-returning function as an ARRAY of rows —
`[{ like_count: 1 }]`. The client indexed that array as though it were
the row, so the lookup was always undefined and the `|| 0` fallback
turned every successful reaction into zero.

Confirmed in production after the RPCs themselves were repaired: the
like row is written, timeline_event_stats.like_count reads 1, and the
button still renders a blank count.

It hid well. The filled heart comes from a boolean the client sets
itself, so the action looked right while the number beside it stayed
empty — and for the eight months the RPC was also raising 42703 there
was never a successful response to notice it in. The previous row in
timeline_likes before today is dated December 2025.

readCount takes either shape, so a future rewrite to a scalar cannot
silently zero it again. Proven by mutation: restoring the old read fails
the array-shape test and only that one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012dpTLxh5GJWeWTF1UEvcD5
@github-actions
github-actions Bot merged commit e21b569 into main Aug 28, 2026
6 checks passed
@github-actions
github-actions Bot deleted the fix/reaction-count branch August 28, 2026 09:54
github-actions Bot pushed a commit that referenced this pull request Aug 28, 2026
…811)

* fix(timeline): a like was stored, then read back as zero everywhere

The write path was repaired in #806 and #809 and the like still did not
appear. It was in timeline_likes, timeline_event_stats.like_count read
1, and the post page rendered an empty, unpressed heart. Nothing on the
read side had ever populated these fields — in three separate places,
all wrong in the same direction:

  eventQueries hardcoded `likesCount: 0, userLiked: false` under a
  comment saying the UI enriched them later. Nothing did.

  userFeeds called enrichEventsForDisplay and then OVERWROTE its output
  with `event.like_count` / `event.user_liked` — columns timeline_events
  has never had — so each value was replaced by `undefined || 0` on its
  way to the screen.

  transformEnrichedEventToDisplay, used by the followed-users feed and
  by search, set no reaction fields at all. It is synchronous and the
  enriched view carries no reaction columns, so it could not have.

Three paths, one direction, which is why the counter never moved no
matter which surface you looked at.

Reaction state is now resolved by one function. enrichEventsForDisplay
calls it for every path that goes through enrichment; the three that
cannot — they build rows from an RPC or from the view — call
attachReactionState after mapping. Making this each caller's
responsibility is precisely what produced three callers that all got it
wrong.

Counts come from timeline_event_stats, which the reaction RPCs maintain
and which is the only place they live. "Did I react" cannot come from
there — it is per-reader — so it reads the membership tables. Batched:
three queries per page regardless of post count, alongside the existing
profile/project batching. Per-post lookups would be 60 round-trips for a
20-post feed.

A failure degrades to zeros rather than failing the feed. A post without
its counters is a small loss; a blank timeline is a total one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012dpTLxh5GJWeWTF1UEvcD5

* fix(timeline): deleting the post you are looking at left it on screen

The post page never passed onDelete, so PostCard called a callback
nobody had supplied. The delete itself succeeded — verified in
production: is_deleted true, deleted_at set — and the page went on
rendering the post until a reload replaced it with "This post doesn't
exist". Deleting a reply had the same gap.

The main post now navigates to the timeline, with replace rather than
push so Back cannot return to a page with nothing to show. A deleted
reply is removed from the thread at whatever depth it sat, and its
parent's reply count follows it down.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012dpTLxh5GJWeWTF1UEvcD5

---------

Co-authored-by: Georgy Butaev <41178744+g-but@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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