mirror of
				https://github.com/zoriya/Kyoo.git
				synced 2025-11-04 03:27:14 -05:00 
			
		
		
		
	Fix player controls style
This commit is contained in:
		
							parent
							
								
									da5823deb2
								
							
						
					
					
						commit
						e01c67b1ef
					
				@ -27,9 +27,10 @@ export const Video = z.object({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		// Name of the tool that made the guess
 | 
							// Name of the tool that made the guess
 | 
				
			||||||
		from: z.string(),
 | 
							from: z.string(),
 | 
				
			||||||
		get history() {
 | 
							// Adding that results in an infinite recursion
 | 
				
			||||||
			return z.array(Video.shape.guess.omit({ history: true })).default([]);
 | 
							// get history() {
 | 
				
			||||||
		},
 | 
							// 	return z.array(Video.shape.guess.omit({ history: true })).default([]);
 | 
				
			||||||
 | 
							// },
 | 
				
			||||||
	}),
 | 
						}),
 | 
				
			||||||
	createdAt: zdate(),
 | 
						createdAt: zdate(),
 | 
				
			||||||
	updatedAt: zdate(),
 | 
						updatedAt: zdate(),
 | 
				
			||||||
 | 
				
			|||||||
@ -25,7 +25,7 @@ export const ImageBackground = ({
 | 
				
			|||||||
	layout: ImageLayout;
 | 
						layout: ImageLayout;
 | 
				
			||||||
	children: ReactNode;
 | 
						children: ReactNode;
 | 
				
			||||||
}) => {
 | 
					}) => {
 | 
				
			||||||
	const { css } = useYoshiki();
 | 
						const { css, theme } = useYoshiki();
 | 
				
			||||||
	const { apiUrl, authToken } = useToken();
 | 
						const { apiUrl, authToken } = useToken();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
@ -42,7 +42,10 @@ export const ImageBackground = ({
 | 
				
			|||||||
			}}
 | 
								}}
 | 
				
			||||||
			placeholder={{ blurhash: src?.blurhash }}
 | 
								placeholder={{ blurhash: src?.blurhash }}
 | 
				
			||||||
			accessibilityLabel={alt}
 | 
								accessibilityLabel={alt}
 | 
				
			||||||
			{...(css([layout, { overflow: "hidden" }], props) as any)}
 | 
								{...(css(
 | 
				
			||||||
 | 
									[layout, { overflow: "hidden", backgroundColor: theme.overlay0 }],
 | 
				
			||||||
 | 
									props,
 | 
				
			||||||
 | 
								) as any)}
 | 
				
			||||||
		/>
 | 
							/>
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -34,7 +34,7 @@ export const Image = ({
 | 
				
			|||||||
	style?: ImageStyle;
 | 
						style?: ImageStyle;
 | 
				
			||||||
	layout: ImageLayout;
 | 
						layout: ImageLayout;
 | 
				
			||||||
}) => {
 | 
					}) => {
 | 
				
			||||||
	const { css } = useYoshiki();
 | 
						const { css, theme } = useYoshiki();
 | 
				
			||||||
	const { apiUrl, authToken } = useToken();
 | 
						const { apiUrl, authToken } = useToken();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
@ -51,7 +51,10 @@ export const Image = ({
 | 
				
			|||||||
			}}
 | 
								}}
 | 
				
			||||||
			placeholder={{ blurhash: src?.blurhash }}
 | 
								placeholder={{ blurhash: src?.blurhash }}
 | 
				
			||||||
			accessibilityLabel={alt}
 | 
								accessibilityLabel={alt}
 | 
				
			||||||
			{...(css([layout, { borderRadius: 6 }], props) as any)}
 | 
								{...(css(
 | 
				
			||||||
 | 
									[layout, { borderRadius: 6, backgroundColor: theme.overlay0 }],
 | 
				
			||||||
 | 
									props,
 | 
				
			||||||
 | 
								) as any)}
 | 
				
			||||||
		/>
 | 
							/>
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -108,7 +108,6 @@ export const Skeleton = ({
 | 
				
			|||||||
						colors={["transparent", theme.overlay1, "transparent"]}
 | 
											colors={["transparent", theme.overlay1, "transparent"]}
 | 
				
			||||||
						style={[
 | 
											style={[
 | 
				
			||||||
							StyleSheet.absoluteFillObject,
 | 
												StyleSheet.absoluteFillObject,
 | 
				
			||||||
							{ transform: [{ translateX: -width.value }] },
 | 
					 | 
				
			||||||
							animated,
 | 
												animated,
 | 
				
			||||||
						]}
 | 
											]}
 | 
				
			||||||
					/>
 | 
										/>
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,7 @@ import {
 | 
				
			|||||||
	tooltip,
 | 
						tooltip,
 | 
				
			||||||
} from "~/primitives";
 | 
					} from "~/primitives";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const Back = ({ name, ...props }: { name: string } & ViewProps) => {
 | 
					export const Back = ({ name, ...props }: { name?: string } & ViewProps) => {
 | 
				
			||||||
	const { css } = useYoshiki();
 | 
						const { css } = useYoshiki();
 | 
				
			||||||
	const { t } = useTranslation();
 | 
						const { t } = useTranslation();
 | 
				
			||||||
	const router = useRouter();
 | 
						const router = useRouter();
 | 
				
			||||||
@ -35,49 +35,19 @@ export const Back = ({ name, ...props }: { name: string } & ViewProps) => {
 | 
				
			|||||||
				onPress={router.back}
 | 
									onPress={router.back}
 | 
				
			||||||
				{...tooltip(t("player.back"))}
 | 
									{...tooltip(t("player.back"))}
 | 
				
			||||||
			/>
 | 
								/>
 | 
				
			||||||
			<H1
 | 
								{name ? (
 | 
				
			||||||
				{...css({
 | 
									<H1
 | 
				
			||||||
					alignSelf: "center",
 | 
										{...css({
 | 
				
			||||||
					fontSize: rem(1.5),
 | 
											alignSelf: "center",
 | 
				
			||||||
					marginLeft: rem(1),
 | 
											fontSize: rem(1.5),
 | 
				
			||||||
				})}
 | 
											marginLeft: rem(1),
 | 
				
			||||||
			>
 | 
										})}
 | 
				
			||||||
				{name}
 | 
									>
 | 
				
			||||||
			</H1>
 | 
										{name}
 | 
				
			||||||
		</View>
 | 
									</H1>
 | 
				
			||||||
	);
 | 
								) : (
 | 
				
			||||||
};
 | 
									<Skeleton {...css({ width: rem(5) })} />
 | 
				
			||||||
 | 
					 | 
				
			||||||
Back.Loader = (props: ViewProps) => {
 | 
					 | 
				
			||||||
	const { css } = useYoshiki();
 | 
					 | 
				
			||||||
	const { t } = useTranslation();
 | 
					 | 
				
			||||||
	const router = useRouter();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return (
 | 
					 | 
				
			||||||
		<View
 | 
					 | 
				
			||||||
			{...css(
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					position: "absolute",
 | 
					 | 
				
			||||||
					top: 0,
 | 
					 | 
				
			||||||
					left: 0,
 | 
					 | 
				
			||||||
					right: 0,
 | 
					 | 
				
			||||||
					bg: (theme) => theme.darkOverlay,
 | 
					 | 
				
			||||||
					display: "flex",
 | 
					 | 
				
			||||||
					flexDirection: "row",
 | 
					 | 
				
			||||||
					alignItems: "center",
 | 
					 | 
				
			||||||
					padding: percent(0.33),
 | 
					 | 
				
			||||||
					color: "white",
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
				props,
 | 
					 | 
				
			||||||
			)}
 | 
								)}
 | 
				
			||||||
		>
 | 
					 | 
				
			||||||
			<IconButton
 | 
					 | 
				
			||||||
				icon={ArrowBack}
 | 
					 | 
				
			||||||
				as={PressableFeedback}
 | 
					 | 
				
			||||||
				onPress={router.back}
 | 
					 | 
				
			||||||
				{...tooltip(t("player.back"))}
 | 
					 | 
				
			||||||
			/>
 | 
					 | 
				
			||||||
			<Skeleton {...css({ width: rem(5) })} />
 | 
					 | 
				
			||||||
		</View>
 | 
							</View>
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@ 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";
 | 
				
			||||||
import { percent, useYoshiki } from "yoshiki/native";
 | 
					import { percent, rem, useYoshiki } from "yoshiki/native";
 | 
				
			||||||
import type { Chapter, KImage } from "~/models";
 | 
					import type { Chapter, KImage } from "~/models";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
	H2,
 | 
						H2,
 | 
				
			||||||
@ -12,6 +12,7 @@ import {
 | 
				
			|||||||
	Link,
 | 
						Link,
 | 
				
			||||||
	type Menu,
 | 
						type Menu,
 | 
				
			||||||
	Poster,
 | 
						Poster,
 | 
				
			||||||
 | 
						Skeleton,
 | 
				
			||||||
	tooltip,
 | 
						tooltip,
 | 
				
			||||||
	ts,
 | 
						ts,
 | 
				
			||||||
	useIsTouch,
 | 
						useIsTouch,
 | 
				
			||||||
@ -31,11 +32,11 @@ export const BottomControls = ({
 | 
				
			|||||||
	...props
 | 
						...props
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
	player: VideoPlayer;
 | 
						player: VideoPlayer;
 | 
				
			||||||
	poster: KImage;
 | 
						poster?: KImage | null;
 | 
				
			||||||
	name: string;
 | 
						name?: string;
 | 
				
			||||||
	chapters: Chapter[];
 | 
						chapters: Chapter[];
 | 
				
			||||||
	previous: string | null;
 | 
						previous?: string | null;
 | 
				
			||||||
	next: string | null;
 | 
						next?: string | null;
 | 
				
			||||||
	setMenu: (isOpen: boolean) => void;
 | 
						setMenu: (isOpen: boolean) => void;
 | 
				
			||||||
} & ViewProps) => {
 | 
					} & ViewProps) => {
 | 
				
			||||||
	const { css } = useYoshiki();
 | 
						const { css } = useYoshiki();
 | 
				
			||||||
@ -57,25 +58,34 @@ export const BottomControls = ({
 | 
				
			|||||||
					position: "relative",
 | 
										position: "relative",
 | 
				
			||||||
				})}
 | 
									})}
 | 
				
			||||||
			>
 | 
								>
 | 
				
			||||||
				<Poster
 | 
									{poster !== undefined ? (
 | 
				
			||||||
					src={poster}
 | 
										<Poster
 | 
				
			||||||
					quality="low"
 | 
											src={poster}
 | 
				
			||||||
					layout={{ width: percent(100) }}
 | 
											quality="low"
 | 
				
			||||||
					{...(css({ position: "absolute", bottom: 0 }) as any)}
 | 
											layout={{ width: percent(100) }}
 | 
				
			||||||
				/>
 | 
											{...(css({ position: "absolute", bottom: 0 }) as any)}
 | 
				
			||||||
 | 
										/>
 | 
				
			||||||
 | 
									) : (
 | 
				
			||||||
 | 
										<Poster.Loader
 | 
				
			||||||
 | 
											layout={{ width: percent(100) }}
 | 
				
			||||||
 | 
											{...(css({ position: "absolute", bottom: 0 }) as any)}
 | 
				
			||||||
 | 
										/>
 | 
				
			||||||
 | 
									)}
 | 
				
			||||||
			</View>
 | 
								</View>
 | 
				
			||||||
			<View
 | 
								<View
 | 
				
			||||||
				{...css({
 | 
									{...css({
 | 
				
			||||||
					marginLeft: { xs: ts(0.5), sm: ts(3) },
 | 
										marginHorizontal: { xs: ts(0.5), sm: ts(3) },
 | 
				
			||||||
					flexDirection: "column",
 | 
										flexDirection: "column",
 | 
				
			||||||
					flexGrow: 1,
 | 
										flex: 1,
 | 
				
			||||||
					flexShrink: 1,
 | 
					 | 
				
			||||||
					maxWidth: percent(100),
 | 
					 | 
				
			||||||
				})}
 | 
									})}
 | 
				
			||||||
			>
 | 
								>
 | 
				
			||||||
				<H2 numberOfLines={1} {...css({ paddingBottom: ts(1) })}>
 | 
									{name ? (
 | 
				
			||||||
					name
 | 
										<H2 numberOfLines={1} {...css({ paddingBottom: ts(1) })}>
 | 
				
			||||||
				</H2>
 | 
											{name}
 | 
				
			||||||
 | 
										</H2>
 | 
				
			||||||
 | 
									) : (
 | 
				
			||||||
 | 
										<Skeleton {...css({ width: rem(15), height: rem(2) })} />
 | 
				
			||||||
 | 
									)}
 | 
				
			||||||
				<ProgressBar player={player} chapters={chapters} />
 | 
									<ProgressBar player={player} chapters={chapters} />
 | 
				
			||||||
				<ControlButtons
 | 
									<ControlButtons
 | 
				
			||||||
					player={player}
 | 
										player={player}
 | 
				
			||||||
@ -96,8 +106,8 @@ const ControlButtons = ({
 | 
				
			|||||||
	...props
 | 
						...props
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
	player: VideoPlayer;
 | 
						player: VideoPlayer;
 | 
				
			||||||
	previous: string | null;
 | 
						previous?: string | null;
 | 
				
			||||||
	next: string | null;
 | 
						next?: string | null;
 | 
				
			||||||
	setMenu: (isOpen: boolean) => void;
 | 
						setMenu: (isOpen: boolean) => void;
 | 
				
			||||||
}) => {
 | 
					}) => {
 | 
				
			||||||
	const { css } = useYoshiki();
 | 
						const { css } = useYoshiki();
 | 
				
			||||||
@ -116,7 +126,7 @@ const ControlButtons = ({
 | 
				
			|||||||
			{...css(
 | 
								{...css(
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					flexDirection: "row",
 | 
										flexDirection: "row",
 | 
				
			||||||
					flexGrow: 1,
 | 
										flex: 1,
 | 
				
			||||||
					justifyContent: "space-between",
 | 
										justifyContent: "space-between",
 | 
				
			||||||
					flexWrap: "wrap",
 | 
										flexWrap: "wrap",
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
@ -124,7 +134,7 @@ const ControlButtons = ({
 | 
				
			|||||||
			)}
 | 
								)}
 | 
				
			||||||
		>
 | 
							>
 | 
				
			||||||
			<View {...css({ flexDirection: "row" })}>
 | 
								<View {...css({ flexDirection: "row" })}>
 | 
				
			||||||
				{isTouch && (
 | 
									{!isTouch && (
 | 
				
			||||||
					<View {...css({ flexDirection: "row" })}>
 | 
										<View {...css({ flexDirection: "row" })}>
 | 
				
			||||||
						{previous && (
 | 
											{previous && (
 | 
				
			||||||
							<IconButton
 | 
												<IconButton
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,7 @@
 | 
				
			|||||||
import { useState } from "react";
 | 
					import { useState } from "react";
 | 
				
			||||||
import type { ViewProps } from "react-native";
 | 
					import type { ViewProps } from "react-native";
 | 
				
			||||||
 | 
					import { StyleSheet } from "react-native";
 | 
				
			||||||
 | 
					import { useSafeAreaInsets } from "react-native-safe-area-context";
 | 
				
			||||||
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 { Chapter, KImage } from "~/models";
 | 
					import type { Chapter, KImage } from "~/models";
 | 
				
			||||||
@ -11,22 +13,23 @@ import { TouchControls } from "./touch";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const Controls = ({
 | 
					export const Controls = ({
 | 
				
			||||||
	player,
 | 
						player,
 | 
				
			||||||
	title,
 | 
						name,
 | 
				
			||||||
	subTitle,
 | 
					 | 
				
			||||||
	poster,
 | 
						poster,
 | 
				
			||||||
 | 
						subName,
 | 
				
			||||||
	chapters,
 | 
						chapters,
 | 
				
			||||||
	previous,
 | 
						previous,
 | 
				
			||||||
	next,
 | 
						next,
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
	player: VideoPlayer;
 | 
						player: VideoPlayer;
 | 
				
			||||||
	title: string;
 | 
						name?: string;
 | 
				
			||||||
	subTitle: string;
 | 
						poster?: KImage | null;
 | 
				
			||||||
	poster: KImage;
 | 
						subName?: string;
 | 
				
			||||||
	chapters: Chapter[];
 | 
						chapters: Chapter[];
 | 
				
			||||||
	previous: string | null;
 | 
						previous?: string | null;
 | 
				
			||||||
	next: string | null;
 | 
						next?: string | null;
 | 
				
			||||||
}) => {
 | 
					}) => {
 | 
				
			||||||
	const { css } = useYoshiki();
 | 
						const { css } = useYoshiki();
 | 
				
			||||||
 | 
						const insets = useSafeAreaInsets();
 | 
				
			||||||
	const isTouch = useIsTouch();
 | 
						const isTouch = useIsTouch();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const [hover, setHover] = useState(false);
 | 
						const [hover, setHover] = useState(false);
 | 
				
			||||||
@ -44,9 +47,13 @@ export const Controls = ({
 | 
				
			|||||||
	} satisfies ViewProps;
 | 
						} satisfies ViewProps;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<TouchControls player={player} forceShow={hover || menuOpenned}>
 | 
							<TouchControls
 | 
				
			||||||
 | 
								player={player}
 | 
				
			||||||
 | 
								forceShow={hover || menuOpenned}
 | 
				
			||||||
 | 
								{...css(StyleSheet.absoluteFillObject)}
 | 
				
			||||||
 | 
							>
 | 
				
			||||||
			<Back
 | 
								<Back
 | 
				
			||||||
				name={title}
 | 
									name={name}
 | 
				
			||||||
				{...css(
 | 
									{...css(
 | 
				
			||||||
					{
 | 
										{
 | 
				
			||||||
						//	pointerEvents: "auto",
 | 
											//	pointerEvents: "auto",
 | 
				
			||||||
@ -55,6 +62,9 @@ export const Controls = ({
 | 
				
			|||||||
						left: 0,
 | 
											left: 0,
 | 
				
			||||||
						right: 0,
 | 
											right: 0,
 | 
				
			||||||
						bg: (theme) => theme.darkOverlay,
 | 
											bg: (theme) => theme.darkOverlay,
 | 
				
			||||||
 | 
											paddingTop: insets.top,
 | 
				
			||||||
 | 
											paddingLeft: insets.left,
 | 
				
			||||||
 | 
											paddingRight: insets.right,
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
					hoverControls,
 | 
										hoverControls,
 | 
				
			||||||
				)}
 | 
									)}
 | 
				
			||||||
@ -64,7 +74,7 @@ export const Controls = ({
 | 
				
			|||||||
			)}
 | 
								)}
 | 
				
			||||||
			<BottomControls
 | 
								<BottomControls
 | 
				
			||||||
				player={player}
 | 
									player={player}
 | 
				
			||||||
				name={subTitle}
 | 
									name={subName}
 | 
				
			||||||
				poster={poster}
 | 
									poster={poster}
 | 
				
			||||||
				chapters={chapters}
 | 
									chapters={chapters}
 | 
				
			||||||
				previous={previous}
 | 
									previous={previous}
 | 
				
			||||||
@ -79,6 +89,9 @@ export const Controls = ({
 | 
				
			|||||||
						left: 0,
 | 
											left: 0,
 | 
				
			||||||
						right: 0,
 | 
											right: 0,
 | 
				
			||||||
						bg: (theme) => theme.darkOverlay,
 | 
											bg: (theme) => theme.darkOverlay,
 | 
				
			||||||
 | 
											paddingLeft: insets.left,
 | 
				
			||||||
 | 
											paddingRight: insets.right,
 | 
				
			||||||
 | 
											paddingBottom: insets.bottom,
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
					hoverControls,
 | 
										hoverControls,
 | 
				
			||||||
				)}
 | 
									)}
 | 
				
			||||||
 | 
				
			|||||||
@ -39,9 +39,9 @@ export const ProgressBar = ({
 | 
				
			|||||||
				}}
 | 
									}}
 | 
				
			||||||
				setProgress={setSeek}
 | 
									setProgress={setSeek}
 | 
				
			||||||
				endSeek={() => {
 | 
									endSeek={() => {
 | 
				
			||||||
					setProgress(seek!);
 | 
										player.seekTo(seek!);
 | 
				
			||||||
					setSeek(null);
 | 
					 | 
				
			||||||
					setTimeout(player.play, 10);
 | 
										setTimeout(player.play, 10);
 | 
				
			||||||
 | 
										setSeek(null);
 | 
				
			||||||
				}}
 | 
									}}
 | 
				
			||||||
				// onHover={(progress, layout) => {
 | 
									// onHover={(progress, layout) => {
 | 
				
			||||||
				// 	setHoverProgress(progress);
 | 
									// 	setHoverProgress(progress);
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@ import {
 | 
				
			|||||||
	Pressable,
 | 
						Pressable,
 | 
				
			||||||
	type PressableProps,
 | 
						type PressableProps,
 | 
				
			||||||
} from "react-native";
 | 
					} from "react-native";
 | 
				
			||||||
import type { VideoPlayer } from "react-native-video";
 | 
					import { useEvent, type VideoPlayer } from "react-native-video";
 | 
				
			||||||
import { useYoshiki } from "yoshiki/native";
 | 
					import { useYoshiki } from "yoshiki/native";
 | 
				
			||||||
import { useIsTouch } from "~/primitives";
 | 
					import { useIsTouch } from "~/primitives";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -18,9 +18,14 @@ export const TouchControls = ({
 | 
				
			|||||||
	const { css } = useYoshiki();
 | 
						const { css } = useYoshiki();
 | 
				
			||||||
	const isTouch = useIsTouch();
 | 
						const isTouch = useIsTouch();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const [_show, setShow] = useState(true);
 | 
						const [playing, setPlay] = useState(player.isPlaying);
 | 
				
			||||||
 | 
						useEvent(player, "onPlaybackStateChange", (status) => {
 | 
				
			||||||
 | 
							setPlay(status.isPlaying);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const [_show, setShow] = useState(false);
 | 
				
			||||||
	const hideTimeout = useRef<NodeJS.Timeout | null>(null);
 | 
						const hideTimeout = useRef<NodeJS.Timeout | null>(null);
 | 
				
			||||||
	const shouldShow = forceShow || _show;
 | 
						const shouldShow = forceShow || _show || !playing;
 | 
				
			||||||
	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);
 | 
				
			||||||
 | 
				
			|||||||
@ -10,31 +10,15 @@ import { type QueryIdentifier, useFetch } from "~/query";
 | 
				
			|||||||
import { useQueryState } from "~/utils";
 | 
					import { useQueryState } from "~/utils";
 | 
				
			||||||
import { Controls, LoadingIndicator } from "./controls";
 | 
					import { Controls, LoadingIndicator } from "./controls";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mapMetadata = (item: FullVideo | undefined) => {
 | 
					 | 
				
			||||||
	if (!item) return null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// TODO: map current entry using entries' duration & the current playtime
 | 
					 | 
				
			||||||
	const currentEntry = 0;
 | 
					 | 
				
			||||||
	const entry = item.entries[currentEntry] ?? item.entries[0];
 | 
					 | 
				
			||||||
	if (!entry) return null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return {
 | 
					 | 
				
			||||||
		currentEntry,
 | 
					 | 
				
			||||||
		title: `${entry.name} (${entryDisplayNumber(entry)})`,
 | 
					 | 
				
			||||||
		description: entry.description,
 | 
					 | 
				
			||||||
		subtitle: item.show!.kind !== "movie" ? item.show!.name : null,
 | 
					 | 
				
			||||||
		poster: item.show!.poster,
 | 
					 | 
				
			||||||
		thumbnail: item.show!.thumbnail,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const Player = () => {
 | 
					export const Player = () => {
 | 
				
			||||||
	const [slug, setSlug] = useQueryState<string>("slug", undefined!);
 | 
						const [slug, setSlug] = useQueryState<string>("slug", undefined!);
 | 
				
			||||||
	const [start, setStart] = useQueryState<number | undefined>("t", undefined);
 | 
						const [start, setStart] = useQueryState<number | undefined>("t", undefined);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const { data, error } = useFetch(Player.query(slug));
 | 
						const { data, error } = useFetch(Player.query(slug));
 | 
				
			||||||
	const { data: info, error: infoError } = useFetch(Player.infoQuery(slug));
 | 
						const { data: info, error: infoError } = useFetch(Player.infoQuery(slug));
 | 
				
			||||||
	const metadata = mapMetadata(data);
 | 
						// TODO: map current entry using entries' duration & the current playtime
 | 
				
			||||||
 | 
						const currentEntry = 0;
 | 
				
			||||||
 | 
						const entry = data?.entries[currentEntry] ?? data?.entries[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const { apiUrl, authToken } = useToken();
 | 
						const { apiUrl, authToken } = useToken();
 | 
				
			||||||
	const [playMode] = useLocalSetting<"direct" | "hls">("playMode", "direct");
 | 
						const [playMode] = useLocalSetting<"direct" | "hls">("playMode", "direct");
 | 
				
			||||||
@ -44,15 +28,15 @@ export const Player = () => {
 | 
				
			|||||||
			headers: {
 | 
								headers: {
 | 
				
			||||||
				Authorization: `Bearer ${authToken}`,
 | 
									Authorization: `Bearer ${authToken}`,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			externalSubtitles: info?.subtitles
 | 
								// externalSubtitles: info?.subtitles
 | 
				
			||||||
				.filter((x) => x.link)
 | 
								// 	.filter((x) => x.link)
 | 
				
			||||||
				.map((x) => ({
 | 
								// 	.map((x) => ({
 | 
				
			||||||
					uri: x.link!,
 | 
								// 		uri: x.link!,
 | 
				
			||||||
					// TODO: translate this `Unknown`
 | 
								// 		// TODO: translate this `Unknown`
 | 
				
			||||||
					label: x.title ?? "Unknown",
 | 
								// 		label: x.title ?? "Unknown",
 | 
				
			||||||
					language: x.language ?? "und",
 | 
								// 		language: x.language ?? "und",
 | 
				
			||||||
					type: x.codec,
 | 
								// 		type: x.codec,
 | 
				
			||||||
				})),
 | 
								// 	})),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		(p) => {
 | 
							(p) => {
 | 
				
			||||||
			p.playWhenInactive = true;
 | 
								p.playWhenInactive = true;
 | 
				
			||||||
@ -111,9 +95,9 @@ export const Player = () => {
 | 
				
			|||||||
			}}
 | 
								}}
 | 
				
			||||||
		>
 | 
							>
 | 
				
			||||||
			<Head
 | 
								<Head
 | 
				
			||||||
				title={metadata?.title}
 | 
									title={entry ? `${entry.name} (${entryDisplayNumber(entry)})` : null}
 | 
				
			||||||
				description={metadata?.description}
 | 
									description={entry?.description}
 | 
				
			||||||
				image={metadata?.thumbnail?.high}
 | 
									image={data?.show?.thumbnail?.high}
 | 
				
			||||||
			/>
 | 
								/>
 | 
				
			||||||
			<Stack.Screen
 | 
								<Stack.Screen
 | 
				
			||||||
				options={{
 | 
									options={{
 | 
				
			||||||
@ -129,15 +113,24 @@ export const Player = () => {
 | 
				
			|||||||
				pictureInPicture
 | 
									pictureInPicture
 | 
				
			||||||
				autoEnterPictureInPicture
 | 
									autoEnterPictureInPicture
 | 
				
			||||||
				resizeMode={"contain"}
 | 
									resizeMode={"contain"}
 | 
				
			||||||
				controls
 | 
					 | 
				
			||||||
				style={StyleSheet.absoluteFillObject}
 | 
									style={StyleSheet.absoluteFillObject}
 | 
				
			||||||
			/>
 | 
								/>
 | 
				
			||||||
			<ContrastArea mode="dark">
 | 
								<ContrastArea mode="dark">
 | 
				
			||||||
				<LoadingIndicator player={player} />
 | 
									<LoadingIndicator player={player} />
 | 
				
			||||||
				<Controls
 | 
									<Controls
 | 
				
			||||||
					player={player}
 | 
										player={player}
 | 
				
			||||||
					title={metadata?.title}
 | 
										name={data?.show?.name}
 | 
				
			||||||
					subTitle={metadata?.subtitle}
 | 
										poster={data?.show?.poster}
 | 
				
			||||||
 | 
										subName={
 | 
				
			||||||
 | 
											entry
 | 
				
			||||||
 | 
												? [entryDisplayNumber(entry), entry.name]
 | 
				
			||||||
 | 
														.filter((x) => x)
 | 
				
			||||||
 | 
														.join(" - ")
 | 
				
			||||||
 | 
												: undefined
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										chapters={info?.chapters ?? []}
 | 
				
			||||||
 | 
										previous={data?.previous?.video}
 | 
				
			||||||
 | 
										next={data?.next?.video}
 | 
				
			||||||
				/>
 | 
									/>
 | 
				
			||||||
			</ContrastArea>
 | 
								</ContrastArea>
 | 
				
			||||||
		</View>
 | 
							</View>
 | 
				
			||||||
@ -147,7 +140,7 @@ export const Player = () => {
 | 
				
			|||||||
Player.query = (slug: string): QueryIdentifier<FullVideo> => ({
 | 
					Player.query = (slug: string): QueryIdentifier<FullVideo> => ({
 | 
				
			||||||
	path: ["api", "videos", slug],
 | 
						path: ["api", "videos", slug],
 | 
				
			||||||
	params: {
 | 
						params: {
 | 
				
			||||||
		fields: ["next", "previous", "show"],
 | 
							with: ["next", "previous", "show"],
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	parser: FullVideo,
 | 
						parser: FullVideo,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user