From c3f19f4bf5152a7d3d51cbb3593f566c002b6b7a Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 19 Jan 2026 15:00:15 +0000 Subject: [PATCH] add reproducer for repost comment visibility bug This commit adds a minimal reproducer script that demonstrates a bug where comments on an original video are hidden when viewing that video via a Repost (Kind 6). The issue stems from `normalizeCommentTarget` using the Repost's metadata (Kind 6, Reposter Pubkey) instead of the Original Video's metadata to generate the comment filter descriptor. This causes valid comments (which tag the Original Video) to be filtered out by `isVideoCommentEvent`. Added: - `examples/reproducers/2-repost-comment-visibility-bug/reproduce_issue.js` - `examples/reproducers/2-repost-comment-visibility-bug/README.md` --- css/tailwind.source.css | 6 +- debug_comments.js | 58 --------------- .../2-repost-comment-visibility-bug/README.md | 29 ++++++++ .../reproduce_issue.js | 73 +++++++++++++++++++ reproduce_issue.js | 53 -------------- 5 files changed, 106 insertions(+), 113 deletions(-) delete mode 100644 debug_comments.js create mode 100644 examples/reproducers/2-repost-comment-visibility-bug/README.md create mode 100644 examples/reproducers/2-repost-comment-visibility-bug/reproduce_issue.js delete mode 100644 reproduce_issue.js diff --git a/css/tailwind.source.css b/css/tailwind.source.css index 828d191a..f68ce3c7 100644 --- a/css/tailwind.source.css +++ b/css/tailwind.source.css @@ -5291,7 +5291,8 @@ font-size: var(--font-size-2xs); font-weight: 600; color: var(--text-muted); - transition: color var(--motion-duration-fast) var(--motion-ease-out), + transition: + color var(--motion-duration-fast) var(--motion-ease-out), background var(--motion-duration-fast) var(--motion-ease-out), border-color var(--motion-duration-fast) var(--motion-ease-out); } @@ -5352,7 +5353,8 @@ background: var(--surface-sunken); font-size: var(--font-size-xs); text-align: left; - transition: border-color var(--motion-duration-fast) var(--motion-ease-out), + transition: + border-color var(--motion-duration-fast) var(--motion-ease-out), background var(--motion-duration-fast) var(--motion-ease-out); } diff --git a/debug_comments.js b/debug_comments.js deleted file mode 100644 index f8d52224..00000000 --- a/debug_comments.js +++ /dev/null @@ -1,58 +0,0 @@ - -import { __testExports } from "./js/nostr/commentEvents.js"; -const { isVideoCommentEvent, normalizeCommentTarget } = __testExports; -import { devLogger, userLogger } from "./js/utils/logger.js"; - -// Logger mocking removed - -const videoEventId = "0000000000000000000000000000000000000000000000000000000000000001"; -const rootKind = "30078"; -const rootAuthor = "0000000000000000000000000000000000000000000000000000000000000002"; - -const targetInput = { - videoEventId, - videoKind: rootKind, - videoAuthorPubkey: rootAuthor, -}; - -const descriptor = normalizeCommentTarget(targetInput); - -console.log("Descriptor:", JSON.stringify(descriptor, null, 2)); - -const validEvent = { - kind: 1111, - tags: [ - ["e", videoEventId, "wss://relay.example.com", "root"], - ["K", rootKind], - ["P", rootAuthor], - ], -}; - -const missingKTagEvent = { - kind: 1111, - tags: [ - ["e", videoEventId, "wss://relay.example.com", "root"], - ["P", rootAuthor], - ], -}; - -const mismatchKTagEvent = { - kind: 1111, - tags: [ - ["e", videoEventId, "wss://relay.example.com", "root"], - ["K", "12345"], // Wrong kind - ["P", rootAuthor], - ], -}; - -const legacyEvent = { - kind: 1, - tags: [ - ["e", videoEventId, "wss://relay.example.com", "root"], - ], -}; - -console.log("Testing Valid Event:", isVideoCommentEvent(validEvent, descriptor)); -console.log("Testing Missing K Tag Event:", isVideoCommentEvent(missingKTagEvent, descriptor)); -console.log("Testing Mismatch K Tag Event:", isVideoCommentEvent(mismatchKTagEvent, descriptor)); -console.log("Testing Legacy Event:", isVideoCommentEvent(legacyEvent, descriptor)); diff --git a/examples/reproducers/2-repost-comment-visibility-bug/README.md b/examples/reproducers/2-repost-comment-visibility-bug/README.md new file mode 100644 index 00000000..f4bb3706 --- /dev/null +++ b/examples/reproducers/2-repost-comment-visibility-bug/README.md @@ -0,0 +1,29 @@ +# Repost Comment Visibility Bug + +This reproducer demonstrates an issue where comments made on an original video are not visible when viewing that video via a Repost (Kind 6). + +## The Bug + +When viewing a repost, the application often passes the Repost event's metadata (Kind 6, Reposter Pubkey) along with the *Original Video's ID* to the comment system. + +The `CommentThreadService` (via `normalizeCommentTarget`) creates a filter descriptor based on this input. It expects comments to tag the **Repost** (Kind 6) and the **Reposter**. + +However, comments are almost always made on the **Original Video** (Kind 30078), tagging the **Original Author**. + +Because `isVideoCommentEvent` enforces strict matching of `rootKind` and `rootAuthor` against the descriptor, it rejects the valid comments on the original video, resulting in an empty comment section. + +## Running the Reproducer + +```bash +node --import ./tests/test-helpers/setup-localstorage.mjs examples/reproducers/2-repost-comment-visibility-bug/reproduce_issue.js +``` + +## Expected Output (Failure) + +``` +Input Target: { ... videoKind: 6 ... } +Generated Descriptor: { ... rootKind: '6' ... } +... +Is Comment Visible? false +FAIL: Comment on original video is NOT visible when viewing repost. +``` diff --git a/examples/reproducers/2-repost-comment-visibility-bug/reproduce_issue.js b/examples/reproducers/2-repost-comment-visibility-bug/reproduce_issue.js new file mode 100644 index 00000000..9c3f458c --- /dev/null +++ b/examples/reproducers/2-repost-comment-visibility-bug/reproduce_issue.js @@ -0,0 +1,73 @@ +import { __testExports } from "../../../js/nostr/commentEvents.js"; +const { isVideoCommentEvent, normalizeCommentTarget } = __testExports; + +/** + * REPRODUCER: Repost Comment Visibility Bug + * + * ISSUE: + * When a video is a repost (Kind 6), the UI may pass the Repost event's metadata + * (kind: 6, author: reposter) along with the Original Video's ID to the comment service. + * + * `normalizeCommentTarget` uses this metadata to build a descriptor that expects comments + * to tag the Repost event (Kind 6) and the Reposter. + * + * However, comments are typically made on the Original Video (Kind 30078), tagging the + * Original Author and Original Kind. + * + * `isVideoCommentEvent` then filters out these valid comments because they don't match + * the "expected" rootKind/rootAuthor (which are set to Repost details). + * + * EXPECTED BEHAVIOR: + * Comments on the original video should be visible even when viewing the repost. + * The descriptor should normalize to the Original Video's metadata if the ID belongs to it, + * OR `isVideoCommentEvent` should be smart enough to handle this mismatch. + */ + +// Mock Data +const originalVideoId = + "0000000000000000000000000000000000000000000000000000000000000001"; +const originalAuthor = + "0000000000000000000000000000000000000000000000000000000000000002"; +const originalKind = 30078; + +const reposterPubkey = + "0000000000000000000000000000000000000000000000000000000000000003"; +const repostKind = 6; + +// The Input provided by UI when viewing a Repost +// It incorrectly mixes Original ID with Repost Kind/Author in the current flow +const targetInput = { + videoEventId: originalVideoId, + videoKind: repostKind, + videoAuthorPubkey: reposterPubkey +}; + +console.log("Input Target:", targetInput); + +const descriptor = normalizeCommentTarget(targetInput); +console.log("Generated Descriptor:", JSON.stringify(descriptor, null, 2)); + +// A valid comment on the ORIGINAL video +const validCommentOnOriginal = { + kind: 1111, + tags: [ + ["e", originalVideoId, "wss://relay.example.com", "root"], + ["K", String(originalKind)], + ["P", originalAuthor] + ] +}; + +console.log("Comment Event Tags:", validCommentOnOriginal.tags); + +// Run the check +const isVisible = isVideoCommentEvent(validCommentOnOriginal, descriptor); +console.log(`\nIs Comment Visible? ${isVisible}`); + +if (!isVisible) { + console.error( + "FAIL: Comment on original video is NOT visible when viewing repost." + ); + process.exit(1); +} else { + console.log("PASS: Comment on original video is visible."); +} diff --git a/reproduce_issue.js b/reproduce_issue.js deleted file mode 100644 index f50f17f5..00000000 --- a/reproduce_issue.js +++ /dev/null @@ -1,53 +0,0 @@ - -import { __testExports } from "./js/nostr/commentEvents.js"; -const { isVideoCommentEvent, normalizeCommentTarget } = __testExports; - -const originalVideoId = "0000000000000000000000000000000000000000000000000000000000000001"; -const originalAuthor = "0000000000000000000000000000000000000000000000000000000000000002"; -const originalKind = "30078"; - -const reposterPubkey = "0000000000000000000000000000000000000000000000000000000000000003"; -const repostKind = "6"; - -// Scenario: UI passes the Repost event as the "video" -const targetInput = { - videoEventId: originalVideoId, // The ID is usually the original ID even in reposts? No, repost has its own ID. - // But CommentThreadService uses video.id. - // If video is Repost, video.id is Repost ID. - // But comments are on Original ID. - // So if video.id is Repost ID, then we are fetching comments for the Repost ID! - // And the comment event tags the Repost ID? No, it tags the Original ID. - // So we wouldn't even FETCH the comments if we used Repost ID. - - // Wait, if loadThread is called with Repost object, videoEventId = Repost ID. - // fetchThread queries for #e = Repost ID. - // But comments are on Original ID. - // So we get NO comments. - - // So the issue is even earlier: we are querying for the wrong ID. - - // BUT, assuming we somehow got the comment (maybe via legacy #e tag pointing to Repost?), - // let's see if it passes filter. - - // Let's assume the UI unwraps the ID but passes the Repost Kind/Pubkey? - // If video.id is Original ID, but video.kind is 6 (Repost Kind) and video.pubkey is Reposter. - - videoEventId: originalVideoId, - videoKind: repostKind, - videoAuthorPubkey: reposterPubkey, -}; - -const descriptor = normalizeCommentTarget(targetInput); - -console.log("Descriptor:", JSON.stringify(descriptor, null, 2)); - -const validCommentOnOriginal = { - kind: 1111, - tags: [ - ["e", originalVideoId, "wss://relay.example.com", "root"], - ["K", originalKind], - ["P", originalAuthor], - ], -}; - -console.log("Testing Valid Comment on Original (against Repost metadata):", isVideoCommentEvent(validCommentOnOriginal, descriptor));