From 04e72a0b4680bc04c00f4c51264c2f8855106dac Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Fri, 14 Nov 2025 09:30:01 -0500 Subject: [PATCH] Propagate video root identifier from pointer metadata --- js/services/commentThreadService.js | 6 ++- js/ui/videoModalCommentController.js | 12 ++++- tests/comment-thread-service.test.mjs | 40 ++++++++++++++++ tests/video-modal-comments.test.mjs | 67 +++++++++++++++++++++++++++ 4 files changed, 122 insertions(+), 3 deletions(-) diff --git a/js/services/commentThreadService.js b/js/services/commentThreadService.js index 3a995071..ecec9c85 100644 --- a/js/services/commentThreadService.js +++ b/js/services/commentThreadService.js @@ -186,7 +186,11 @@ export default class CommentThreadService { this.videoKind = normalizeKind(video?.kind); this.videoAuthorPubkeyRaw = rawVideoAuthorPubkey; this.videoAuthorPubkey = normalizePubkey(rawVideoAuthorPubkey); - this.videoRootIdentifier = normalizeString(video?.videoRootId); + const pointerRootIdentifier = normalizeString( + video?.pointerIdentifiers?.videoRootId, + ); + this.videoRootIdentifier = + normalizeString(video?.videoRootId) || pointerRootIdentifier; this.videoRootRelay = normalizeRelay( video?.videoRootRelay || video?.rootIdentifierRelay, ); diff --git a/js/ui/videoModalCommentController.js b/js/ui/videoModalCommentController.js index 72aaffc5..3faf6eb1 100644 --- a/js/ui/videoModalCommentController.js +++ b/js/ui/videoModalCommentController.js @@ -161,7 +161,10 @@ export default class VideoModalCommentController { } const videoKind = normalizeKind(video.kind); const videoAuthorPubkey = normalizeString(video.pubkey); - const videoRootId = normalizeString(video.videoRootId); + const pointerRootId = normalizeString( + video.pointerIdentifiers?.videoRootId, + ); + const videoRootId = normalizeString(video.videoRootId) || pointerRootId; const videoRootRelay = normalizeRelay( video.videoRootRelay || video.rootIdentifierRelay, ); @@ -611,8 +614,13 @@ export default class VideoModalCommentController { this.modalCommentState.videoKind || normalizeKind(video.kind); const videoAuthorPubkey = this.modalCommentState.videoAuthorPubkey || normalizeString(video.pubkey); + const fallbackPointerRootId = normalizeString( + video.pointerIdentifiers?.videoRootId, + ); const videoRootId = - this.modalCommentState.videoRootId || normalizeString(video.videoRootId); + this.modalCommentState.videoRootId || + normalizeString(video.videoRootId) || + fallbackPointerRootId; const videoRootRelay = this.modalCommentState.videoRootRelay || normalizeRelay(video.videoRootRelay || video.rootIdentifierRelay); diff --git a/tests/comment-thread-service.test.mjs b/tests/comment-thread-service.test.mjs index 525d0564..bf276403 100644 --- a/tests/comment-thread-service.test.mjs +++ b/tests/comment-thread-service.test.mjs @@ -509,6 +509,46 @@ test( }, ); +test( + "CommentThreadService falls back to pointerIdentifiers root id", + async () => { + const video = { + id: "pointer-video", + pubkey: "rootpk", + kind: 30078, + tags: [], + pointerIdentifiers: { + videoRootId: "pointer-root", + }, + }; + + const pointerComment = { + id: "comment-pointer", + kind: COMMENT_EVENT_KIND, + pubkey: "commenter", + created_at: 1700000001, + content: "Pointer scoped", + tags: [["i", "pointer-root"], ["p", "rootpk"]], + }; + + let fetchTarget = null; + const service = new CommentThreadService({ + nostrClient: { ensurePool: async () => {} }, + fetchVideoComments: async (target) => { + fetchTarget = target; + return [pointerComment]; + }, + subscribeVideoComments: () => () => {}, + }); + + const snapshot = await service.loadThread({ video }); + + assert.ok(fetchTarget, "fetch should run when pointer identifiers are provided"); + assert.equal(fetchTarget.rootIdentifier, "pointer-root"); + assert.equal(snapshot.rootIdentifier, "pointer-root"); + }, +); + test( "CommentThreadService preserves raw video author pubkeys during hydration fetches", async () => { diff --git a/tests/video-modal-comments.test.mjs b/tests/video-modal-comments.test.mjs index e2927ee7..02f1f4aa 100644 --- a/tests/video-modal-comments.test.mjs +++ b/tests/video-modal-comments.test.mjs @@ -407,6 +407,73 @@ test( }, ); +test( + "VideoModalCommentController uses pointerIdentifiers root id fallback", + async () => { + const publishCalls = []; + const loadTargets = []; + + const controller = new VideoModalCommentController({ + commentThreadService: { + setCallbacks: () => {}, + teardown: () => {}, + defaultLimit: 10, + loadThread: async (target) => { + loadTargets.push(target); + return {}; + }, + processIncomingEvent: () => {}, + }, + videoModal: { + setCommentSectionCallbacks: () => {}, + hideCommentsDisabledMessage: () => {}, + showCommentsDisabledMessage: () => {}, + setCommentsVisibility: () => {}, + clearComments: () => {}, + resetCommentComposer: () => {}, + setCommentStatus: () => {}, + setCommentComposerState: () => {}, + renderComments: () => {}, + }, + auth: { + isLoggedIn: () => true, + }, + services: { + publishComment: async (payload) => { + publishCalls.push(payload); + return { ok: true, event: { id: "comment", tags: [] } }; + }, + }, + utils: { + normalizeHexPubkey: (value) => value, + }, + }); + + const video = { + id: "pointer-video", + pubkey: "AUTHORPK", + enableComments: true, + kind: 30078, + pointerIdentifiers: { + videoRootId: "pointer-root", + }, + }; + + controller.load(video); + await Promise.resolve(); + + assert.equal(loadTargets.length, 1, "loadThread should capture pointer target"); + assert.equal(loadTargets[0].rootIdentifier, "pointer-root"); + + controller.submit({ text: "Pointer fallback" }); + await controller.modalCommentPublishPromise; + + assert.equal(publishCalls.length, 1, "publishComment should run once"); + const payload = publishCalls[0]; + assert.equal(payload.rootIdentifier, "pointer-root"); + }, +); + test( "VideoModalCommentController publishes comment using event id fallback", async () => {