diff --git a/js/app/components/mobile/desktop-ui.tsx b/js/app/components/mobile/desktop-ui.tsx
index c937caad..7050fbba 100644
--- a/js/app/components/mobile/desktop-ui.tsx
+++ b/js/app/components/mobile/desktop-ui.tsx
@@ -196,15 +196,24 @@ export function DesktopUi({
const hover = Gesture.Hover().onChange((_) => runOnJS(onPlayerHover)());
+ const togglePlayPause = usePlayerStore((x) => x.togglePlayPause);
+
+ const handleSingleClick = useCallback(() => {
+ togglePlayPause();
+ }, [togglePlayPause]);
+
const handleDoubleClick = useCallback(() => {
toggleFullscreen();
}, [toggleFullscreen]);
+ const singleTap = Gesture.Tap().onEnd(() => runOnJS(handleSingleClick)());
+
const doubleTap = Gesture.Tap()
.numberOfTaps(2)
.onEnd(() => runOnJS(handleDoubleClick)());
- const hoverAndTap = Gesture.Race(hover, doubleTap);
+ const tap = Gesture.Exclusive(doubleTap, singleTap);
+ const hoverAndTap = Gesture.Race(hover, tap);
const portalContainerID = "desktop-ui-dropdown-portal-" + playerId;
diff --git a/js/app/components/mobile/desktop-ui/top-controls.tsx b/js/app/components/mobile/desktop-ui/top-controls.tsx
index f5ff6659..5cc87c60 100644
--- a/js/app/components/mobile/desktop-ui/top-controls.tsx
+++ b/js/app/components/mobile/desktop-ui/top-controls.tsx
@@ -8,6 +8,7 @@ import {
useAvatar,
useCameraToggle,
useLivestreamStore,
+ useTitle,
zero,
} from "@streamplace/components";
import { Image } from "expo-image";
@@ -48,6 +49,8 @@ export function TopControlBar({
const isTinyScreen = width < 450;
const isSmallScreen = width < 600;
+ const title = useTitle();
+
// Get content warnings from segment
const segment = useLivestreamStore((x) => x.segment);
const contentWarnings =
@@ -103,13 +106,9 @@ export function TopControlBar({
]}
/>
-
-
- {profile?.handle}
-
- {!offline && }
+
+ {title}
+ {profile?.handle}
diff --git a/js/app/src/screens/vod-embed.tsx b/js/app/src/screens/vod-embed.tsx
index c17f4a5e..eb6dec83 100644
--- a/js/app/src/screens/vod-embed.tsx
+++ b/js/app/src/screens/vod-embed.tsx
@@ -6,13 +6,10 @@ import {
zero,
} from "@streamplace/components";
import { Redirect } from "components/aqlink";
-import { BottomControlBar } from "components/mobile/desktop-ui/bottom-controls";
-import { TopControlBar } from "components/mobile/desktop-ui/top-controls";
+import { DesktopUi } from "components/mobile/desktop-ui";
import { useEffect } from "react";
import { useStore } from "store";
-const { layout, h, w, position, px, py } = zero;
-
export default function VodEmbedScreen({
route,
}: {
@@ -37,56 +34,8 @@ export default function VodEmbedScreen({
-
-
-
- {}}
- embedded={true}
- />
-
-
-
- {}}
- showChat={false}
- />
-
-
-
+
+
diff --git a/js/components/src/components/mobile-player/video.tsx b/js/components/src/components/mobile-player/video.tsx
index 768d3b4c..95ce7336 100644
--- a/js/components/src/components/mobile-player/video.tsx
+++ b/js/components/src/components/mobile-player/video.tsx
@@ -139,6 +139,7 @@ const VideoElement = forwardRef<
VideoProps & { videoRef?: React.RefObject }
>((props, ref) => {
const x = usePlayerStore((x) => x);
+ const mode = usePlayerStore((x) => x.mode);
const url = useStreamplaceStore((x) => x.url);
const playerEvent = usePlayerStore((x) => x.playerEvent);
const setMuteWasForced = usePlayerStore((x) => x.setMuteWasForced);
@@ -204,7 +205,9 @@ const VideoElement = forwardRef<
localVideoRef.current.play().catch((err) => {
console.log("error playing video", err.name);
if (err.name === "NotAllowedError") {
- if (localVideoRef.current) {
+ if (mode === "vod") {
+ setAutoplayFailed(true);
+ } else if (localVideoRef.current) {
console.log("Setting muted and retrying");
setMuted(true);
localVideoRef.current.muted = true;
diff --git a/js/components/src/components/vod/vod-player.tsx b/js/components/src/components/vod/vod-player.tsx
index 4361f31b..01d55963 100644
--- a/js/components/src/components/vod/vod-player.tsx
+++ b/js/components/src/components/vod/vod-player.tsx
@@ -2,20 +2,33 @@ import { useEffect } from "react";
import { View } from "react-native";
import { PlayerProvider } from "../../player-store/player-provider";
import { usePlayerStore } from "../../player-store/player-store";
+import { useMuted, useSetMuted } from "../../streamplace-store";
import Video from "../mobile-player/video";
export function VodPlayer({
src,
objectFit = "contain",
+ embedded,
+ muted: mutedProp,
+ pictureInPictureEnabled,
children,
}: {
src: string;
objectFit?: "contain" | "cover";
+ embedded?: boolean;
+ muted?: boolean;
+ pictureInPictureEnabled?: boolean;
children?: React.ReactNode;
}) {
return (
-
+
{children}
@@ -25,24 +38,53 @@ export function VodPlayer({
function VodPlayerInner({
src,
objectFit,
+ embedded,
+ muted: mutedProp,
+ pictureInPictureEnabled,
children,
}: {
src: string;
objectFit: "contain" | "cover";
+ embedded: boolean;
+ muted?: boolean;
+ pictureInPictureEnabled?: boolean;
children?: React.ReactNode;
}) {
const setSrc = usePlayerStore((x) => x.setSrc);
const setMode = usePlayerStore((x) => x.setMode);
+ const setEmbedded = usePlayerStore((x) => x.setEmbedded);
const storeSrc = usePlayerStore((x) => x.src);
+ const setMuted = useSetMuted();
+ const muted = useMuted();
+
useEffect(() => {
setMode("vod");
setSrc(src);
}, [src, setMode, setSrc]);
+ useEffect(() => {
+ setEmbedded(embedded);
+ }, [embedded, setEmbedded]);
+
+ useEffect(() => {
+ if (mutedProp !== undefined) {
+ let wasMuted: boolean | null = muted;
+ setTimeout(() => setMuted(mutedProp), 200);
+ return () => {
+ if (wasMuted !== null) setMuted(wasMuted);
+ };
+ }
+ }, [mutedProp]);
+
return (
- {storeSrc === src ? : null}
+ {storeSrc === src ? (
+
+ ) : null}
{children}
);