# 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:` - 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="