mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Update player's layout for tv
This commit is contained in:
parent
1bba1eb02a
commit
0e3d87a9ca
@ -19,8 +19,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import { GestureResponderEvent, Platform, View } from "react-native";
|
import { GestureResponderEvent, Platform, Pressable, View } from "react-native";
|
||||||
|
import { useTVEventHandler } from "@kyoo/primitives/tv";
|
||||||
import { px, percent, Stylable, useYoshiki } from "yoshiki/native";
|
import { px, percent, Stylable, useYoshiki } from "yoshiki/native";
|
||||||
|
import { focusReset } from "./utils";
|
||||||
|
|
||||||
export const Slider = ({
|
export const Slider = ({
|
||||||
progress,
|
progress,
|
||||||
@ -49,7 +51,6 @@ export const Slider = ({
|
|||||||
const [isHover, setHover] = useState(false);
|
const [isHover, setHover] = useState(false);
|
||||||
const [isFocus, setFocus] = useState(false);
|
const [isFocus, setFocus] = useState(false);
|
||||||
const smallBar = !(isSeeking || isHover || isFocus);
|
const smallBar = !(isSeeking || isHover || isFocus);
|
||||||
|
|
||||||
const ts = (value: number) => px(value * size);
|
const ts = (value: number) => px(value * size);
|
||||||
|
|
||||||
const change = (event: GestureResponderEvent) => {
|
const change = (event: GestureResponderEvent) => {
|
||||||
@ -61,16 +62,17 @@ export const Slider = ({
|
|||||||
setProgress(Math.max(0, Math.min(locationX / layout.width, 1)) * max);
|
setProgress(Math.max(0, Math.min(locationX / layout.width, 1)) * max);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useTVEventHandler((e) => {
|
||||||
|
if (!isFocus) return;
|
||||||
|
|
||||||
|
if (e.eventType === "left" && e.eventKeyAction === 0) setProgress(Math.max(progress - 5, 0));
|
||||||
|
if (e.eventType === "right" && e.eventKeyAction === 0) setProgress(Math.max(progress + 5, 0));
|
||||||
|
});
|
||||||
|
|
||||||
|
const Container = Platform.isTV ? Pressable : View;
|
||||||
return (
|
return (
|
||||||
<View
|
<Container
|
||||||
ref={ref}
|
ref={ref}
|
||||||
// @ts-ignore Web only
|
|
||||||
onMouseEnter={() => setHover(true)}
|
|
||||||
// @ts-ignore Web only
|
|
||||||
onMouseLeave={() => setHover(false)}
|
|
||||||
focusable
|
|
||||||
onFocus={() => setFocus(true)}
|
|
||||||
onBlur={() => setFocus(false)}
|
|
||||||
onStartShouldSetResponder={() => true}
|
onStartShouldSetResponder={() => true}
|
||||||
onResponderGrant={() => {
|
onResponderGrant={() => {
|
||||||
setSeek(true);
|
setSeek(true);
|
||||||
@ -85,6 +87,7 @@ export const Slider = ({
|
|||||||
onLayout={() =>
|
onLayout={() =>
|
||||||
ref.current?.measure((_, __, width, ___, pageX) => setLayout({ width: width, x: pageX }))
|
ref.current?.measure((_, __, width, ___, pageX) => setLayout({ width: width, x: pageX }))
|
||||||
}
|
}
|
||||||
|
// @ts-ignore Web only
|
||||||
onKeyDown={(e: KeyboardEvent) => {
|
onKeyDown={(e: KeyboardEvent) => {
|
||||||
switch (e.code) {
|
switch (e.code) {
|
||||||
case "ArrowLeft":
|
case "ArrowLeft":
|
||||||
@ -107,10 +110,16 @@ export const Slider = ({
|
|||||||
// @ts-ignore Web only
|
// @ts-ignore Web only
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
focus: {
|
focus: {
|
||||||
shadowRadius: 0,
|
self: focusReset,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
props,
|
{
|
||||||
|
onFocus: () => setFocus(true),
|
||||||
|
onBlur: () => setFocus(false),
|
||||||
|
onMouseEnter: () => setHover(true),
|
||||||
|
onMouseLeave: () => setHover(false),
|
||||||
|
...props,
|
||||||
|
},
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
@ -174,7 +183,7 @@ export const Slider = ({
|
|||||||
position: "absolute",
|
position: "absolute",
|
||||||
top: 0,
|
top: 0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
marginY: ts(Platform.OS === "android" ? -0.5 : 0.5),
|
marginY: ts(Platform.OS === "android" && !Platform.isTV ? -0.5 : 0.5),
|
||||||
bg: (theme) => theme.accent,
|
bg: (theme) => theme.accent,
|
||||||
width: ts(2),
|
width: ts(2),
|
||||||
height: ts(2),
|
height: ts(2),
|
||||||
@ -190,6 +199,6 @@ export const Slider = ({
|
|||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</View>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
23
front/packages/primitives/tv.ts
Normal file
23
front/packages/primitives/tv.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import "./tvos-type.d.ts";
|
||||||
|
|
||||||
|
export { useTVEventHandler } from "react-native";
|
21
front/packages/primitives/tv.web.ts
Normal file
21
front/packages/primitives/tv.web.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const useTVEventHandler = () => {};
|
178
front/packages/primitives/tvos-type.d.ts
vendored
Normal file
178
front/packages/primitives/tvos-type.d.ts
vendored
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { ViewProps, ScrollViewProps } from "react-native";
|
||||||
|
|
||||||
|
declare module "react-native" {
|
||||||
|
interface ViewProps {
|
||||||
|
/**
|
||||||
|
* TV next focus down (see documentation for the View component).
|
||||||
|
*/
|
||||||
|
nextFocusDown?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TV next focus forward (see documentation for the View component).
|
||||||
|
*
|
||||||
|
* @platform android
|
||||||
|
*/
|
||||||
|
nextFocusForward?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TV next focus left (see documentation for the View component).
|
||||||
|
*/
|
||||||
|
nextFocusLeft?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TV next focus right (see documentation for the View component).
|
||||||
|
*/
|
||||||
|
nextFocusRight?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TV next focus up (see documentation for the View component).
|
||||||
|
*/
|
||||||
|
nextFocusUp?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useTVEventHandler: (handleEvent: (event: HWEvent) => void) => void;
|
||||||
|
|
||||||
|
export const TVEventControl: {
|
||||||
|
enableTVMenuKey(): void;
|
||||||
|
disableTVMenuKey(): void;
|
||||||
|
enableTVPanGesture(): void;
|
||||||
|
disableTVPanGesture(): void;
|
||||||
|
enableGestureHandlersCancelTouches(): void;
|
||||||
|
disableGestureHandlersCancelTouches(): void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type HWEvent = {
|
||||||
|
eventType:
|
||||||
|
| "up"
|
||||||
|
| "down"
|
||||||
|
| "right"
|
||||||
|
| "left"
|
||||||
|
| "longUp"
|
||||||
|
| "longDown"
|
||||||
|
| "longRight"
|
||||||
|
| "longLeft"
|
||||||
|
| "blur"
|
||||||
|
| "focus"
|
||||||
|
| "pan"
|
||||||
|
| string;
|
||||||
|
eventKeyAction?: -1 | 1 | 0 | number;
|
||||||
|
tag?: number;
|
||||||
|
body?: {
|
||||||
|
state: "Began" | "Changed" | "Ended";
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
velocityx: number;
|
||||||
|
velocityy: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export class TVEventHandler {
|
||||||
|
enable<T extends React.Component<unknown>>(
|
||||||
|
component?: T,
|
||||||
|
callback?: (component: T, data: HWEvent) => void,
|
||||||
|
): void;
|
||||||
|
disable(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FocusGuideProps extends ViewProps {
|
||||||
|
/**
|
||||||
|
* If the view should be "visible". display "flex" if visible, otherwise "none". Defaults to
|
||||||
|
* true
|
||||||
|
*/
|
||||||
|
enabled?: boolean;
|
||||||
|
/**
|
||||||
|
* Array of `Component`s to register as destinations with `UIFocusGuide`
|
||||||
|
*/
|
||||||
|
destinations?: (null | number | React.Component<any, any> | React.ComponentClass<any>)[];
|
||||||
|
/**
|
||||||
|
* @deprecated Don't use it, no longer necessary.
|
||||||
|
*/
|
||||||
|
safePadding?: "both" | "vertical" | "horizontal" | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component provides support for Apple's `UIFocusGuide` API, to help ensure that focusable
|
||||||
|
* controls can be navigated to, even if they are not directly in line with other controls. An
|
||||||
|
* example is provided in `RNTester` that shows two different ways of using this component.
|
||||||
|
* https://github.com/react-native-tvos/react-native-tvos/blob/tvos-v0.63.4/RNTester/js/examples/TVFocusGuide/TVFocusGuideExample.js
|
||||||
|
*/
|
||||||
|
export class TVFocusGuideView extends React.Component<FocusGuideProps> {}
|
||||||
|
|
||||||
|
export interface TVTextScrollViewProps extends ScrollViewProps {
|
||||||
|
/**
|
||||||
|
* The duration of the scroll animation when a swipe is detected. Default value is 0.3 s
|
||||||
|
*/
|
||||||
|
scrollDuration?: number;
|
||||||
|
/**
|
||||||
|
* Scrolling distance when a swipe is detected Default value is half the visible height
|
||||||
|
* (vertical scroller) or width (horizontal scroller)
|
||||||
|
*/
|
||||||
|
pageSize?: number;
|
||||||
|
/**
|
||||||
|
* If true, will scroll to start when focus moves out past the beginning of the scroller
|
||||||
|
* Defaults to true
|
||||||
|
*/
|
||||||
|
snapToStart?: boolean;
|
||||||
|
/**
|
||||||
|
* If true, will scroll to end when focus moves out past the end of the scroller Defaults to
|
||||||
|
* true
|
||||||
|
*/
|
||||||
|
snapToEnd?: boolean;
|
||||||
|
/**
|
||||||
|
* Called when the scroller comes into focus (e.g. for highlighting)
|
||||||
|
*/
|
||||||
|
onFocus?(evt: HWEvent): void;
|
||||||
|
/**
|
||||||
|
* Called when the scroller goes out of focus
|
||||||
|
*/
|
||||||
|
onBlur?(evt: HWEvent): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TVTextScrollView extends React.Component<TVTextScrollViewProps> {}
|
||||||
|
|
||||||
|
export interface PressableStateCallbackType {
|
||||||
|
readonly focused: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TouchableWithoutFeedbackPropsIOS {
|
||||||
|
/**
|
||||||
|
* _(Apple TV only)_ TV preferred focus (see documentation for the View component).
|
||||||
|
*
|
||||||
|
* @platform ios
|
||||||
|
*/
|
||||||
|
hasTVPreferredFocus?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* _(Apple TV only)_ Object with properties to control Apple TV parallax effects.
|
||||||
|
*
|
||||||
|
* Enabled: If true, parallax effects are enabled. Defaults to true. shiftDistanceX: Defaults to
|
||||||
|
* 2.0. shiftDistanceY: Defaults to 2.0. tiltAngle: Defaults to 0.05. magnification: Defaults to
|
||||||
|
* 1.0. pressMagnification: Defaults to 1.0. pressDuration: Defaults to 0.3. pressDelay:
|
||||||
|
* Defaults to 0.0.
|
||||||
|
*
|
||||||
|
* @platform ios
|
||||||
|
*/
|
||||||
|
tvParallaxProperties?: TVParallaxProperties;
|
||||||
|
}
|
||||||
|
}
|
@ -35,7 +35,7 @@ import {
|
|||||||
} from "@kyoo/primitives";
|
} from "@kyoo/primitives";
|
||||||
import { Chapter, Font, Track } from "@kyoo/models";
|
import { Chapter, Font, Track } from "@kyoo/models";
|
||||||
import { useAtomValue, useSetAtom, useAtom } from "jotai";
|
import { useAtomValue, useSetAtom, useAtom } from "jotai";
|
||||||
import { View, ViewProps } from "react-native";
|
import { Platform, View, ViewProps } from "react-native";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { percent, rem, useYoshiki } from "yoshiki/native";
|
import { percent, rem, useYoshiki } from "yoshiki/native";
|
||||||
import { useRouter } from "solito/router";
|
import { useRouter } from "solito/router";
|
||||||
@ -43,6 +43,7 @@ import ArrowBack from "@material-symbols/svg-400/rounded/arrow_back-fill.svg";
|
|||||||
import { LeftButtons } from "./left-buttons";
|
import { LeftButtons } from "./left-buttons";
|
||||||
import { RightButtons } from "./right-buttons";
|
import { RightButtons } from "./right-buttons";
|
||||||
import { bufferedAtom, durationAtom, loadAtom, playAtom, progressAtom } from "../state";
|
import { bufferedAtom, durationAtom, loadAtom, playAtom, progressAtom } from "../state";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
export const Hover = ({
|
export const Hover = ({
|
||||||
isLoading,
|
isLoading,
|
||||||
@ -74,6 +75,14 @@ export const Hover = ({
|
|||||||
onMenuClose: () => void;
|
onMenuClose: () => void;
|
||||||
show: boolean;
|
show: boolean;
|
||||||
} & ViewProps) => {
|
} & ViewProps) => {
|
||||||
|
const ref = useRef<View | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
ref.current?.focus();
|
||||||
|
}, 100);
|
||||||
|
}, [show]);
|
||||||
|
|
||||||
// TODO animate show
|
// TODO animate show
|
||||||
const opacity = !show && { opacity: 0 };
|
const opacity = !show && { opacity: 0 };
|
||||||
return (
|
return (
|
||||||
@ -113,7 +122,7 @@ export const Hover = ({
|
|||||||
<View
|
<View
|
||||||
{...css({ flexDirection: "row", flexGrow: 1, justifyContent: "space-between" })}
|
{...css({ flexDirection: "row", flexGrow: 1, justifyContent: "space-between" })}
|
||||||
>
|
>
|
||||||
<LeftButtons previousSlug={previousSlug} nextSlug={nextSlug} />
|
<LeftButtons previousSlug={previousSlug} nextSlug={nextSlug} playRef={ref} />
|
||||||
<RightButtons
|
<RightButtons
|
||||||
subtitles={subtitles}
|
subtitles={subtitles}
|
||||||
fonts={fonts}
|
fonts={fonts}
|
||||||
@ -176,14 +185,18 @@ export const Back = ({
|
|||||||
props,
|
props,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<IconButton
|
{!Platform.isTV && (
|
||||||
icon={ArrowBack}
|
<IconButton
|
||||||
{...(href ? { as: Link as any, href: href } : { as: PressableFeedback, onPress: router.back })}
|
icon={ArrowBack}
|
||||||
{...tooltip(t("player.back"))}
|
{...(href
|
||||||
/>
|
? { as: Link as any, href: href }
|
||||||
|
: { as: PressableFeedback, onPress: router.back })}
|
||||||
|
{...tooltip(t("player.back"))}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Skeleton>
|
<Skeleton>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<Skeleton {...css({ width: rem(5), })} />
|
<Skeleton {...css({ width: rem(5) })} />
|
||||||
) : (
|
) : (
|
||||||
<H1
|
<H1
|
||||||
{...css({
|
{...css({
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
import { IconButton, Link, P, Slider, tooltip, ts } from "@kyoo/primitives";
|
import { IconButton, Link, P, Slider, tooltip, ts } from "@kyoo/primitives";
|
||||||
import { useAtom, useAtomValue } from "jotai";
|
import { useAtom, useAtomValue } from "jotai";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { View } from "react-native";
|
import { Platform, View } from "react-native";
|
||||||
import SkipPrevious from "@material-symbols/svg-400/rounded/skip_previous-fill.svg";
|
import SkipPrevious from "@material-symbols/svg-400/rounded/skip_previous-fill.svg";
|
||||||
import SkipNext from "@material-symbols/svg-400/rounded/skip_next-fill.svg";
|
import SkipNext from "@material-symbols/svg-400/rounded/skip_next-fill.svg";
|
||||||
import PlayArrow from "@material-symbols/svg-400/rounded/play_arrow-fill.svg";
|
import PlayArrow from "@material-symbols/svg-400/rounded/play_arrow-fill.svg";
|
||||||
@ -32,13 +32,16 @@ import VolumeDown from "@material-symbols/svg-400/rounded/volume_down-fill.svg";
|
|||||||
import VolumeUp from "@material-symbols/svg-400/rounded/volume_up-fill.svg";
|
import VolumeUp from "@material-symbols/svg-400/rounded/volume_up-fill.svg";
|
||||||
import { durationAtom, mutedAtom, playAtom, progressAtom, volumeAtom } from "../state";
|
import { durationAtom, mutedAtom, playAtom, progressAtom, volumeAtom } from "../state";
|
||||||
import { px, useYoshiki } from "yoshiki/native";
|
import { px, useYoshiki } from "yoshiki/native";
|
||||||
|
import { RefObject } from "react";
|
||||||
|
|
||||||
export const LeftButtons = ({
|
export const LeftButtons = ({
|
||||||
previousSlug,
|
previousSlug,
|
||||||
nextSlug,
|
nextSlug,
|
||||||
|
playRef,
|
||||||
}: {
|
}: {
|
||||||
previousSlug?: string | null;
|
previousSlug?: string | null;
|
||||||
nextSlug?: string | null;
|
nextSlug?: string | null;
|
||||||
|
playRef: RefObject<View>;
|
||||||
}) => {
|
}) => {
|
||||||
const { css } = useYoshiki();
|
const { css } = useYoshiki();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -58,7 +61,9 @@ export const LeftButtons = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<IconButton
|
<IconButton
|
||||||
|
ref={playRef}
|
||||||
icon={isPlaying ? Pause : PlayArrow}
|
icon={isPlaying ? Pause : PlayArrow}
|
||||||
|
hasTVPreferredFocus
|
||||||
onPress={() => setPlay(!isPlaying)}
|
onPress={() => setPlay(!isPlaying)}
|
||||||
{...tooltip(isPlaying ? t("player.pause") : t("player.play"), true)}
|
{...tooltip(isPlaying ? t("player.pause") : t("player.play"), true)}
|
||||||
{...spacing}
|
{...spacing}
|
||||||
@ -72,7 +77,7 @@ export const LeftButtons = ({
|
|||||||
{...spacing}
|
{...spacing}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<VolumeSlider />
|
{!Platform.isTV && <VolumeSlider />}
|
||||||
<ProgressText />
|
<ProgressText />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
@ -21,7 +21,8 @@
|
|||||||
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 } from "react-native";
|
import { BackHandler, Platform, Pressable, StyleSheet } from "react-native";
|
||||||
|
import { useTVEventHandler } from "@kyoo/primitives/tv";
|
||||||
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";
|
||||||
@ -106,6 +107,19 @@ export const Player: QueryPage<{ slug: string }> = ({ slug }) => {
|
|||||||
document.addEventListener("pointermove", handler);
|
document.addEventListener("pointermove", handler);
|
||||||
return () => document.removeEventListener("pointermove", handler);
|
return () => document.removeEventListener("pointermove", handler);
|
||||||
});
|
});
|
||||||
|
useTVEventHandler((e) => {
|
||||||
|
if (e.eventType === "cancel") setMouseMoved(false);
|
||||||
|
show();
|
||||||
|
});
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = BackHandler.addEventListener("hardwareBackPress", () => {
|
||||||
|
if (!displayControls) return false;
|
||||||
|
setMouseMoved(false);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return () => handler.remove();
|
||||||
|
}, [displayControls]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Platform.OS !== "web" || !/Mobi/i.test(window.navigator.userAgent)) return;
|
if (Platform.OS !== "web" || !/Mobi/i.test(window.navigator.userAgent)) return;
|
||||||
@ -116,7 +130,7 @@ export const Player: QueryPage<{ slug: string }> = ({ slug }) => {
|
|||||||
if (error || playbackError)
|
if (error || playbackError)
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Back isLoading={false} {...css({ position: "relative", bg: (theme) => theme.appbar })} />
|
<Back isLoading={false} {...css({ position: "relative", bg: (theme) => theme.accent })} />
|
||||||
<ErrorView error={error ?? { errors: [playbackError!] }} />
|
<ErrorView error={error ?? { errors: [playbackError!] }} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user