mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-08 13:52:58 +00:00
Merge pull request #1056 from PR0M3TH3AN/codex/decouple-snapshot-rendering-for-comments
Render guest comment snapshots before auth gating
This commit is contained in:
2 files changed
+192
-11
No files matched your search
@@ -201,29 +201,40 @@ export default class VideoModalCommentController {
|
||||
this.videoModal.clearComments?.();
|
||||
this.videoModal.resetCommentComposer?.();
|
||||
this.videoModal.setCommentStatus?.("Loading comments…");
|
||||
this.applyCommentComposerAuthState();
|
||||
|
||||
const loadPromise = this.commentThreadService.loadThread({
|
||||
const loadResult = this.commentThreadService.loadThread({
|
||||
video,
|
||||
parentCommentId: null,
|
||||
limit: this.modalCommentLimit,
|
||||
});
|
||||
|
||||
if (!loadPromise || typeof loadPromise.then !== "function") {
|
||||
if (!loadResult) {
|
||||
this.applyCommentComposerAuthState();
|
||||
return;
|
||||
}
|
||||
|
||||
this.modalCommentLoadPromise = loadPromise;
|
||||
loadPromise
|
||||
.then(() => {
|
||||
if (this.modalCommentLoadPromise === loadPromise) {
|
||||
if (typeof loadResult.then !== "function") {
|
||||
const didRenderSnapshot = this.handleCommentThreadReady(loadResult);
|
||||
if (didRenderSnapshot === false) {
|
||||
this.applyCommentComposerAuthState();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.modalCommentLoadPromise = loadResult;
|
||||
loadResult
|
||||
.then((snapshot) => {
|
||||
if (this.modalCommentLoadPromise === loadResult) {
|
||||
this.modalCommentLoadPromise = null;
|
||||
}
|
||||
this.applyCommentComposerAuthState();
|
||||
|
||||
const didRenderSnapshot = this.handleCommentThreadReady(snapshot);
|
||||
if (didRenderSnapshot === false) {
|
||||
this.applyCommentComposerAuthState();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
if (this.modalCommentLoadPromise === loadPromise) {
|
||||
if (this.modalCommentLoadPromise === loadResult) {
|
||||
this.modalCommentLoadPromise = null;
|
||||
}
|
||||
this.handleCommentThreadError(error);
|
||||
@@ -355,14 +366,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, {
|
||||
@@ -402,9 +413,11 @@ export default class VideoModalCommentController {
|
||||
}
|
||||
|
||||
const sanitizedSnapshot = this.buildModalCommentSnapshot(snapshot);
|
||||
this.videoModal.setCommentsVisibility?.(true);
|
||||
this.videoModal.renderComments?.(sanitizedSnapshot);
|
||||
this.videoModal.setCommentStatus?.("");
|
||||
this.applyCommentComposerAuthState();
|
||||
return true;
|
||||
}
|
||||
|
||||
handleCommentThreadAppend(payload) {
|
||||
|
||||
@@ -186,6 +186,174 @@ 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(
|
||||
"Thread ready snapshots force comments visible before composer gating",
|
||||
async (t) => {
|
||||
const { modal, cleanup } = await setupModal();
|
||||
t.after(cleanup);
|
||||
|
||||
const visibilityCalls = [];
|
||||
const instrumentedModal = {
|
||||
...modal,
|
||||
setCommentsVisibility: (isVisible) => {
|
||||
visibilityCalls.push(isVisible);
|
||||
modal.setCommentsVisibility(isVisible);
|
||||
},
|
||||
};
|
||||
|
||||
const snapshot = {
|
||||
videoEventId: "video-visibility-snapshot",
|
||||
parentCommentId: null,
|
||||
commentsById: new Map([
|
||||
[
|
||||
"visible-comment",
|
||||
createCommentEvent({
|
||||
id: "visible-comment",
|
||||
pubkey: "pk-visible",
|
||||
content: "Rendered as soon as thread is ready",
|
||||
createdAt: 1700000300,
|
||||
}),
|
||||
],
|
||||
]),
|
||||
childrenByParent: new Map([[null, ["visible-comment"]]]),
|
||||
profiles: new Map([["pk-visible", { name: "Visible" }]]),
|
||||
};
|
||||
|
||||
const controller = new VideoModalCommentController({
|
||||
commentThreadService: {
|
||||
defaultLimit: 1,
|
||||
setCallbacks: () => {},
|
||||
teardown: () => {},
|
||||
loadThread: () => Promise.resolve(snapshot),
|
||||
},
|
||||
videoModal: instrumentedModal,
|
||||
auth: {
|
||||
isLoggedIn: () => false,
|
||||
},
|
||||
});
|
||||
|
||||
controller.load({ id: "video-visibility-snapshot", enableComments: true });
|
||||
await controller.modalCommentLoadPromise;
|
||||
|
||||
assert.ok(visibilityCalls.length >= 2);
|
||||
assert.equal(visibilityCalls.pop(), true);
|
||||
|
||||
const renderedComments = Array.from(
|
||||
modal.commentsList.querySelectorAll("[data-comment-id]") || [],
|
||||
);
|
||||
assert.equal(renderedComments.length, 1);
|
||||
assert.equal(renderedComments[0].dataset.commentId, "visible-comment");
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
"Controller renders synchronous loadThread snapshots immediately",
|
||||
async (t) => {
|
||||
const { modal, cleanup } = await setupModal();
|
||||
t.after(cleanup);
|
||||
|
||||
const snapshot = {
|
||||
videoEventId: "video-sync-snapshot",
|
||||
parentCommentId: null,
|
||||
commentsById: new Map([
|
||||
[
|
||||
"sync-comment",
|
||||
createCommentEvent({
|
||||
id: "sync-comment",
|
||||
pubkey: "pk-sync",
|
||||
content: "Snapshot delivered instantly",
|
||||
createdAt: 1700000200,
|
||||
}),
|
||||
],
|
||||
]),
|
||||
childrenByParent: new Map([[null, ["sync-comment"]]]),
|
||||
profiles: new Map([["pk-sync", { name: "Synchronous" }]]),
|
||||
};
|
||||
|
||||
const controller = new VideoModalCommentController({
|
||||
commentThreadService: {
|
||||
defaultLimit: 1,
|
||||
setCallbacks: () => {},
|
||||
teardown: () => {},
|
||||
loadThread: () => snapshot,
|
||||
},
|
||||
videoModal: modal,
|
||||
auth: {
|
||||
isLoggedIn: () => false,
|
||||
},
|
||||
});
|
||||
|
||||
controller.load({ id: "video-sync-snapshot", enableComments: true });
|
||||
await Promise.resolve();
|
||||
|
||||
const renderedComments = Array.from(
|
||||
modal.commentsList.querySelectorAll("[data-comment-id]") || [],
|
||||
);
|
||||
assert.equal(renderedComments.length, 1);
|
||||
assert.equal(renderedComments[0].dataset.commentId, "sync-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 () => {
|
||||
|
||||
Reference in new issue
Block a user