From 9d859b07edbfa3a770d5445795d297610f7cdb07 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Fri, 2 Jan 2026 11:08:28 -0500 Subject: [PATCH] Render guest comment snapshots before auth gating --- js/ui/videoModalCommentController.js | 14 +++++--- tests/video-modal-comments.test.mjs | 54 ++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/js/ui/videoModalCommentController.js b/js/ui/videoModalCommentController.js index ad88cc21..7c2451f0 100644 --- a/js/ui/videoModalCommentController.js +++ b/js/ui/videoModalCommentController.js @@ -201,7 +201,6 @@ export default class VideoModalCommentController { this.videoModal.clearComments?.(); this.videoModal.resetCommentComposer?.(); this.videoModal.setCommentStatus?.("Loading comments…"); - this.applyCommentComposerAuthState(); const loadPromise = this.commentThreadService.loadThread({ video, @@ -216,11 +215,15 @@ export default class VideoModalCommentController { this.modalCommentLoadPromise = loadPromise; loadPromise - .then(() => { + .then((snapshot) => { if (this.modalCommentLoadPromise === loadPromise) { this.modalCommentLoadPromise = null; } - this.applyCommentComposerAuthState(); + + const didRenderSnapshot = this.handleCommentThreadReady(snapshot); + if (didRenderSnapshot === false) { + this.applyCommentComposerAuthState(); + } }) .catch((error) => { if (this.modalCommentLoadPromise === loadPromise) { @@ -355,14 +358,14 @@ export default class VideoModalCommentController { handleCommentThreadReady(snapshot) { if (!snapshot || !this.videoModal) { - return; + return false; } const snapshotVideoEventId = normalizeHexId(snapshot.videoEventId); const modalVideoEventId = normalizeHexId(this.modalCommentState.videoEventId); if (!snapshotVideoEventId || snapshotVideoEventId !== modalVideoEventId) { - return; + return false; } this.modalCommentProfiles = this.createMapFromInput(snapshot.profiles, { @@ -405,6 +408,7 @@ export default class VideoModalCommentController { this.videoModal.renderComments?.(sanitizedSnapshot); this.videoModal.setCommentStatus?.(""); this.applyCommentComposerAuthState(); + return true; } handleCommentThreadAppend(payload) { diff --git a/tests/video-modal-comments.test.mjs b/tests/video-modal-comments.test.mjs index 47b2edf6..983e8f6f 100644 --- a/tests/video-modal-comments.test.mjs +++ b/tests/video-modal-comments.test.mjs @@ -186,6 +186,60 @@ test( }, ); +test( + "Guest users render loaded thread snapshots before composer auth enforcement", + async (t) => { + const { modal, cleanup } = await setupModal(); + t.after(cleanup); + + const snapshot = { + videoEventId: "video-loaded-snapshot", + parentCommentId: null, + commentsById: new Map([ + [ + "loaded-comment", + createCommentEvent({ + id: "loaded-comment", + pubkey: "pk-loaded", + content: "Snapshot content shows up", + createdAt: 1700000100, + }), + ], + ]), + childrenByParent: new Map([[null, ["loaded-comment"]]]), + profiles: new Map([["pk-loaded", { name: "Loaded" }]]), + }; + + const controller = new VideoModalCommentController({ + commentThreadService: { + defaultLimit: 1, + setCallbacks: () => {}, + teardown: () => {}, + loadThread: () => Promise.resolve(snapshot), + }, + videoModal: modal, + auth: { + isLoggedIn: () => false, + }, + }); + + controller.load({ id: "video-loaded-snapshot", enableComments: true }); + await controller.modalCommentLoadPromise; + + const renderedComments = Array.from( + modal.commentsList.querySelectorAll("[data-comment-id]") || [], + ); + assert.equal(renderedComments.length, 1); + assert.equal(renderedComments[0].dataset.commentId, "loaded-comment"); + + const statusText = + modal.commentsStatusMessage?.textContent.trim().replace(/\s+/g, " ") || + ""; + assert.equal(statusText, ""); + assert.equal(modal.commentComposerState.reason, "login-required"); + }, +); + test( "VideoModalCommentController attaches profiles using normalized pubkeys", async () => {