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