diff --git a/js/services/commentThreadService.js b/js/services/commentThreadService.js index 21dff452..9b32ce86 100644 --- a/js/services/commentThreadService.js +++ b/js/services/commentThreadService.js @@ -603,18 +603,19 @@ export default class CommentThreadService { }); } - startSubscription(target) { + startSubscription(target, options = {}) { if (typeof this.subscribeVideoComments !== "function") { return; } - const options = { + const subOptions = { + ...options, relays: this.activeRelays, onEvent: (event) => this.processIncomingEvent(event), }; try { - const cleanup = this.subscribeVideoComments(target, options); + const cleanup = this.subscribeVideoComments(target, subOptions); if (typeof cleanup === "function") { this.subscriptionCleanup = () => { try { diff --git a/js/ui/components/VideoModal.js b/js/ui/components/VideoModal.js index 451743da..89fd1d93 100644 --- a/js/ui/components/VideoModal.js +++ b/js/ui/components/VideoModal.js @@ -1104,11 +1104,36 @@ export class VideoModal { return; } - container.appendChild(node); + this.insertCommentNodeSorted(container, node); this.updateCommentCount(this.commentNodes.size); this.toggleCommentEmptyState(this.commentNodes.size === 0); } + insertCommentNodeSorted(container, node) { + if (!container || !node) { + return; + } + + const timestamp = Number(node.dataset.timestamp || 0); + let inserted = false; + + const children = container.children; + for (let i = 0; i < children.length; i++) { + const child = children[i]; + const childTimestamp = Number(child.dataset.timestamp || 0); + + if (childTimestamp > timestamp) { + container.insertBefore(node, child); + inserted = true; + break; + } + } + + if (!inserted) { + container.appendChild(node); + } + } + buildCommentTree(commentId, commentsMap, childrenMap, depth = 0) { const normalizedId = typeof commentId === "string" && commentId.trim() @@ -1172,6 +1197,9 @@ export class VideoModal { listItem.classList.add("comment-thread__item"); listItem.dataset.commentId = commentId; listItem.dataset.commentDepth = String(Math.max(0, depth)); + listItem.dataset.timestamp = String( + Number.isFinite(event.created_at) ? event.created_at : 0 + ); if (typeof event.pubkey === "string" && event.pubkey.trim()) { listItem.dataset.commentAuthor = event.pubkey.trim(); } diff --git a/tests/app/channel-profile-moderation.test.mjs b/tests/app/channel-profile-moderation.test.mjs index f647c6f2..76f853c2 100644 --- a/tests/app/channel-profile-moderation.test.mjs +++ b/tests/app/channel-profile-moderation.test.mjs @@ -38,7 +38,6 @@ function setupDom(t) { CustomEvent: globalThis.CustomEvent, Event: globalThis.Event, Node: globalThis.Node, - navigator: globalThis.navigator, location: globalThis.location, MutationObserver: globalThis.MutationObserver, IntersectionObserver: globalThis.IntersectionObserver, @@ -50,7 +49,11 @@ function setupDom(t) { globalThis.CustomEvent = window.CustomEvent; globalThis.Event = window.Event; globalThis.Node = window.Node; - globalThis.navigator = window.navigator; + Object.defineProperty(globalThis, "navigator", { + value: window.navigator, + writable: true, + configurable: true, + }); globalThis.location = window.location; globalThis.MutationObserver = window.MutationObserver; globalThis.IntersectionObserver = diff --git a/tests/video-modal-accessibility.test.mjs b/tests/video-modal-accessibility.test.mjs index 7cc857d8..79d725dd 100644 --- a/tests/video-modal-accessibility.test.mjs +++ b/tests/video-modal-accessibility.test.mjs @@ -37,6 +37,12 @@ export async function setupModal({ lazyLoad = false } = {}) { configurable: true, writable: true, }); + if (globalThis.navigator && !globalThis.navigator.clipboard) { + globalThis.navigator.clipboard = { + writeText: () => Promise.resolve(), + readText: () => Promise.resolve(""), + }; + } globalThis.location = window.location; globalThis.KeyboardEvent = window.KeyboardEvent; globalThis.MouseEvent = window.MouseEvent;