diff --git a/src/components/Transition.tsx b/src/components/Transition.tsx index b46df9b1..c29846c3 100644 --- a/src/components/Transition.tsx +++ b/src/components/Transition.tsx @@ -4,7 +4,7 @@ import { } from "@headlessui/react"; import { Fragment, ReactNode } from "react"; -type TransitionAnimations = +export type TransitionAnimations = | "slide-down" | "slide-full-left" | "slide-full-right" diff --git a/src/components/overlays/OverlayAnchor.tsx b/src/components/overlays/OverlayAnchor.tsx index 1282f9ee..8bd36794 100644 --- a/src/components/overlays/OverlayAnchor.tsx +++ b/src/components/overlays/OverlayAnchor.tsx @@ -1,8 +1,4 @@ -import { ReactNode, useEffect, useRef } from "react"; - -export function createOverlayAnchorEvent(id: string): string { - return `__overlay::anchor::${id}`; -} +import { ReactNode } from "react"; interface Props { id: string; @@ -10,38 +6,5 @@ interface Props { } export function OverlayAnchor(props: Props) { - const ref = useRef(null); - const old = useRef(null); - - useEffect(() => { - if (!ref.current) return; - - let cancelled = false; - function render() { - if (cancelled) return; - - if (ref.current) { - const current = old.current; - const newer = ref.current.getBoundingClientRect(); - const newerStr = JSON.stringify(newer); - if (current !== newerStr) { - old.current = newerStr; - const evtStr = createOverlayAnchorEvent(props.id); - (window as any)[evtStr] = newer; - const evObj = new CustomEvent(createOverlayAnchorEvent(props.id), { - detail: newer, - }); - document.dispatchEvent(evObj); - } - } - window.requestAnimationFrame(render); - } - - window.requestAnimationFrame(render); - return () => { - cancelled = true; - }; - }, [props]); - - return
{props.children}
; + return
{props.children}
; } diff --git a/src/components/overlays/OverlayPage.tsx b/src/components/overlays/OverlayPage.tsx index 9d31360f..af4c7d03 100644 --- a/src/components/overlays/OverlayPage.tsx +++ b/src/components/overlays/OverlayPage.tsx @@ -1,29 +1,44 @@ import classNames from "classnames"; -import { ReactNode } from "react"; +import { ReactNode, useEffect, useMemo } from "react"; -import { Transition } from "@/components/Transition"; +import { Transition, TransitionAnimations } from "@/components/Transition"; import { useIsMobile } from "@/hooks/useIsMobile"; import { useInternalOverlayRouter } from "@/hooks/useOverlayRouter"; +import { useOverlayStore } from "@/stores/overlay/store"; interface Props { id: string; path: string; children?: ReactNode; className?: string; - height?: number; - width?: number; + height: number; + width: number; } export function OverlayPage(props: Props) { const router = useInternalOverlayRouter(props.id); const backwards = router.showBackwardsTransition(props.path); const show = router.isCurrentPage(props.path); - + const registerRoute = useOverlayStore((s) => s.registerRoute); + const path = useMemo(() => router.makePath(props.path), [props.path, router]); const { isMobile } = useIsMobile(); + + useEffect(() => { + registerRoute({ + id: path, + width: props.width, + height: props.height, + }); + }, [props.height, props.width, path, registerRoute]); + const width = !isMobile ? `${props.width}px` : "100%"; + let animation: TransitionAnimations = "none"; + if (backwards === "yes" || backwards === "no") + animation = backwards === "yes" ? "slide-full-left" : "slide-full-right"; + return ( (null); + const { isMobile } = useIsMobile(); + + const routes = useOverlayStore((s) => s.routes); + const router = useInternalOverlayRouter(props.id); + const routeMeta = useMemo( + () => routes[router.currentRoute ?? ""], + [routes, router] + ); + + const [dimensions, api] = useSpring( + () => ({ + from: { + height: `${routeMeta?.height ?? 0}px`, + width: isMobile ? "100%" : `${routeMeta?.width ?? 0}px`, + }, + config: { + easing: easings.linear, + }, + }), + [] + ); + + const currentState = useRef(null); + useEffect(() => { + const data = { + height: routeMeta?.height, + width: routeMeta?.width, + isMobile, + }; + const dataStr = JSON.stringify(data); + if (dataStr !== currentState.current) { + const oldData = currentState.current + ? JSON.parse(currentState.current) + : null; + currentState.current = dataStr; + if (data.isMobile) { + api.set({ + width: "100%", + }); + api({ + height: `${routeMeta?.height ?? 0}px`, + }); + } else if (oldData?.height === undefined && data.height !== undefined) { + api.set({ + height: `${routeMeta?.height ?? 0}px`, + width: `${routeMeta?.width ?? 0}px`, + }); + } else { + api({ + height: `${routeMeta?.height ?? 0}px`, + width: `${routeMeta?.width ?? 0}px`, + }); + } + } + }, [routeMeta?.height, routeMeta?.width, isMobile, api]); + + return ( + + {props.children} + + ); +} + export function OverlayRouter(props: OverlayRouterProps) { const { isMobile } = useIsMobile(); - const content = props.children; + const content = {props.children}; if (isMobile) return {content}; - return {content}; + return {content}; } diff --git a/src/components/overlays/positions/OverlayAnchorPosition.tsx b/src/components/overlays/positions/OverlayAnchorPosition.tsx index 80f98391..58fcaf27 100644 --- a/src/components/overlays/positions/OverlayAnchorPosition.tsx +++ b/src/components/overlays/positions/OverlayAnchorPosition.tsx @@ -1,78 +1,19 @@ import classNames from "classnames"; -import { ReactNode, useCallback, useEffect, useRef, useState } from "react"; - -import { createOverlayAnchorEvent } from "@/components/overlays/OverlayAnchor"; +import { ReactNode } from "react"; interface AnchorPositionProps { children?: ReactNode; - id: string; className?: string; } export function OverlayAnchorPosition(props: AnchorPositionProps) { - const ref = useRef(null); - const [left, setLeft] = useState(0); - const [top, setTop] = useState(0); - const [cardRect, setCardRect] = useState(null); - const [anchorRect, setAnchorRect] = useState(null); - - const calculateAndSetCoords = useCallback( - (anchor: DOMRect, card: DOMRect) => { - const buttonCenter = anchor.left + anchor.width / 2; - const bottomReal = window.innerHeight - anchor.bottom; - - setTop( - window.innerHeight - bottomReal - anchor.height - card.height - 30 - ); - setLeft( - Math.min( - buttonCenter - card.width / 2, - window.innerWidth - card.width - 30 - ) - ); - }, - [] - ); - - useEffect(() => { - if (!anchorRect || !cardRect) return; - calculateAndSetCoords(anchorRect, cardRect); - }, [anchorRect, calculateAndSetCoords, cardRect]); - - useEffect(() => { - if (!ref.current) return; - function checkBox() { - const divRect = ref.current?.getBoundingClientRect(); - setCardRect(divRect ?? null); - } - checkBox(); - const observer = new ResizeObserver(checkBox); - observer.observe(ref.current); - return () => { - observer.disconnect(); - }; - }, []); - - useEffect(() => { - const evtStr = createOverlayAnchorEvent(props.id); - if ((window as any)[evtStr]) setAnchorRect((window as any)[evtStr]); - function listen(ev: CustomEvent) { - setAnchorRect(ev.detail); - } - document.addEventListener(evtStr, listen as any); - return () => { - document.removeEventListener(evtStr, listen as any); - }; - }, [props.id]); - return (
diff --git a/src/components/overlays/positions/OverlayMobilePosition.tsx b/src/components/overlays/positions/OverlayMobilePosition.tsx index 68534714..4c4b387c 100644 --- a/src/components/overlays/positions/OverlayMobilePosition.tsx +++ b/src/components/overlays/positions/OverlayMobilePosition.tsx @@ -10,7 +10,7 @@ export function OverlayMobilePosition(props: MobilePositionProps) { return (
diff --git a/src/components/player/atoms/ProgressBar.tsx b/src/components/player/atoms/ProgressBar.tsx index e036b439..47ac1a2d 100644 --- a/src/components/player/atoms/ProgressBar.tsx +++ b/src/components/player/atoms/ProgressBar.tsx @@ -46,7 +46,7 @@ export function ProgressBar() { > {/* Pre-loaded content bar */}
{ + console.log("setting", percentage); setVolume(percentage); }, [setVolume] diff --git a/src/components/player/base/BottomControls.tsx b/src/components/player/base/BottomControls.tsx index 34054478..dce015e1 100644 --- a/src/components/player/base/BottomControls.tsx +++ b/src/components/player/base/BottomControls.tsx @@ -9,12 +9,12 @@ export function BottomControls(props: { {props.children} diff --git a/src/components/player/base/Container.tsx b/src/components/player/base/Container.tsx index 180f36b6..13f1371f 100644 --- a/src/components/player/base/Container.tsx +++ b/src/components/player/base/Container.tsx @@ -79,11 +79,13 @@ export function Container(props: PlayerProps) { }, []); return ( - +
- - - {props.children} - + + + + {props.children} + +
); } diff --git a/src/components/player/base/TopControls.tsx b/src/components/player/base/TopControls.tsx index cc3d4ccb..60022a4e 100644 --- a/src/components/player/base/TopControls.tsx +++ b/src/components/player/base/TopControls.tsx @@ -14,7 +14,7 @@ export function TopControls(props: { {props.children} diff --git a/src/components/player/display/base.ts b/src/components/player/display/base.ts index ef3a5a88..d86a0436 100644 --- a/src/components/player/display/base.ts +++ b/src/components/player/display/base.ts @@ -26,6 +26,7 @@ export function makeVideoElementDisplayInterface(): DisplayInterface { function setSource() { if (!videoElement || !source) return; videoElement.src = source.url; + videoElement.addEventListener("play", () => { emit("play", undefined); emit("loading", false); @@ -35,7 +36,7 @@ export function makeVideoElementDisplayInterface(): DisplayInterface { videoElement.addEventListener("canplay", () => emit("loading", false)); videoElement.addEventListener("waiting", () => emit("loading", true)); videoElement.addEventListener("volumechange", () => - emit("volumechange", videoElement?.volume ?? 0) + emit("volumechange", videoElement?.muted ? 0 : videoElement?.volume ?? 0) ); videoElement.addEventListener("timeupdate", () => emit("time", videoElement?.currentTime ?? 0) @@ -118,9 +119,16 @@ export function makeVideoElementDisplayInterface(): DisplayInterface { // clamp time between 0 and 1 let volume = Math.min(v, 1); volume = Math.max(0, volume); + videoElement.muted = volume === 0; // Muted attribute is always supported // update state - if (await canChangeVolume()) videoElement.volume = volume; + const isChangeable = await canChangeVolume(); + if (isChangeable) { + videoElement.volume = volume; + } else { + // For browsers where it can't be changed + emit("volumechange", volume === 0 ? 0 : 1); + } }, toggleFullscreen() { if (isFullscreen) { diff --git a/src/components/player/internals/VideoContainer.tsx b/src/components/player/internals/VideoContainer.tsx index f4b372ac..eaa4315f 100644 --- a/src/components/player/internals/VideoContainer.tsx +++ b/src/components/player/internals/VideoContainer.tsx @@ -37,7 +37,14 @@ function VideoElement() { } }, [display, videoEl]); - return