fix(site): stabilize media and notification runtime
This commit is contained in:
@@ -12,7 +12,9 @@
|
||||
const MEDIA_HANDLE_BOUND_FLAG = "__nodedcMediaHandleBound";
|
||||
const DOCK_LINK_BOUND_FLAG = "__nodedcDockLinkBound";
|
||||
const ORDERED_SLIDER_BOUND_FLAG = "__nodedcOrderedSliderBound";
|
||||
const LAZY_MEDIA_VIDEO_BOUND_FLAG = "__nodedcLazyMediaVideoBound";
|
||||
const SCROLL_HEIGHT_SYNC_FLAG = "__nodedcScrollHeightSyncBound";
|
||||
const NOTIFICATION_STACK_BOUND_FLAG = "__nodedcNotificationStackBound";
|
||||
const SCROLL_HEIGHT_SYNC_WARMUP_MS = 14000;
|
||||
const SCROLL_HEIGHT_SYNC_INTERVAL_MS = 90;
|
||||
const IMAGE_EXTENSIONS = new Set(["avif", "gif", "jpeg", "jpg", "png", "svg", "webp"]);
|
||||
@@ -384,6 +386,75 @@
|
||||
document.querySelectorAll('[data-module="dock"] a[data-app]').forEach(bindDockLink);
|
||||
}
|
||||
|
||||
function refreshNotificationStack(wrapper) {
|
||||
const notifications = Array.from(wrapper.querySelectorAll(':scope > [data-module="notification"]'));
|
||||
const firstOffset = notifications[0]?.offsetTop || 0;
|
||||
const collapsedHeight = (notifications[0]?.offsetHeight || 0) + Math.min(Math.max(notifications.length - 1, 0), 3) * 5;
|
||||
|
||||
wrapper.dataset.notificationStackReady = "true";
|
||||
wrapper.style.setProperty("--notification-count", String(notifications.length));
|
||||
wrapper.style.setProperty("--notification-collapsed-height", `${collapsedHeight}px`);
|
||||
|
||||
notifications.forEach((notification, index) => {
|
||||
const naturalOffset = notification.offsetTop - firstOffset;
|
||||
const deckOffset = Math.min(index, 3) * 5;
|
||||
|
||||
notification.style.setProperty("--notification-stack-y", `${deckOffset - naturalOffset}px`);
|
||||
notification.style.setProperty("--notification-z", String(index + 1));
|
||||
});
|
||||
}
|
||||
|
||||
function scheduleNotificationStackRefresh(wrapper) {
|
||||
if (wrapper.__nodedcNotificationStackFrame) return;
|
||||
|
||||
wrapper.__nodedcNotificationStackFrame = window.requestAnimationFrame(() => {
|
||||
wrapper.__nodedcNotificationStackFrame = 0;
|
||||
refreshNotificationStack(wrapper);
|
||||
});
|
||||
}
|
||||
|
||||
function bindNotificationStack(wrapper) {
|
||||
if (!wrapper || wrapper[NOTIFICATION_STACK_BOUND_FLAG]) return;
|
||||
|
||||
wrapper[NOTIFICATION_STACK_BOUND_FLAG] = true;
|
||||
scheduleNotificationStackRefresh(wrapper);
|
||||
|
||||
const mutationObserver =
|
||||
typeof MutationObserver === "function"
|
||||
? new MutationObserver(() => scheduleNotificationStackRefresh(wrapper))
|
||||
: null;
|
||||
|
||||
mutationObserver?.observe(wrapper, { childList: true });
|
||||
|
||||
wrapper.addEventListener("pointerdown", (event) => {
|
||||
if (event.target?.closest?.("[data-close]")) return;
|
||||
wrapper.dataset.notificationExpanded = "true";
|
||||
});
|
||||
|
||||
wrapper.addEventListener("mouseleave", () => {
|
||||
delete wrapper.dataset.notificationExpanded;
|
||||
wrapper.scrollTop = 0;
|
||||
scheduleNotificationStackRefresh(wrapper);
|
||||
});
|
||||
|
||||
document.addEventListener(
|
||||
"pointerdown",
|
||||
(event) => {
|
||||
if (wrapper.contains(event.target)) return;
|
||||
delete wrapper.dataset.notificationExpanded;
|
||||
wrapper.scrollTop = 0;
|
||||
},
|
||||
{ capture: true },
|
||||
);
|
||||
|
||||
window.addEventListener("resize", () => scheduleNotificationStackRefresh(wrapper));
|
||||
window.setTimeout(() => scheduleNotificationStackRefresh(wrapper), 120);
|
||||
}
|
||||
|
||||
function bindNotificationStacks() {
|
||||
document.querySelectorAll('[data-module="notification-w"]').forEach(bindNotificationStack);
|
||||
}
|
||||
|
||||
function mediaExtension(src) {
|
||||
const cleanSrc = String(src || "").split(/[?#]/)[0];
|
||||
const match = /\.([a-z0-9]+)$/i.exec(cleanSrc);
|
||||
@@ -407,7 +478,7 @@
|
||||
|
||||
const video = document.createElement("video");
|
||||
video.className = "video-el";
|
||||
video.dataset.module = "video-handle";
|
||||
video.dataset.lazyVideo = "";
|
||||
video.src = src;
|
||||
video.muted = true;
|
||||
video.loop = true;
|
||||
@@ -458,8 +529,53 @@
|
||||
document.querySelectorAll("[data-adaptive-video] video").forEach(bindAdaptiveVideo);
|
||||
}
|
||||
|
||||
function playLazyVideo(video) {
|
||||
video.preload = "auto";
|
||||
const playPromise = video.play();
|
||||
|
||||
if (playPromise && typeof playPromise.catch === "function") {
|
||||
playPromise.catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
function bindLazyMediaVideo(video) {
|
||||
if (!video || video[LAZY_MEDIA_VIDEO_BOUND_FLAG]) return;
|
||||
|
||||
video[LAZY_MEDIA_VIDEO_BOUND_FLAG] = true;
|
||||
video.muted = true;
|
||||
video.loop = true;
|
||||
video.playsInline = true;
|
||||
video.preload = "none";
|
||||
video.removeAttribute("autoplay");
|
||||
|
||||
if (typeof IntersectionObserver !== "function") {
|
||||
playLazyVideo(video);
|
||||
return;
|
||||
}
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
playLazyVideo(video);
|
||||
} else {
|
||||
video.pause();
|
||||
}
|
||||
});
|
||||
},
|
||||
{ rootMargin: "80px 0px", threshold: 0.01 },
|
||||
);
|
||||
|
||||
observer.observe(video);
|
||||
}
|
||||
|
||||
function bindLazyMediaVideos() {
|
||||
document.querySelectorAll("video[data-lazy-video]").forEach(bindLazyMediaVideo);
|
||||
}
|
||||
|
||||
bindScrollHeightSync();
|
||||
hydrateMediaHandles();
|
||||
bindNotificationStacks();
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener(
|
||||
@@ -470,7 +586,9 @@
|
||||
bindInteractiveScroll();
|
||||
bindOrderedSliders();
|
||||
bindDockLinks();
|
||||
bindNotificationStacks();
|
||||
bindAdaptiveVideos();
|
||||
bindLazyMediaVideos();
|
||||
scheduleScrollHeightSync(true);
|
||||
},
|
||||
{ once: true },
|
||||
@@ -481,7 +599,9 @@
|
||||
bindInteractiveScroll();
|
||||
bindOrderedSliders();
|
||||
bindDockLinks();
|
||||
bindNotificationStacks();
|
||||
bindAdaptiveVideos();
|
||||
bindLazyMediaVideos();
|
||||
scheduleScrollHeightSync(true);
|
||||
}
|
||||
|
||||
@@ -491,14 +611,18 @@
|
||||
bindInteractiveScroll();
|
||||
bindOrderedSliders();
|
||||
bindDockLinks();
|
||||
bindNotificationStacks();
|
||||
bindAdaptiveVideos();
|
||||
bindLazyMediaVideos();
|
||||
scheduleScrollHeightSync(true);
|
||||
window.setTimeout(() => {
|
||||
hydrateMediaHandles();
|
||||
bindInteractiveScroll();
|
||||
bindOrderedSliders();
|
||||
bindDockLinks();
|
||||
bindNotificationStacks();
|
||||
bindAdaptiveVideos();
|
||||
bindLazyMediaVideos();
|
||||
scheduleScrollHeightSync(true);
|
||||
}, 500);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user