diff --git a/js/ui/videoModalCommentController.js b/js/ui/videoModalCommentController.js index 3faf6eb1..fb999dbb 100644 --- a/js/ui/videoModalCommentController.js +++ b/js/ui/videoModalCommentController.js @@ -356,7 +356,9 @@ export default class VideoModalCommentController { return; } - this.modalCommentProfiles = this.createMapFromInput(snapshot.profiles); + this.modalCommentProfiles = this.createMapFromInput(snapshot.profiles, { + normalizeKey: this.utils.normalizeHexPubkey, + }); this.modalCommentState.parentCommentId = normalizeString( snapshot.parentCommentId, ) || null; @@ -407,7 +409,9 @@ export default class VideoModalCommentController { } const comments = this.createMapFromInput(payload.commentsById); - const profiles = this.createMapFromInput(payload.profiles); + const profiles = this.createMapFromInput(payload.profiles, { + normalizeKey: this.utils.normalizeHexPubkey, + }); profiles.forEach((profile, pubkey) => { this.modalCommentProfiles.set(pubkey, profile); }); @@ -469,22 +473,41 @@ export default class VideoModalCommentController { } } - createMapFromInput(input) { - if (input instanceof Map) { - return new Map(input); - } + createMapFromInput(input, { normalizeKey = null } = {}) { const map = new Map(); + const applyKey = (key) => { + if (!normalizeKey) { + return key; + } + const normalized = normalizeKey(key); + return normalized === null || normalized === undefined ? null : normalized; + }; + + const setEntry = (key, value) => { + const normalizedKey = applyKey(key); + if (normalizedKey === null || normalizedKey === undefined) { + return; + } + map.set(normalizedKey, value); + }; + + if (input instanceof Map) { + input.forEach((value, key) => { + setEntry(key, value); + }); + return map; + } if (Array.isArray(input)) { input.forEach((entry) => { if (Array.isArray(entry) && entry.length >= 2) { - map.set(entry[0], entry[1]); + setEntry(entry[0], entry[1]); } }); return map; } if (input && typeof input === "object") { Object.entries(input).forEach(([key, value]) => { - map.set(key, value); + setEntry(key, value); }); } return map; diff --git a/tests/video-modal-comments.test.mjs b/tests/video-modal-comments.test.mjs index 02f1f4aa..883a8058 100644 --- a/tests/video-modal-comments.test.mjs +++ b/tests/video-modal-comments.test.mjs @@ -120,6 +120,63 @@ test("VideoModal comment section toggles visibility and renders hydrated comment assert.equal(modal.commentsList.getAttribute("data-empty"), null); }); +test( + "VideoModalCommentController attaches profiles using normalized pubkeys", + async () => { + const appended = []; + + const controller = new VideoModalCommentController({ + commentThreadService: { + setCallbacks: () => {}, + teardown: () => {}, + loadThread: () => Promise.resolve(), + }, + videoModal: { + setCommentSectionCallbacks: () => {}, + hideCommentsDisabledMessage: () => {}, + showCommentsDisabledMessage: () => {}, + setCommentsVisibility: () => {}, + clearComments: () => {}, + resetCommentComposer: () => {}, + setCommentStatus: () => {}, + setCommentComposerState: () => {}, + appendComment: (comment) => appended.push(comment), + }, + auth: { + isLoggedIn: () => true, + }, + utils: { + normalizeHexPubkey: (value) => + typeof value === "string" ? value.toLowerCase() : null, + }, + }); + + controller.load({ id: "VideoCase", enableComments: true }); + await Promise.resolve(); + + controller.handleCommentThreadAppend({ + videoEventId: "VideoCase", + commentsById: new Map([ + [ + "c1", + { + id: "c1", + pubkey: "AbCd", + kind: COMMENT_EVENT_KIND, + content: "Hi", + tags: [], + }, + ], + ]), + commentIds: ["c1"], + profiles: new Map([["ABCD", { name: "Mixed Case" }]]), + }); + + assert.equal(appended.length, 1); + assert.deepEqual(appended[0].profile, { name: "Mixed Case" }); + }, +); + test("VideoModal comment composer updates messaging and dispatches events", async (t) => { const { window, document, modal, cleanup } = await setupModal(); t.after(cleanup);