Two bugs in the #26 wiring surfaced live:
- The ⋯ menu lazy-import used "../../viewCountChart.js" which resolves one level
too high (dist/viewCountChart.js → 404 "Failed to fetch dynamically imported
module"). Corrected to "../viewCountChart.js" (js/ui → js/viewCountChart.js).
- The video-list context path called handleMoreMenuAction without the video, so
view-stats hit "No video selected". Forward detail.video.
Also make the popularity modal single-instance: close any open one (proper
subscription/listener cleanup) before opening a new one, so the two menu paths
firing for one click can't stack duplicates or leak a live subscription.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
A "Popularity" item in the video ⋯ menu opens a public views-over-time chart
(view data is public). Builds on the now-trustworthy view counts (#4).
- buildViewCountTimeSeries buckets raw kind-30079 view events by the counter's
dedupe window (one view per viewer per day) into a cumulative series.
- buildViewCountChartSvg renders a token-colored (currentColor / text-accent)
SVG area+line chart — no charting library, no raw colors.
- The modal fetches via listVideoViewEventsWithDefaultClient, shows the total,
and updates LIVE by subscribing to view events (debounced re-render); cleans
up on close (Esc / backdrop / ✕). Lazy-loaded from the menu handler so it
only ships when opened.
New size tokens for the modal; reuses .ds-overlay-backdrop for the scrim.
Tests: tests/view-count-chart.test.mjs (cumulative dedupe, junk filtering,
SVG line/area + empty/single-point).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
- Updates `js/userBlocks.js` to treat Kind 10000 (standard Mute List) as the primary write target.
- Public mutes are stored in `p` tags, and private blocks are stored in encrypted content within the same event.
- Legacy `d=user-blocks` lists are still read for backward compatibility (preferring the newest event), but no longer written.
- Updates `js/nostrEventSchemas.js` to mark `USER_BLOCK_LIST` as legacy and standardise `MUTE_LIST`.
- Updates UI controllers (`MoreMenuController`, `ProfileModalController`) to use "Mute/Block List" terminology and delegate all mute/block actions to `userBlocks`.
- Updates `ModerationService` to delegate viewer mute operations to `userBlocks` and disable direct Kind 10000 publishing to prevent overwrites.
- Documentation updated in `docs/nostr-event-schemas.md`.
- Fixes regressions in `more-menu-controller.test.mjs`, `moderation-service.test.mjs`, `trusted-mute-lists.test.mjs`, and `user-blocks.test.mjs`.
Co-authored-by: PR0M3TH3AN <[email protected]>