diff --git a/front/packages/ui/src/player/components/hover.tsx b/front/packages/ui/src/player/components/hover.tsx index 3c92dba7..3f5e37b3 100644 --- a/front/packages/ui/src/player/components/hover.tsx +++ b/front/packages/ui/src/player/components/hover.tsx @@ -77,7 +77,7 @@ export const Hover = ({ show: boolean; } & ViewProps) => { // TODO animate show - const opacity = !show && { opacity: 0 }; + const opacity = !show && (Platform.OS === "web" ? { opacity: 0 } : { display: "none" as const}); return ( {({ css }) => ( diff --git a/front/packages/ui/src/player/index.tsx b/front/packages/ui/src/player/index.tsx index 7ab5325b..a638554d 100644 --- a/front/packages/ui/src/player/index.tsx +++ b/front/packages/ui/src/player/index.tsx @@ -21,7 +21,7 @@ import { QueryIdentifier, QueryPage, WatchItem, WatchItemP, useFetch } from "@kyoo/models"; import { Head } from "@kyoo/primitives"; import { useState, useEffect, ComponentProps } from "react"; -import { Platform, Pressable, StyleSheet, View } from "react-native"; +import { Platform, Pressable, PressableProps, StyleSheet, View } from "react-native"; import { useTranslation } from "react-i18next"; import { useRouter } from "solito/router"; import { useAtom } from "jotai"; @@ -59,6 +59,17 @@ const mapData = ( }; }; +const PressView = + Platform.OS === "web" + ? View + : ({ + onPointerDown, + onMobilePress, + ...props + }: PressableProps & { onMobilePress: PressableProps["onPress"] }) => ( + onMobilePress?.(e)} {...props} /> + ); + // Callback used to hide the controls when the mouse goes iddle. This is stored globally to clear the old timeout // if the mouse moves again (if this is stored as a state, the whole page is redrawn on mouse move) let mouseCallback: NodeJS.Timeout; @@ -148,9 +159,33 @@ export const Player: QueryPage<{ slug: string }> = ({ slug }) => { next={next} previous={previous} /> - { if (e.nativeEvent.pointerType === "mouse") setMouseMoved(false) }} + onMobilePress={(e) => { + e.preventDefault(); + displayControls ? setMouseMoved(false) : show(); + }} + onStartShouldSetResponder={(e) => true} + onPointerDown={(e) => { + e.preventDefault(); + if (e.nativeEvent.pointerType !== "mouse") { + displayControls ? setMouseMoved(false) : show(); + return; + } + touchCount++; + if (touchCount == 2) { + touchCount = 0; + setFullscreen(!isFullscreen); + clearTimeout(touchTimeout); + } else + touchTimeout = setTimeout(() => { + touchCount = 0; + }, 400); + setPlay(!isPlaying); + }} + onPointerLeave={(e) => { + if (e.nativeEvent.pointerType === "mouse") setMouseMoved(false); + }} {...css({ flexGrow: 1, bg: "black", @@ -158,51 +193,33 @@ export const Player: QueryPage<{ slug: string }> = ({ slug }) => { cursor: displayControls ? "unset" : "none", })} > - { - if (e.nativeEvent.pointerType !== "mouse") { - displayControls ? setMouseMoved(false) : show(); - return; - } - e.preventDefault(); - touchCount++; - if (touchCount == 2) { - touchCount = 0; - setFullscreen(!isFullscreen); - clearTimeout(touchTimeout); - } else - touchTimeout = setTimeout(() => { - touchCount = 0; - }, 400); - setPlay(!isPlaying); + + /> { if (e.nativeEvent.pointerType === "mouse") setHover(true) }} - onPointerLeave={(e) => { if (e.nativeEvent.pointerType === "mouse") setHover(false) }} + onPointerEnter={(e) => { + if (e.nativeEvent.pointerType === "mouse") setHover(true); + }} + onPointerLeave={(e) => { + if (e.nativeEvent.pointerType === "mouse") setHover(false); + }} onPointerDown={(e) => { - // also handle touch here because if we dont, the area where the hover should be will catch touches - // without openning the hover. - if (e.nativeEvent.pointerType !== "mouse") - displayControls ? setMouseMoved(false) : show(); + // Prevent clicks on the hover to play/pause. + e.preventDefault(); + e.stopPropagation(); }} onMenuOpen={() => setMenuOpen(true)} onMenuClose={() => { @@ -212,7 +229,7 @@ export const Player: QueryPage<{ slug: string }> = ({ slug }) => { }} show={displayControls} /> - + ); }; diff --git a/front/packages/ui/src/player/state.tsx b/front/packages/ui/src/player/state.tsx index 175bd6c1..594f415b 100644 --- a/front/packages/ui/src/player/state.tsx +++ b/front/packages/ui/src/player/state.tsx @@ -20,7 +20,7 @@ import { Track, WatchItem, Font } from "@kyoo/models"; import { atom, useAtom, useAtomValue, useSetAtom } from "jotai"; -import { memo, useEffect, useLayoutEffect, useRef, useState } from "react"; +import { ElementRef, memo, useEffect, useLayoutEffect, useRef, useState } from "react"; import NativeVideo, { VideoProperties as VideoProps } from "./video"; import { Platform } from "react-native"; @@ -84,7 +84,7 @@ export const Video = memo(function _Video({ setError: (error: string | undefined) => void; fonts?: Font[]; } & Partial) { - const ref = useRef(null); + const ref = useRef | null>(null); const [isPlaying, setPlay] = useAtom(playAtom); const setLoad = useSetAtom(loadAtom); const [source, setSource] = useState(null); diff --git a/front/packages/ui/src/player/video.tsx b/front/packages/ui/src/player/video.tsx index 0d886693..f41d6371 100644 --- a/front/packages/ui/src/player/video.tsx +++ b/front/packages/ui/src/player/video.tsx @@ -33,7 +33,7 @@ export * from "react-native-video"; import { Font } from "@kyoo/models"; import { IconButton, Menu } from "@kyoo/primitives"; -import { ComponentProps, useRef } from "react"; +import { ComponentProps, forwardRef } from "react"; import { atom, useAtom, useAtomValue, useSetAtom } from "jotai"; import NativeVideo, { OnLoadData } from "react-native-video"; import { useTranslation } from "react-i18next"; @@ -43,17 +43,17 @@ const infoAtom = atom(null); const videoAtom = atom(0); const audioAtom = atom(0); -const Video = ({ onLoad, ...props }: ComponentProps) => { - const player = useRef(null); +const Video = forwardRef>(function _NativeVideo( + { onLoad, ...props }, + ref, +) { const setInfo = useSetAtom(infoAtom); const video = useAtomValue(videoAtom); const audio = useAtomValue(audioAtom); return ( { - player.current = ref; - }} + ref={ref} onLoad={(info) => { setInfo(info); onLoad?.(info); @@ -63,7 +63,7 @@ const Video = ({ onLoad, ...props }: ComponentProps) => { {...props} /> ); -}; +}); export default Video;