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