From a20388c5d6007265335e84999ac2bcbe7e3f5f68 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Thu, 2 Jul 2026 08:51:10 -0400 Subject: [PATCH] =?UTF-8?q?[video-modal]=20Fix=20dead=20channel/creator=20?= =?UTF-8?q?link=20=E2=80=94=20event-name=20mismatch=20(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clicking the creator avatar/name in the video player modal did nothing. VideoModal dispatched "navigate:profile", but ModalManager only listens for "creator:navigate" (→ app.openCreatorChannel), so the event went into the void — same class as the old action:embed bug noted just above the handler. Fix: - VideoModal.handleCreatorNavigation dispatches "creator:navigate" (the listened name) with { pubkey }. - ModalManager's handler forwards event.detail.pubkey to app.openCreatorChannel. - routerCoordinator.openCreatorChannel(pubkey) accepts an optional pubkey (prefers it, falls back to the active video's creator), so it navigates to the modal's own creator rather than whatever currentVideo happens to be. Tests: tests/video-modal-controllers.test.mjs (+2 — dispatches creator:navigate with the pubkey and never the dead navigate:profile; no-op without a pubkey). Lint + build green. Co-Authored-By: Claude Opus 4.8 --- js/app/routerCoordinator.js | 12 ++++-- js/ui/ModalManager.js | 4 +- js/ui/components/VideoModal.js | 6 ++- tests/video-modal-controllers.test.mjs | 58 ++++++++++++++++++++++++++ todo/TODO_2026-06-20_pre-launch.md | 13 ++++-- 5 files changed, 83 insertions(+), 10 deletions(-) diff --git a/js/app/routerCoordinator.js b/js/app/routerCoordinator.js index 92c5d713..0f019db8 100644 --- a/js/app/routerCoordinator.js +++ b/js/app/routerCoordinator.js @@ -45,15 +45,21 @@ export function createRouterCoordinator(deps) { window.location.hash = `#view=channel-profile&npub=${npub}`; }, - openCreatorChannel() { - if (!this.currentVideo || !this.currentVideo.pubkey) { + openCreatorChannel(pubkey) { + // Prefer an explicit pubkey (e.g. the video modal's own creator) and fall + // back to the active video's creator. + const targetPubkey = + (typeof pubkey === "string" && pubkey.trim()) || + this.currentVideo?.pubkey || + ""; + if (!targetPubkey) { this.showError("No creator info available."); return; } try { // Encode the hex pubkey to npub - const npub = window.NostrTools.nip19.npubEncode(this.currentVideo.pubkey); + const npub = window.NostrTools.nip19.npubEncode(targetPubkey); // Close the video modal this.hideModal(); diff --git a/js/ui/ModalManager.js b/js/ui/ModalManager.js index cdf0c13b..7ac2cc50 100644 --- a/js/ui/ModalManager.js +++ b/js/ui/ModalManager.js @@ -574,8 +574,8 @@ export default class ModalManager { this.videoModalHandlers.contextAction, ); - this.videoModalHandlers.creatorNavigate = () => { - app.openCreatorChannel(); + this.videoModalHandlers.creatorNavigate = (event) => { + app.openCreatorChannel(event?.detail?.pubkey); }; this.videoModal.addEventListener( "creator:navigate", diff --git a/js/ui/components/VideoModal.js b/js/ui/components/VideoModal.js index a612b40e..4f2d140a 100644 --- a/js/ui/components/VideoModal.js +++ b/js/ui/components/VideoModal.js @@ -3017,7 +3017,11 @@ export class VideoModal { handleCreatorNavigation() { if (this.activeVideo?.pubkey) { - this.dispatch("navigate:profile", { pubkey: this.activeVideo.pubkey }); + // ModalManager listens for "creator:navigate" (→ app.openCreatorChannel). + // The previous "navigate:profile" name had NO listener, so clicking the + // creator avatar/name did nothing — the event went into the void (same class + // as the old action:embed bug above). + this.dispatch("creator:navigate", { pubkey: this.activeVideo.pubkey }); } } diff --git a/tests/video-modal-controllers.test.mjs b/tests/video-modal-controllers.test.mjs index 4199e211..b74bc17d 100644 --- a/tests/video-modal-controllers.test.mjs +++ b/tests/video-modal-controllers.test.mjs @@ -190,3 +190,61 @@ test("VideoModal trims tag strip to fit modal width", async (t) => { ); }); + +// Channel-profile link in the player modal (TODO #53): clicking the creator +// avatar/name must navigate to the creator's channel. It dispatched +// "navigate:profile", which had NO listener (ModalManager listens for +// "creator:navigate"), so the click did nothing — same class as the old +// action:embed bug. Now it dispatches "creator:navigate" with the pubkey. +// +// test_integrity_note: +// change_type: ["new_tests"] +// scenarios: +// - id: SCN-video-modal-creator-nav +// given: "a video modal with an active video" +// when: "handleCreatorNavigation runs (creator avatar/name click)" +// then: "it dispatches creator:navigate with the pubkey, not the dead navigate:profile" +// observable_outcomes: +// - "creator:navigate fires with { pubkey }" +// - "the old dead navigate:profile event never fires" +// determinism_controls: +// - "JSDOM modal via setupModal; synchronous dispatch" +// anti_cheat_rationale: +// prevents: ["hard-coded return value", "asserting the broken event name"] +// relaxation: +// did_relax_any_assertion: false + +test("creator link dispatches creator:navigate with the pubkey (not the dead navigate:profile)", async (t) => { + const { modal, cleanup } = await setupModal(); + t.after(cleanup); + + modal.activeVideo = { pubkey: "a".repeat(64) }; + + let creatorNavigate = null; + let deadEventFired = false; + modal.addEventListener("creator:navigate", (event) => { + creatorNavigate = event.detail; + }); + modal.addEventListener("navigate:profile", () => { + deadEventFired = true; + }); + + modal.handleCreatorNavigation(); + + assert.ok(creatorNavigate, "creator:navigate should fire (ModalManager listens for it)"); + assert.equal(creatorNavigate.pubkey, "a".repeat(64)); + assert.equal(deadEventFired, false, "the old listener-less navigate:profile must not be used"); +}); + +test("creator navigation is a no-op without an active video pubkey", async (t) => { + const { modal, cleanup } = await setupModal(); + t.after(cleanup); + + modal.activeVideo = { pubkey: "" }; + let fired = false; + modal.addEventListener("creator:navigate", () => { + fired = true; + }); + modal.handleCreatorNavigation(); + assert.equal(fired, false, "no pubkey → no navigation event"); +}); diff --git a/todo/TODO_2026-06-20_pre-launch.md b/todo/TODO_2026-06-20_pre-launch.md index d4ac2ba3..b5f7620a 100644 --- a/todo/TODO_2026-06-20_pre-launch.md +++ b/todo/TODO_2026-06-20_pre-launch.md @@ -1636,10 +1636,15 @@ passphrase-encrypted). Items 51, 56, 57 are all facets of that. `onVideosShouldRefresh` path #46 uses, keyed by `videoRootId`. ### 53. Channel-profile link in the video player modal doesn't work (BUG) -- [ ] Clicking the channel/creator link inside the video player modal does nothing - (should navigate to that author's channel profile view). Audit the modal's - channel-link handler (`js/ui/videoModal*` / the author byline) — likely a missing - or wrong nav callback (compare with the working channel links in the grid cards). +- [x] **FIXED 2026-07-02.** Event-name mismatch: `VideoModal.handleCreatorNavigation` + dispatched `"navigate:profile"`, but ModalManager only listens for + `"creator:navigate"` → the click went into the void (same class as the old + `action:embed` bug). Renamed the dispatch to `"creator:navigate"` and threaded the + modal's own creator pubkey through: the handler now passes `event.detail.pubkey` + to `app.openCreatorChannel(pubkey)`, and `routerCoordinator.openCreatorChannel` + accepts an optional pubkey (falls back to the active video's creator). Tests: + `tests/video-modal-controllers.test.mjs` (+2 — dispatches creator:navigate with + the pubkey, never the dead event; no-op without a pubkey). Lint + build green. ### 54. Like / dislike buttons in the video player modal don't work (BUG) - [ ] The reaction (like/dislike) buttons in the player modal are unresponsive / don't