From 17dda8f2c1d89f5dced6136eb78925902a4353c8 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Tue, 7 Oct 2025 22:08:09 -0400 Subject: [PATCH] Fix video modal readiness and sidebar stacking --- css/style.css | 13 +++++ js/app.js | 129 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 119 insertions(+), 23 deletions(-) diff --git a/css/style.css b/css/style.css index 5486e43a..2ce2b17d 100644 --- a/css/style.css +++ b/css/style.css @@ -987,6 +987,19 @@ body.modal-open #app { overflow: hidden; } +body.modal-open #sidebar, +body.modal-open #sidebarOverlay { + pointer-events: none; +} + +body.modal-open #sidebar { + z-index: var(--z-sidebar-overlay); +} + +body.modal-open #sidebarOverlay { + z-index: calc(var(--z-sidebar-overlay) - 1); +} + /* Modal Content Container */ .modal-content { width: 100%; diff --git a/js/app.js b/js/app.js index ed048602..ea48fd58 100644 --- a/js/app.js +++ b/js/app.js @@ -193,6 +193,7 @@ class Application { this.watchHistoryPreferenceUnsubscribe = null; this.authEventUnsubscribes = []; this.unsubscribeFromNostrService = null; + this.videoModalReadyPromise = null; this.boundUploadSubmitHandler = null; this.boundEditModalSubmitHandler = null; this.boundEditModalCancelHandler = null; @@ -1188,9 +1189,33 @@ class Application { */ showModalWithPoster(video = this.currentVideo) { if (!this.videoModal) { - return; + return Promise.resolve(null); } - this.videoModal.open(video || this.currentVideo); + + const targetVideo = video || this.currentVideo; + + return this.ensureVideoModalReady({ ensureVideoElement: true }) + .then(({ root }) => { + if (!this.videoModal) { + return root || null; + } + this.videoModal.open(targetVideo); + this.applyModalLoadingPoster(); + return ( + root || + (typeof this.videoModal.getRoot === "function" + ? this.videoModal.getRoot() + : null) + ); + }) + .catch((error) => { + console.error( + "[Application] Failed to open the video modal before playback:", + error + ); + this.showError("Could not open the video player. Please try again."); + return null; + }); } applyModalLoadingPoster() { @@ -1207,6 +1232,69 @@ class Application { return this.videoModal.forceRemovePoster(reason); } + async ensureVideoModalReady({ ensureVideoElement = false } = {}) { + if (!this.videoModal) { + throw new Error("Video modal instance is not available."); + } + + const getRoot = () => + typeof this.videoModal.getRoot === "function" + ? this.videoModal.getRoot() + : null; + const getVideoElement = () => + typeof this.videoModal.getVideoElement === "function" + ? this.videoModal.getVideoElement() + : null; + + const existingRoot = getRoot(); + const existingVideoElement = getVideoElement(); + + if (existingRoot && existingRoot.isConnected) { + if (ensureVideoElement && !existingVideoElement) { + throw new Error("Video modal video element is not ready."); + } + if (existingVideoElement) { + this.modalVideo = existingVideoElement; + } + return { + root: existingRoot, + videoElement: existingVideoElement, + }; + } + + if (!this.videoModalReadyPromise) { + if (typeof this.videoModal.load !== "function") { + throw new Error("Video modal does not expose a load() method."); + } + this.videoModalReadyPromise = Promise.resolve(this.videoModal.load()); + } + + try { + await this.videoModalReadyPromise; + } catch (error) { + this.videoModalReadyPromise = null; + throw error; + } + + this.videoModalReadyPromise = null; + + const readyRoot = getRoot(); + const readyVideoElement = getVideoElement(); + + if (ensureVideoElement && !readyVideoElement) { + throw new Error("Video modal video element is missing after load()."); + } + + if (readyVideoElement) { + this.modalVideo = readyVideoElement; + } + + return { + root: readyRoot, + videoElement: readyVideoElement, + }; + } + extractDTagValue(tags) { if (!Array.isArray(tags)) { return ""; @@ -6936,26 +7024,19 @@ class Application { this.cancelPendingViewLogging(); let modalVideoEl = this.modalVideo; - if (!modalVideoEl && this.videoModal) { - modalVideoEl = this.videoModal.getVideoElement(); - } - if (!modalVideoEl && this.videoModal) { - const maybeLoader = this.videoModal.load; - if (typeof maybeLoader === "function") { - try { - await maybeLoader.call(this.videoModal); - } catch (error) { - this.log( - "[playVideoWithFallback] Failed to load video modal before playback:", - error - ); - this.showError("Could not prepare the video player. Please try again."); - return { source: null, error }; - } - modalVideoEl = this.modalVideo; - if (!modalVideoEl && this.videoModal) { - modalVideoEl = this.videoModal.getVideoElement(); - } + if (!modalVideoEl) { + try { + const { videoElement } = await this.ensureVideoModalReady({ + ensureVideoElement: true, + }); + modalVideoEl = videoElement; + } catch (error) { + this.log( + "[playVideoWithFallback] Failed to load video modal before playback:", + error + ); + this.showError("Could not prepare the video player. Please try again."); + return { source: null, error }; } } @@ -7320,7 +7401,6 @@ class Application { ""; this.showModalWithPoster(this.currentVideo); - this.applyModalLoadingPoster(); const playbackPromise = this.playVideoWithFallback({ url: trimmedUrl, @@ -7449,6 +7529,8 @@ class Application { }); } + this.showModalWithPoster(this.currentVideo); + const urlObj = new URL(window.location.href); urlObj.searchParams.delete("v"); const cleaned = `${urlObj.pathname}${urlObj.search}${urlObj.hash}`; @@ -7616,6 +7698,7 @@ class Application { destroy() { this.clearActiveIntervals(); this.teardownModalViewCountSubscription(); + this.videoModalReadyPromise = null; if (typeof this.unsubscribeFromPubkeyState === "function") { try {