diff --git a/js/ui/moreMenuController.js b/js/ui/moreMenuController.js index 3dbadf03..0d8bda43 100644 --- a/js/ui/moreMenuController.js +++ b/js/ui/moreMenuController.js @@ -157,7 +157,7 @@ export default class MoreMenuController { ...(detail.dataset || {}), eventId: detail.dataset?.eventId || detail.video?.id || "", }; - this.handleMoreMenuAction(detail.action, dataset); + this.handleMoreMenuAction(detail.action, dataset, detail.video || null); }; if (typeof view.addEventListener === "function") { @@ -1176,7 +1176,7 @@ export default class MoreMenuController { // ignore } try { - const { openPopularityModal } = await import("../../viewCountChart.js"); + const { openPopularityModal } = await import("../viewCountChart.js"); openPopularityModal({ video: targetVideo }); } catch (error) { userLogger.warn("[MoreMenu] Failed to open popularity modal:", error); diff --git a/js/viewCountChart.js b/js/viewCountChart.js index e2ed6272..295e5be9 100644 --- a/js/viewCountChart.js +++ b/js/viewCountChart.js @@ -159,6 +159,9 @@ function el(doc, tag, className, text) { return node; } +// At most one popularity modal open at a time. +let activePopularityModal = null; + // Opens the popularity modal for a video. Dependencies are injectable for tests. export function openPopularityModal({ document: doc = typeof document !== "undefined" ? document : null, @@ -174,6 +177,18 @@ export function openPopularityModal({ } const pointer = pointerInfo.pointer; + // Close any already-open popularity modal (proper cleanup of its subscription + // + listeners) so re-triggering — or two menu paths firing for one click — + // can't stack duplicates. + if (activePopularityModal) { + try { + activePopularityModal.close(); + } catch (error) { + // ignore + } + activePopularityModal = null; + } + const root = mount || doc.getElementById("uiOverlay") || doc.body; const backdrop = el(doc, "div", "ds-overlay-backdrop popularity-modal__backdrop"); const dialog = el(doc, "div", "popularity-modal card p-md"); @@ -255,6 +270,9 @@ export function openPopularityModal({ } doc.removeEventListener("keydown", onKeydown, true); backdrop.remove(); + if (activePopularityModal === handle) { + activePopularityModal = null; + } }; function onKeydown(event) { @@ -298,5 +316,7 @@ export function openPopularityModal({ devLogger.warn("[popularity] Failed to subscribe to view events:", error); } - return { close }; + const handle = { close }; + activePopularityModal = handle; + return handle; }