forum: post score + highlight missing after page refresh on both list and detail views #65

Open
opened 2026-05-23 13:08:34 +00:00 by padreug · 0 comments
Owner

Repro

  1. On either the forum list (ForumListPage) or a single-post detail (SubmissionDetailPage), have a post visible whose score is non-zero (and where you'd see your own up/down highlighted, if applicable).
  2. Reload the browser.
  3. Observed: score does not render and your vote arrow is not highlighted on the visible post(s) — votes appear as 0 / no userVote.
  4. Navigate from list into a post, or from a post back out to the list.
  5. The score and highlight reappear on whichever view you land on.

What's going on (best read of the code)

Both views render SubmissionWithMeta.votes.{score, userVote}. The votes field is computed by SubmissionService.enrichSubmission() at the moment a kind-1111 event is parsed, then re-computed by refreshAllSubmissionVotes() (list path) or refreshSubmissionVotes(id) (detail path) on EOSE.

Two plausible contributors, in order of suspected impact:

1. List subscription's reaction filter is too broad.

buildFilters() in src/modules/forum/services/SubmissionService.ts ships three filters; the reactions one is:

filters.push({
  kinds: [SUBMISSION_KINDS.REACTION],
  limit: 1000
})

— no #e scoping. On any relay with meaningful kind-7 volume, the 1000-event slice it returns may not contain the user's reactions to the posts currently in view. EOSE then fires, refreshAllSubmissionVotes runs against a ReactionService that simply doesn't have the relevant reactions, and votes stay at zero. When you open the detail page, subscribeToSubmission(id) requests { kinds: 7, '#e': [submissionId] } which IS correctly scoped — those reactions land in ReactionService, refreshSubmissionVotes(id) reassigns submission.votes, and Vue propagates the change to the row that's still mounted on the list (same reactive object in _submissions). That's the "navigating in/out fixes it" effect.

Fix: scope the list reactions filter to the submission IDs we actually care about — fetch submissions in a first pass, then open a follow-up reactions subscription with '#e': [...submission.ids] (this is essentially what ReactionService.subscribeToReactions() is designed for at src/modules/base/nostr/ReactionService.ts:84).

2. Auth-pubkey race on the userVote highlight.

Even when reactions DO arrive, ReactionService.recalculateEventReactions() (src/modules/base/nostr/ReactionService.ts:237) computes userHasLiked / userHasDisliked by comparing reaction.pubkey === authService.user.value?.pubkey (lines 271, 277). On refresh, the LNbits-backed auth state hydrates async; reactions that arrive before that hydration completes get counted in the score but never flagged as the user's own, so the arrow stays unhighlighted even when the score is right.

Fix: in ReactionService, watch authService.user.value?.pubkey; on a falsy → truthy transition, re-run recalculateEventReactions across all known event IDs.

Notes

  • Bug pre-dates the nostr-tools 2.23 bump (PR #63). The bumped code path doesn't touch any of this.
  • (2) likely affects nostr-feed / chat / activities reactions too — ReactionService is shared. Worth checking after the forum fix.
## Repro 1. On either the forum list (ForumListPage) or a single-post detail (SubmissionDetailPage), have a post visible whose score is non-zero (and where you'd see your own up/down highlighted, if applicable). 2. Reload the browser. 3. Observed: score does not render and your vote arrow is not highlighted on the visible post(s) — votes appear as 0 / no userVote. 4. Navigate from list into a post, or from a post back out to the list. 5. The score and highlight reappear on whichever view you land on. ## What's going on (best read of the code) Both views render `SubmissionWithMeta.votes.{score, userVote}`. The `votes` field is computed by `SubmissionService.enrichSubmission()` at the moment a kind-1111 event is parsed, then re-computed by `refreshAllSubmissionVotes()` (list path) or `refreshSubmissionVotes(id)` (detail path) on EOSE. Two plausible contributors, in order of suspected impact: **1. List subscription's reaction filter is too broad.** `buildFilters()` in `src/modules/forum/services/SubmissionService.ts` ships three filters; the reactions one is: ```ts filters.push({ kinds: [SUBMISSION_KINDS.REACTION], limit: 1000 }) ``` — no `#e` scoping. On any relay with meaningful kind-7 volume, the 1000-event slice it returns may not contain the user's reactions to the posts currently in view. EOSE then fires, `refreshAllSubmissionVotes` runs against a ReactionService that simply doesn't have the relevant reactions, and votes stay at zero. When you open the detail page, `subscribeToSubmission(id)` requests `{ kinds: 7, '#e': [submissionId] }` which IS correctly scoped — those reactions land in ReactionService, `refreshSubmissionVotes(id)` reassigns `submission.votes`, and Vue propagates the change to the row that's still mounted on the list (same reactive object in `_submissions`). That's the "navigating in/out fixes it" effect. Fix: scope the list reactions filter to the submission IDs we actually care about — fetch submissions in a first pass, then open a follow-up reactions subscription with `'#e': [...submission.ids]` (this is essentially what `ReactionService.subscribeToReactions()` is designed for at `src/modules/base/nostr/ReactionService.ts:84`). **2. Auth-pubkey race on the userVote highlight.** Even when reactions DO arrive, `ReactionService.recalculateEventReactions()` (`src/modules/base/nostr/ReactionService.ts:237`) computes `userHasLiked` / `userHasDisliked` by comparing `reaction.pubkey === authService.user.value?.pubkey` (lines 271, 277). On refresh, the LNbits-backed auth state hydrates async; reactions that arrive before that hydration completes get counted in the score but never flagged as the user's own, so the arrow stays unhighlighted even when the score is right. Fix: in `ReactionService`, watch `authService.user.value?.pubkey`; on a falsy → truthy transition, re-run `recalculateEventReactions` across all known event IDs. ## Notes - Bug pre-dates the nostr-tools 2.23 bump (PR #63). The bumped code path doesn't touch any of this. - (2) likely affects nostr-feed / chat / activities reactions too — ReactionService is shared. Worth checking after the forum fix.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aiolabs/webapp#65
No description provided.