Fix hover issues on android

This commit is contained in:
Zoe Roux 2023-06-13 10:24:49 +09:00
parent 4993ae50fe
commit 72e74cea32
4 changed files with 70 additions and 53 deletions

View File

@ -77,7 +77,7 @@ export const Hover = ({
show: boolean; show: boolean;
} & ViewProps) => { } & ViewProps) => {
// TODO animate show // TODO animate show
const opacity = !show && { opacity: 0 }; const opacity = !show && (Platform.OS === "web" ? { opacity: 0 } : { display: "none" as const});
return ( return (
<ContrastArea mode="dark"> <ContrastArea mode="dark">
{({ css }) => ( {({ css }) => (

View File

@ -21,7 +21,7 @@
import { QueryIdentifier, QueryPage, WatchItem, WatchItemP, useFetch } from "@kyoo/models"; import { QueryIdentifier, QueryPage, WatchItem, WatchItemP, useFetch } from "@kyoo/models";
import { Head } from "@kyoo/primitives"; import { Head } from "@kyoo/primitives";
import { useState, useEffect, ComponentProps } from "react"; 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 { useTranslation } from "react-i18next";
import { useRouter } from "solito/router"; import { useRouter } from "solito/router";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
@ -59,6 +59,17 @@ const mapData = (
}; };
}; };
const PressView =
Platform.OS === "web"
? View
: ({
onPointerDown,
onMobilePress,
...props
}: PressableProps & { onMobilePress: PressableProps["onPress"] }) => (
<Pressable focusable={false} onPress={(e) => onMobilePress?.(e)} {...props} />
);
// Callback used to hide the controls when the mouse goes iddle. This is stored globally to clear the old timeout // 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) // if the mouse moves again (if this is stored as a state, the whole page is redrawn on mouse move)
let mouseCallback: NodeJS.Timeout; let mouseCallback: NodeJS.Timeout;
@ -148,9 +159,33 @@ export const Player: QueryPage<{ slug: string }> = ({ slug }) => {
next={next} next={next}
previous={previous} previous={previous}
/> />
<View <PressView
focusable={false} focusable={false}
onPointerLeave={(e) => { 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({ {...css({
flexGrow: 1, flexGrow: 1,
bg: "black", bg: "black",
@ -158,51 +193,33 @@ export const Player: QueryPage<{ slug: string }> = ({ slug }) => {
cursor: displayControls ? "unset" : "none", cursor: displayControls ? "unset" : "none",
})} })}
> >
<View <Video
onPointerDown={(e) => { links={data?.link}
if (e.nativeEvent.pointerType !== "mouse") { setError={setPlaybackError}
displayControls ? setMouseMoved(false) : show(); fonts={data?.fonts}
return; onEnd={() => {
} if (!data) return;
e.preventDefault(); if (data.isMovie) router.push(`/movie/${data.slug}`);
touchCount++; else
if (touchCount == 2) { router.push(
touchCount = 0; data.nextEpisode ? `/watch/${data.nextEpisode.slug}` : `/show/${data.showSlug}`,
setFullscreen(!isFullscreen); );
clearTimeout(touchTimeout);
} else
touchTimeout = setTimeout(() => {
touchCount = 0;
}, 400);
setPlay(!isPlaying);
}} }}
{...css(StyleSheet.absoluteFillObject)} {...css(StyleSheet.absoluteFillObject)}
> />
<Video
links={data?.link}
setError={setPlaybackError}
fonts={data?.fonts}
onEnd={() => {
if (!data) return;
if (data.isMovie) router.push(`/movie/${data.slug}`);
else
router.push(
data.nextEpisode ? `/watch/${data.nextEpisode.slug}` : `/show/${data.showSlug}`,
);
}}
{...css(StyleSheet.absoluteFillObject)}
/>
</View>
<LoadingIndicator /> <LoadingIndicator />
<Hover <Hover
{...mapData(data, previous, next)} {...mapData(data, previous, next)}
onPointerEnter={(e) => { if (e.nativeEvent.pointerType === "mouse") setHover(true) }} onPointerEnter={(e) => {
onPointerLeave={(e) => { if (e.nativeEvent.pointerType === "mouse") setHover(false) }} if (e.nativeEvent.pointerType === "mouse") setHover(true);
}}
onPointerLeave={(e) => {
if (e.nativeEvent.pointerType === "mouse") setHover(false);
}}
onPointerDown={(e) => { onPointerDown={(e) => {
// also handle touch here because if we dont, the area where the hover should be will catch touches // Prevent clicks on the hover to play/pause.
// without openning the hover. e.preventDefault();
if (e.nativeEvent.pointerType !== "mouse") e.stopPropagation();
displayControls ? setMouseMoved(false) : show();
}} }}
onMenuOpen={() => setMenuOpen(true)} onMenuOpen={() => setMenuOpen(true)}
onMenuClose={() => { onMenuClose={() => {
@ -212,7 +229,7 @@ export const Player: QueryPage<{ slug: string }> = ({ slug }) => {
}} }}
show={displayControls} show={displayControls}
/> />
</View> </PressView>
</> </>
); );
}; };

View File

@ -20,7 +20,7 @@
import { Track, WatchItem, Font } from "@kyoo/models"; import { Track, WatchItem, Font } from "@kyoo/models";
import { atom, useAtom, useAtomValue, useSetAtom } from "jotai"; 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 NativeVideo, { VideoProperties as VideoProps } from "./video";
import { Platform } from "react-native"; import { Platform } from "react-native";
@ -84,7 +84,7 @@ export const Video = memo(function _Video({
setError: (error: string | undefined) => void; setError: (error: string | undefined) => void;
fonts?: Font[]; fonts?: Font[];
} & Partial<VideoProps>) { } & Partial<VideoProps>) {
const ref = useRef<NativeVideo | null>(null); const ref = useRef<ElementRef<typeof NativeVideo> | null>(null);
const [isPlaying, setPlay] = useAtom(playAtom); const [isPlaying, setPlay] = useAtom(playAtom);
const setLoad = useSetAtom(loadAtom); const setLoad = useSetAtom(loadAtom);
const [source, setSource] = useState<string | null>(null); const [source, setSource] = useState<string | null>(null);

View File

@ -33,7 +33,7 @@ export * from "react-native-video";
import { Font } from "@kyoo/models"; import { Font } from "@kyoo/models";
import { IconButton, Menu } from "@kyoo/primitives"; import { IconButton, Menu } from "@kyoo/primitives";
import { ComponentProps, useRef } from "react"; import { ComponentProps, forwardRef } from "react";
import { atom, useAtom, useAtomValue, useSetAtom } from "jotai"; import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
import NativeVideo, { OnLoadData } from "react-native-video"; import NativeVideo, { OnLoadData } from "react-native-video";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@ -43,17 +43,17 @@ const infoAtom = atom<OnLoadData | null>(null);
const videoAtom = atom(0); const videoAtom = atom(0);
const audioAtom = atom(0); const audioAtom = atom(0);
const Video = ({ onLoad, ...props }: ComponentProps<typeof NativeVideo>) => { const Video = forwardRef<NativeVideo, ComponentProps<typeof NativeVideo>>(function _NativeVideo(
const player = useRef<NativeVideo | null>(null); { onLoad, ...props },
ref,
) {
const setInfo = useSetAtom(infoAtom); const setInfo = useSetAtom(infoAtom);
const video = useAtomValue(videoAtom); const video = useAtomValue(videoAtom);
const audio = useAtomValue(audioAtom); const audio = useAtomValue(audioAtom);
return ( return (
<NativeVideo <NativeVideo
ref={(ref) => { ref={ref}
player.current = ref;
}}
onLoad={(info) => { onLoad={(info) => {
setInfo(info); setInfo(info);
onLoad?.(info); onLoad?.(info);
@ -63,7 +63,7 @@ const Video = ({ onLoad, ...props }: ComponentProps<typeof NativeVideo>) => {
{...props} {...props}
/> />
); );
}; });
export default Video; export default Video;