Add comment threading flow documentation

This commit is contained in:
thePR0M3TH3AN committed 2026-01-07 20:08:20 -05:00
1 parent cc130ae513
commit 370f23feb6
1 file changed
+116
+116
View File
@@ -0,0 +1,116 @@
# Comment threads & discussion counts (Nostr → UI flow)
This note documents how bitvid turns Nostr comment events into UI comment threads and discussion counts. The primary implementations live in:
- `js/services/commentThreadService.js` (comment thread hydration + caching)
- `js/services/discussionCountService.js` (NIP-45 count queries + DOM updates)
## Comment thread flow (`CommentThreadService`)
### Data sources
`CommentThreadService` is the single place that fetches and subscribes to comment events for a video. It relies on the injected `nostrClient` APIs:
- `fetchVideoComments(target, options)` for the initial thread load.
- `subscribeVideoComments(target, options)` to keep the thread up to date in real time.
The service builds a `target` payload using the current video metadata:
- `videoEventId` (event id)
- `videoDefinitionAddress` from `buildVideoAddressPointer(video)`
- `videoKind`
- `videoAuthorPubkey`
- `rootIdentifier` / `rootIdentifierRelay` (derived from `video.videoRootId`, pointer identifiers, or tag data)
- optional `parentCommentId` context when fetching a reply thread
Internally, it also infers parentage from the incoming comment events by looking at `e` tags; the most recent `e` tag that is **not** the video event id becomes the parent comment id for threading. It also watches for the `i` tag to update root identifier and relay details.
### Caching
When `FEATURE_IMPROVED_COMMENT_FETCHING` is enabled, comment events are cached in `localStorage`:
- Key format: `bitvid:comments:<videoEventId>`
- TTL: 5 minutes
- Versioned schema: `COMMENT_CACHE_VERSION = 2`
Cache usage is simple: load from cache during `loadThread`, and write the latest event list after thread updates. Cache diagnostics (`commentCacheDiagnostics.storageUnavailable`) are surfaced to consumers to indicate storage issues.
### Polling & refresh
There is **no polling loop** for comments. Refresh happens through:
1. A one-time fetch via `fetchVideoComments` during `loadThread`.
2. A live subscription via `subscribeVideoComments`, which streams new comments into the thread.
Profile hydration for comment authors is batched and debounced (default 25ms), with up to three retry attempts and a backoff delay between attempts.
### UI surfacing
`VideoModalCommentController` owns the comment UI and subscribes to `CommentThreadService` callbacks:
- `onThreadReady` → full thread render
- `onCommentsAppended` → incremental updates
- `onError` → status messaging
The controller translates the thread snapshot into UI state for the modal and uses the comment thread’s profile cache to annotate authors.
### Telemetry/logging
All comment-thread telemetry is routed through `logger.dev`/`logger.user` (no direct console logging). The service emits warnings for:
- Nostr client failures (fetch/subscribe)
- Comment cache read/write errors
- Profile hydration retries and failures
## Discussion count flow (`DiscussionCountService`)
### Data sources
`DiscussionCountService` uses NIP-45 COUNT queries to count comment events across relays. It requires a Nostr client that exposes:
- `nostrClient.pool` (relay pool)
- `nostrClient.countEventsAcrossRelays(filters)`
### Filters & event kinds
The service builds COUNT filters based on the current video metadata. The base event kind is `COMMENT_EVENT_KIND` (video comment kind). Filters include:
- `#E` (root event id)
- `#A` (address pointer from `buildVideoAddressPointer(video)`)
- `#I` (root identifier / videoRootId)
- `#K` (root kind)
- `#P` (root author)
It always tries an `#E` filter first, then constructs an uppercase-tag filter that uses the best available pointer information, and finally a lowercase `#A` filter when available.
### Caching
Counts are cached in-memory per video id during a session:
- `videoDiscussionCountCache` stores resolved counts.
- `inFlightDiscussionCounts` deduplicates concurrent requests.
There is no persistence beyond the runtime session.
### Polling & refresh
Discussion counts are not polled on a timer. They refresh when the UI asks for them (typically after feed renders). The application triggers refreshes via `app.refreshVideoDiscussionCounts`, which is wired into video list rendering flows.
### UI surfacing
The service updates DOM nodes directly using data attributes:
- Root node: `[data-discussion-count="<video id>"]`
- Count value: `[data-discussion-count-value]`
The element’s `data-count-state` is set to:
- `pending` while a COUNT request is in flight
- `ready` after a successful response
- `unsupported` or `error` when COUNT is unavailable or fails
If relay support is missing, the service sets a tooltip (`title`) explaining that COUNT is unsupported.
### Telemetry/logging
Failures to fetch counts are logged through the injected logger (default `devLogger.warn`), and the UI falls back to the `error`/`unsupported` state rather than throwing.