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 ? );