diff --git a/js/app.js b/js/app.js index a67d7d5b..5d79e85a 100644 --- a/js/app.js +++ b/js/app.js @@ -18,6 +18,7 @@ import { URL_FIRST_ENABLED } from "./constants.js"; import { trackVideoView } from "./analytics.js"; import { attachHealthBadges } from "./gridHealth.js"; import { attachUrlHealthBadges } from "./urlHealthObserver.js"; +import { updateVideoCardSourceVisibility } from "./utils/cardSourceVisibility.js"; import { ADMIN_INITIAL_EVENT_BLACKLIST } from "./lists.js"; import { userBlocks } from "./userBlocks.js"; import { relayManager } from "./relayManager.js"; @@ -4179,6 +4180,7 @@ class Application { const cardEl = badgeEl.closest(".card[data-video-id]"); if (cardEl) { cardEl.dataset.urlHealthState = status; + updateVideoCardSourceVisibility(cardEl); } badgeEl.setAttribute("aria-live", "polite"); badgeEl.setAttribute("role", status === "offline" ? "alert" : "status"); diff --git a/js/gridHealth.js b/js/gridHealth.js index 1842ae20..1dba3761 100644 --- a/js/gridHealth.js +++ b/js/gridHealth.js @@ -1,5 +1,6 @@ import { createCardObserver } from "./dom/cardObserver.js"; import { infoHashFromMagnet } from "./magnets.js"; +import { updateVideoCardSourceVisibility } from "./utils/cardSourceVisibility.js"; import { TorrentClient, torrentClient } from "./webtorrent.js"; const badgeUpdateListeners = new Set(); @@ -428,6 +429,8 @@ function setBadge(card, state, details) { delete card.dataset.streamHealthReason; } + updateVideoCardSourceVisibility(card); + const badge = card.querySelector(".torrent-health-badge"); if (!badge) { return; diff --git a/js/ui/components/VideoCard.js b/js/ui/components/VideoCard.js index 8f272d79..7e6cde84 100644 --- a/js/ui/components/VideoCard.js +++ b/js/ui/components/VideoCard.js @@ -1,4 +1,5 @@ import { normalizeDesignSystemContext } from "../../designSystem.js"; +import { updateVideoCardSourceVisibility } from "../../utils/cardSourceVisibility.js"; import positionFloatingPanel from "../utils/positionFloatingPanel.js"; import { createFloatingPanelStyles } from "../utils/floatingPanelStyles.js"; import { @@ -1388,6 +1389,8 @@ export class VideoCard { } else if (this.magnetProvided && this.magnetSupported) { this.root.dataset.torrentSupported = "true"; } + + updateVideoCardSourceVisibility(this.root); } applyPlaybackDatasets() { diff --git a/js/utils/cardSourceVisibility.js b/js/utils/cardSourceVisibility.js new file mode 100644 index 00000000..a3f91b1c --- /dev/null +++ b/js/utils/cardSourceVisibility.js @@ -0,0 +1,90 @@ +function resolveCardElement(cardLike) { + if (!cardLike) { + return null; + } + + const isElement = + typeof cardLike === "object" && cardLike !== null && cardLike.nodeType === 1; + + if (isElement) { + const element = cardLike; + if (typeof element.closest === "function") { + if (element.classList?.contains("card")) { + return element; + } + return element.closest(".card"); + } + return element.classList?.contains("card") ? element : null; + } + + if ( + typeof HTMLElement !== "undefined" && + cardLike instanceof HTMLElement && + typeof cardLike.closest === "function" + ) { + if (cardLike.classList.contains("card")) { + return cardLike; + } + return cardLike.closest(".card"); + } + + if (typeof cardLike === "object" && cardLike !== null) { + if (typeof cardLike.card === "object" && cardLike.card?.nodeType === 1) { + return resolveCardElement(cardLike.card); + } + if (typeof cardLike.root === "object" && cardLike.root?.nodeType === 1) { + return resolveCardElement(cardLike.root); + } + } + + return null; +} + +export function updateVideoCardSourceVisibility(cardLike) { + const card = resolveCardElement(cardLike); + if (!card) { + return; + } + + if (card.dataset.ownerIsViewer === "true") { + if (card.hidden) { + card.hidden = false; + } + if (card.dataset.sourceVisibility !== "visible") { + card.dataset.sourceVisibility = "visible"; + } + return; + } + + const cdnState = (card.dataset.urlHealthState || "").toLowerCase(); + const streamState = (card.dataset.streamHealthState || "").toLowerCase(); + + const cdnPending = !cdnState || cdnState === "checking"; + const streamPending = !streamState || streamState === "checking"; + + const cdnHealthy = cdnState === "healthy"; + const streamHealthy = streamState === "healthy"; + + const shouldHide = + !cdnHealthy && + !streamHealthy && + !cdnPending && + !streamPending; + + if (shouldHide) { + if (!card.hidden) { + card.hidden = true; + } + if (card.dataset.sourceVisibility !== "hidden") { + card.dataset.sourceVisibility = "hidden"; + } + return; + } + + if (card.hidden) { + card.hidden = false; + } + if (card.dataset.sourceVisibility !== "visible") { + card.dataset.sourceVisibility = "visible"; + } +}