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));