mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-08-07 09:01:29 -04:00
Keep controls open in menues or hover
This commit is contained in:
parent
f7eed87c5c
commit
9cc8ee7490
@ -1,5 +1,6 @@
|
|||||||
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 SkipPrevious from "@material-symbols/svg-400/rounded/skip_previous-fill.svg";
|
import SkipPrevious from "@material-symbols/svg-400/rounded/skip_previous-fill.svg";
|
||||||
|
import type { ComponentProps } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Platform, View, type ViewProps } from "react-native";
|
import { Platform, View, type ViewProps } from "react-native";
|
||||||
import type { VideoPlayer } from "react-native-video";
|
import type { VideoPlayer } from "react-native-video";
|
||||||
@ -9,6 +10,7 @@ import {
|
|||||||
H2,
|
H2,
|
||||||
IconButton,
|
IconButton,
|
||||||
Link,
|
Link,
|
||||||
|
type Menu,
|
||||||
noTouch,
|
noTouch,
|
||||||
Poster,
|
Poster,
|
||||||
tooltip,
|
tooltip,
|
||||||
@ -23,12 +25,18 @@ export const BottomControls = ({
|
|||||||
poster,
|
poster,
|
||||||
name,
|
name,
|
||||||
chapters,
|
chapters,
|
||||||
|
previous,
|
||||||
|
next,
|
||||||
|
setMenu,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
player: VideoPlayer;
|
player: VideoPlayer;
|
||||||
poster: KImage;
|
poster: KImage;
|
||||||
name: string;
|
name: string;
|
||||||
chapters: Chapter[];
|
chapters: Chapter[];
|
||||||
|
previous: string | null;
|
||||||
|
next: string | null;
|
||||||
|
setMenu: (isOpen: boolean) => void;
|
||||||
} & ViewProps) => {
|
} & ViewProps) => {
|
||||||
const { css } = useYoshiki();
|
const { css } = useYoshiki();
|
||||||
|
|
||||||
@ -69,7 +77,12 @@ export const BottomControls = ({
|
|||||||
name
|
name
|
||||||
</H2>
|
</H2>
|
||||||
<ProgressBar player={player} chapters={chapters} />
|
<ProgressBar player={player} chapters={chapters} />
|
||||||
<ControlButtons player={player} />
|
<ControlButtons
|
||||||
|
player={player}
|
||||||
|
previous={previous}
|
||||||
|
next={next}
|
||||||
|
setMenu={setMenu}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
@ -79,16 +92,23 @@ const ControlButtons = ({
|
|||||||
player,
|
player,
|
||||||
previous,
|
previous,
|
||||||
next,
|
next,
|
||||||
|
setMenu,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
player: VideoPlayer;
|
player: VideoPlayer;
|
||||||
previous: string;
|
previous: string | null;
|
||||||
next: string;
|
next: string | null;
|
||||||
|
setMenu: (isOpen: boolean) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { css } = useYoshiki();
|
const { css } = useYoshiki();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const spacing = css({ marginHorizontal: ts(1) });
|
const spacing = css({ marginHorizontal: ts(1) });
|
||||||
|
const menuProps = {
|
||||||
|
onMenuOpen: () => setMenu(true),
|
||||||
|
onMenuClose: () => setMenu(false),
|
||||||
|
...spacing,
|
||||||
|
} satisfies Partial<ComponentProps<typeof Menu>>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
@ -130,9 +150,9 @@ const ControlButtons = ({
|
|||||||
<ProgressText player={player} {...spacing} />
|
<ProgressText player={player} {...spacing} />
|
||||||
</View>
|
</View>
|
||||||
<View {...css({ flexDirection: "row" })}>
|
<View {...css({ flexDirection: "row" })}>
|
||||||
<SubtitleMenu {...(spacing as any)} />
|
<SubtitleMenu {...menuProps} />
|
||||||
<AudioMenu {...(spacing as any)} />
|
<AudioMenu {...menuProps} />
|
||||||
<QualityMenu {...(spacing as any)} />
|
<QualityMenu {...menuProps} />
|
||||||
{Platform.OS === "web" && <FullscreenButton {...spacing} />}
|
{Platform.OS === "web" && <FullscreenButton {...spacing} />}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import type { ViewProps } from "react-native";
|
||||||
import type { VideoPlayer } from "react-native-video";
|
import type { VideoPlayer } from "react-native-video";
|
||||||
import { useYoshiki } from "yoshiki/native";
|
import { useYoshiki } from "yoshiki/native";
|
||||||
import type { KImage } from "~/models";
|
import type { Chapter, KImage } from "~/models";
|
||||||
import { Back } from "./back";
|
import { Back } from "./back";
|
||||||
import { BottomControls } from "./bottom-controls";
|
import { BottomControls } from "./bottom-controls";
|
||||||
import { TouchControls } from "./touch";
|
import { TouchControls } from "./touch";
|
||||||
@ -8,54 +10,75 @@ import { TouchControls } from "./touch";
|
|||||||
export const Controls = ({
|
export const Controls = ({
|
||||||
player,
|
player,
|
||||||
title,
|
title,
|
||||||
|
subTitle,
|
||||||
poster,
|
poster,
|
||||||
|
chapters,
|
||||||
|
previous,
|
||||||
|
next,
|
||||||
}: {
|
}: {
|
||||||
player: VideoPlayer;
|
player: VideoPlayer;
|
||||||
title: string;
|
title: string;
|
||||||
description: string | null;
|
subTitle: string;
|
||||||
poster: KImage | null;
|
poster: KImage;
|
||||||
|
chapters: Chapter[];
|
||||||
|
previous: string | null;
|
||||||
|
next: string | null;
|
||||||
}) => {
|
}) => {
|
||||||
const { css } = useYoshiki();
|
const { css } = useYoshiki();
|
||||||
|
|
||||||
|
const [hover, setHover] = useState(false);
|
||||||
|
const [menuOpenned, setMenu] = useState(false);
|
||||||
|
|
||||||
// <TouchControls previousSlug={previousSlug} nextSlug={nextSlug} />
|
// <TouchControls previousSlug={previousSlug} nextSlug={nextSlug} />
|
||||||
|
|
||||||
|
const hoverControls = {
|
||||||
|
onPointerEnter: (e) => {
|
||||||
|
if (e.nativeEvent.pointerType === "mouse") setHover(true);
|
||||||
|
},
|
||||||
|
onPointerLeave: (e) => {
|
||||||
|
if (e.nativeEvent.pointerType === "mouse") setHover(false);
|
||||||
|
},
|
||||||
|
} satisfies ViewProps;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchControls
|
<TouchControls player={player} forceShow={hover || menuOpenned}>
|
||||||
player={player}
|
|
||||||
// onPointerEnter={(e) => {
|
|
||||||
// if (e.nativeEvent.pointerType === "mouse")
|
|
||||||
// setHover((x) => ({ ...x, mouseHover: true }));
|
|
||||||
// }}
|
|
||||||
// onPointerLeave={(e) => {
|
|
||||||
// if (e.nativeEvent.pointerType === "mouse")
|
|
||||||
// setHover((x) => ({ ...x, mouseHover: false }));
|
|
||||||
// }}
|
|
||||||
>
|
|
||||||
<Back
|
<Back
|
||||||
name={title}
|
name={title}
|
||||||
{...css({
|
{...css(
|
||||||
// pointerEvents: "auto",
|
{
|
||||||
position: "absolute",
|
// pointerEvents: "auto",
|
||||||
top: 0,
|
position: "absolute",
|
||||||
left: 0,
|
top: 0,
|
||||||
right: 0,
|
left: 0,
|
||||||
bg: (theme) => theme.darkOverlay,
|
right: 0,
|
||||||
})}
|
bg: (theme) => theme.darkOverlay,
|
||||||
|
},
|
||||||
|
hoverControls,
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
<BottomControls
|
<BottomControls
|
||||||
player={player}
|
player={player}
|
||||||
name={title}
|
name={subTitle}
|
||||||
poster={poster!}
|
poster={poster}
|
||||||
chapters={[]}
|
chapters={chapters}
|
||||||
{...css({
|
previous={previous}
|
||||||
// Fixed is used because firefox android make the hover disapear under the navigation bar in absolute
|
next={next}
|
||||||
// position: Platform.OS === "web" ? ("fixed" as any) : "absolute",
|
setMenu={setMenu}
|
||||||
position: "absolute",
|
{...css(
|
||||||
bottom: 0,
|
{
|
||||||
left: 0,
|
// Fixed is used because firefox android make the hover disapear under the navigation bar in absolute
|
||||||
right: 0,
|
// position: Platform.OS === "web" ? ("fixed" as any) : "absolute",
|
||||||
bg: (theme) => theme.darkOverlay,
|
position: "absolute",
|
||||||
})}
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bg: (theme) => theme.darkOverlay,
|
||||||
|
},
|
||||||
|
hoverControls,
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</TouchControls>
|
</TouchControls>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export { LoadingIndicator } from "./misc";
|
||||||
|
@ -12,13 +12,15 @@ import { useIsTouch } from "~/primitives";
|
|||||||
export const TouchControls = ({
|
export const TouchControls = ({
|
||||||
player,
|
player,
|
||||||
children,
|
children,
|
||||||
|
forceShow = false,
|
||||||
...props
|
...props
|
||||||
}: { player: VideoPlayer } & PressableProps) => {
|
}: { player: VideoPlayer; forceShow?: boolean } & PressableProps) => {
|
||||||
const { css } = useYoshiki();
|
const { css } = useYoshiki();
|
||||||
const isTouch = useIsTouch();
|
const isTouch = useIsTouch();
|
||||||
|
|
||||||
const [shouldShow, setShow] = useState(true);
|
const [_show, setShow] = useState(true);
|
||||||
const hideTimeout = useRef<NodeJS.Timeout | null>(null);
|
const hideTimeout = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
const shouldShow = forceShow || _show;
|
||||||
const show = useCallback((val: boolean = true) => {
|
const show = useCallback((val: boolean = true) => {
|
||||||
setShow(val);
|
setShow(val);
|
||||||
if (hideTimeout.current) clearTimeout(hideTimeout.current);
|
if (hideTimeout.current) clearTimeout(hideTimeout.current);
|
||||||
@ -27,7 +29,6 @@ export const TouchControls = ({
|
|||||||
setShow(false);
|
setShow(false);
|
||||||
}, 2500);
|
}, 2500);
|
||||||
}, []);
|
}, []);
|
||||||
// TODO: handle mouse hover & seek
|
|
||||||
|
|
||||||
// On mouse move
|
// On mouse move
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -8,7 +8,7 @@ import { useToken } from "~/providers/account-context";
|
|||||||
import { useLocalSetting } from "~/providers/settings";
|
import { useLocalSetting } from "~/providers/settings";
|
||||||
import { type QueryIdentifier, useFetch } from "~/query";
|
import { type QueryIdentifier, useFetch } from "~/query";
|
||||||
import { useQueryState } from "~/utils";
|
import { useQueryState } from "~/utils";
|
||||||
import { LoadingIndicator } from "./controls";
|
import { Controls, LoadingIndicator } from "./controls";
|
||||||
|
|
||||||
const mapMetadata = (item: FullVideo | undefined) => {
|
const mapMetadata = (item: FullVideo | undefined) => {
|
||||||
if (!item) return null;
|
if (!item) return null;
|
||||||
@ -134,7 +134,11 @@ export const Player = () => {
|
|||||||
/>
|
/>
|
||||||
<ContrastArea mode="dark">
|
<ContrastArea mode="dark">
|
||||||
<LoadingIndicator player={player} />
|
<LoadingIndicator player={player} />
|
||||||
<Controls player={player} {...metadata} />
|
<Controls
|
||||||
|
player={player}
|
||||||
|
title={metadata?.title}
|
||||||
|
subTitle={metadata?.subtitle}
|
||||||
|
/>
|
||||||
</ContrastArea>
|
</ContrastArea>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user