diff --git a/js/app/components/provider/provider.tsx b/js/app/components/provider/provider.tsx index 2c8a59972..211c8f690 100644 --- a/js/app/components/provider/provider.tsx +++ b/js/app/components/provider/provider.tsx @@ -1,4 +1,5 @@ import { LinkingOptions } from "@react-navigation/native"; +import { VideoHostProvider } from "@streamplace/components/src/components/mobile-player/video-host"; import React, { useEffect } from "react"; import { SafeAreaProvider } from "react-native-safe-area-context"; import SharedProvider from "./provider.shared"; @@ -20,7 +21,10 @@ export default function Provider({ }, []); return ( - {children} + + {/* Owns players' ); } diff --git a/js/components/src/components/mobile-player/fullscreen.tsx b/js/components/src/components/mobile-player/fullscreen.tsx index a977720ce..faeedda4f 100644 --- a/js/components/src/components/mobile-player/fullscreen.tsx +++ b/js/components/src/components/mobile-player/fullscreen.tsx @@ -13,6 +13,7 @@ import { import { View } from "../../components/ui"; import { AudioOnlyOverlay } from "./ui/audio-only-overlay"; import Video from "./video"; +import { HostedVideo } from "./video-host"; import VideoRetry from "./video-retry"; export function Fullscreen(props: { @@ -123,12 +124,14 @@ export function Fullscreen(props: { ref={divRef} style={{ width: "100%", height: "100%", overflow: "hidden" }} > - - + + + + element and the hls.js or WebRTC session +// feeding it) is rendered here, near the top of the app, instead of wherever +// the layout puts the player. The layout renders a slot and the +// video's DOM node is moved into it, so when a window resize moves the player +// to another branch of the layout, the new slot picks up the same playing +// video instead of loading it again. + +type Host = { + attach(store: PlayerStore, box: HTMLElement): void; + detach(store: PlayerStore, box: HTMLElement): void; + render(store: PlayerStore, content: ReactNode): void; +}; + +type Hosted = { + key: string; + container: HTMLDivElement; + release?: ReturnType; +}; + +const VideoHostContext = createContext(null); + +let nextKey = 0; + +export function VideoHostProvider({ children }: { children: ReactNode }) { + const parking = useRef(null); + const [hosted] = useState(() => new Map()); + const [contents, setContents] = useState>( + () => new Map(), + ); + + const host = useMemo( + () => ({ + attach(store, box) { + let h = hosted.get(store); + if (!h) { + const container = document.createElement("div"); + container.style.width = "100%"; + container.style.height = "100%"; + h = { key: String(nextKey++), container }; + hosted.set(store, h); + } + clearTimeout(h.release); + box.appendChild(h.container); + }, + detach(store, box) { + const h = hosted.get(store); + if (!h || h.container.parentNode !== box) return; + // A