mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-09-29 15:30:53 -04:00
Implement player's enEnd
& loading indicator
This commit is contained in:
parent
908b120a7c
commit
ee32ecd527
@ -7,16 +7,16 @@ export * from "./divider";
|
|||||||
export * from "./icons";
|
export * from "./icons";
|
||||||
export * from "./image";
|
export * from "./image";
|
||||||
export * from "./image-background";
|
export * from "./image-background";
|
||||||
// export * from "./popup";
|
|
||||||
// export * from "./select";
|
|
||||||
export * from "./input";
|
export * from "./input";
|
||||||
export * from "./links";
|
export * from "./links";
|
||||||
// export * from "./progress";
|
|
||||||
// export * from "./slider";
|
|
||||||
// export * from "./snackbar";
|
// export * from "./snackbar";
|
||||||
// export * from "./alert";
|
// export * from "./alert";
|
||||||
export * from "./menu";
|
export * from "./menu";
|
||||||
|
export * from "./progress";
|
||||||
|
// export * from "./popup";
|
||||||
|
export * from "./select";
|
||||||
export * from "./skeleton";
|
export * from "./skeleton";
|
||||||
|
export * from "./slider";
|
||||||
export * from "./text";
|
export * from "./text";
|
||||||
export * from "./theme";
|
export * from "./theme";
|
||||||
export * from "./tooltip";
|
export * from "./tooltip";
|
||||||
|
@ -1,26 +1,5 @@
|
|||||||
/*
|
import { ActivityIndicator } from "react-native";
|
||||||
* Kyoo - A portable and vast media library solution.
|
import { type Stylable, useYoshiki } from "yoshiki/native";
|
||||||
* 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 { ActivityIndicator, Platform, View } from "react-native";
|
|
||||||
import { Circle, Svg } from "react-native-svg";
|
|
||||||
import { px, type Stylable, useYoshiki } from "yoshiki/native";
|
|
||||||
|
|
||||||
export const CircularProgress = ({
|
export const CircularProgress = ({
|
||||||
size = 48,
|
size = 48,
|
||||||
@ -28,64 +7,9 @@ export const CircularProgress = ({
|
|||||||
color,
|
color,
|
||||||
...props
|
...props
|
||||||
}: { size?: number; tickness?: number; color?: string } & Stylable) => {
|
}: { size?: number; tickness?: number; color?: string } & Stylable) => {
|
||||||
const { css, theme } = useYoshiki();
|
const { theme } = useYoshiki();
|
||||||
|
|
||||||
if (Platform.OS !== "web")
|
|
||||||
return (
|
return (
|
||||||
<ActivityIndicator size={size} color={color ?? theme.accent} {...props} />
|
<ActivityIndicator size={size} color={color ?? theme.accent} {...props} />
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
|
||||||
<View {...css({ width: size, height: size, overflow: "hidden" }, props)}>
|
|
||||||
<style jsx global>{`
|
|
||||||
@keyframes circularProgress-svg {
|
|
||||||
0% {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@keyframes circularProgress-circle {
|
|
||||||
0% {
|
|
||||||
stroke-dasharray: 1px, 200px;
|
|
||||||
stroke-dashoffset: 0;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
stroke-dasharray: 100px, 200px;
|
|
||||||
stroke-dashoffset: -15px;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
stroke-dasharray: 100px, 200px;
|
|
||||||
stroke-dashoffset: -125px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`}</style>
|
|
||||||
<Svg
|
|
||||||
viewBox={`${size / 2} ${size / 2} ${size} ${size}`}
|
|
||||||
{...css(
|
|
||||||
// @ts-ignore Web only
|
|
||||||
Platform.OS === "web" && {
|
|
||||||
animation: "circularProgress-svg 1.4s ease-in-out infinite",
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Circle
|
|
||||||
cx={size}
|
|
||||||
cy={size}
|
|
||||||
r={(size - tickness) / 2}
|
|
||||||
strokeWidth={tickness}
|
|
||||||
fill="none"
|
|
||||||
stroke={color ?? theme.accent}
|
|
||||||
strokeDasharray={[px(80), px(200)]}
|
|
||||||
{...css(
|
|
||||||
Platform.OS === "web" && {
|
|
||||||
// @ts-ignore Web only
|
|
||||||
animation: "circularProgress-circle 1.4s ease-in-out infinite",
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Svg>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
@ -1,23 +1,3 @@
|
|||||||
/*
|
|
||||||
* 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 ExpandMore from "@material-symbols/svg-400/rounded/keyboard_arrow_down-fill.svg";
|
import ExpandMore from "@material-symbols/svg-400/rounded/keyboard_arrow_down-fill.svg";
|
||||||
import { Button } from "./button";
|
import { Button } from "./button";
|
||||||
import { Icon } from "./icons";
|
import { Icon } from "./icons";
|
||||||
|
@ -1,26 +1,10 @@
|
|||||||
/*
|
|
||||||
* 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 { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import { type GestureResponderEvent, Platform, View } from "react-native";
|
import {
|
||||||
import type { ViewProps } from "react-native-svg/lib/typescript/fabric/utils";
|
type GestureResponderEvent,
|
||||||
|
Platform,
|
||||||
|
View,
|
||||||
|
type ViewProps,
|
||||||
|
} from "react-native";
|
||||||
import { percent, px, useYoshiki } from "yoshiki/native";
|
import { percent, px, useYoshiki } from "yoshiki/native";
|
||||||
import { focusReset } from "./utils";
|
import { focusReset } from "./utils";
|
||||||
|
|
||||||
|
@ -1,25 +1,24 @@
|
|||||||
/*
|
import ArrowBack from "@material-symbols/svg-400/rounded/arrow_back-fill.svg";
|
||||||
* 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 type { Audio, Chapter, KyooImage, Subtitle } from "@kyoo/models";
|
|
||||||
import {
|
import {
|
||||||
|
type ReactNode,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import {
|
||||||
|
type ImageStyle,
|
||||||
|
Platform,
|
||||||
|
Pressable,
|
||||||
|
View,
|
||||||
|
type ViewProps,
|
||||||
|
} from "react-native";
|
||||||
|
import { useEvent, type VideoPlayer } from "react-native-video";
|
||||||
|
import { percent, rem, useYoshiki } from "yoshiki/native";
|
||||||
|
import type { AudioTrack, Chapter, KImage, Subtitle } from "~/models";
|
||||||
|
import {
|
||||||
|
alpha,
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
ContrastArea,
|
ContrastArea,
|
||||||
H1,
|
H1,
|
||||||
@ -30,43 +29,14 @@ import {
|
|||||||
Skeleton,
|
Skeleton,
|
||||||
Slider,
|
Slider,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
alpha,
|
|
||||||
imageBorderRadius,
|
|
||||||
tooltip,
|
tooltip,
|
||||||
ts,
|
ts,
|
||||||
useIsTouch,
|
useIsTouch,
|
||||||
} from "@kyoo/primitives";
|
} from "~/primitives";
|
||||||
import ArrowBack from "@material-symbols/svg-400/rounded/arrow_back-fill.svg";
|
|
||||||
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
|
||||||
import { atom } from "jotai";
|
|
||||||
import { type ReactNode, useCallback, useEffect, useRef, useState } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { type ImageStyle, Platform, Pressable, View, type ViewProps } from "react-native";
|
|
||||||
import { useRouter } from "solito/router";
|
|
||||||
import { percent, rem, useYoshiki } from "yoshiki/native";
|
|
||||||
import {
|
|
||||||
bufferedAtom,
|
|
||||||
durationAtom,
|
|
||||||
fullscreenAtom,
|
|
||||||
loadAtom,
|
|
||||||
playAtom,
|
|
||||||
progressAtom,
|
|
||||||
} from "../state";
|
|
||||||
import { LeftButtons, TouchControls } from "./left-buttons";
|
import { LeftButtons, TouchControls } from "./left-buttons";
|
||||||
import { RightButtons } from "./right-buttons";
|
import { RightButtons } from "./right-buttons";
|
||||||
import { BottomScrubber, ScrubberTooltip } from "./scrubber";
|
import { BottomScrubber, ScrubberTooltip } from "./scrubber";
|
||||||
|
|
||||||
const hoverReasonAtom = atom({
|
|
||||||
mouseMoved: false,
|
|
||||||
mouseHover: false,
|
|
||||||
menuOpened: false,
|
|
||||||
});
|
|
||||||
export const hoverAtom = atom((get) =>
|
|
||||||
[!get(playAtom), ...Object.values(get(hoverReasonAtom))].includes(true),
|
|
||||||
);
|
|
||||||
export const seekingAtom = atom(false);
|
|
||||||
export const seekProgressAtom = atom<number | null>(null);
|
|
||||||
|
|
||||||
export const Hover = ({
|
export const Hover = ({
|
||||||
isLoading,
|
isLoading,
|
||||||
url,
|
url,
|
||||||
@ -145,7 +115,11 @@ export const Hover = ({
|
|||||||
padding: percent(1),
|
padding: percent(1),
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<VideoPoster poster={poster} alt={showName} isLoading={isLoading} />
|
<VideoPoster
|
||||||
|
poster={poster}
|
||||||
|
alt={showName}
|
||||||
|
isLoading={isLoading}
|
||||||
|
/>
|
||||||
<View
|
<View
|
||||||
{...css({
|
{...css({
|
||||||
marginLeft: { xs: ts(0.5), sm: ts(3) },
|
marginLeft: { xs: ts(0.5), sm: ts(3) },
|
||||||
@ -157,7 +131,11 @@ export const Hover = ({
|
|||||||
>
|
>
|
||||||
{!showBottomSeeker && (
|
{!showBottomSeeker && (
|
||||||
<H2 numberOfLines={1} {...css({ paddingBottom: ts(1) })}>
|
<H2 numberOfLines={1} {...css({ paddingBottom: ts(1) })}>
|
||||||
{isLoading ? <Skeleton {...css({ width: rem(15), height: rem(2) })} /> : name}
|
{isLoading ? (
|
||||||
|
<Skeleton {...css({ width: rem(15), height: rem(2) })} />
|
||||||
|
) : (
|
||||||
|
name
|
||||||
|
)}
|
||||||
</H2>
|
</H2>
|
||||||
)}
|
)}
|
||||||
<ProgressBar chapters={chapters} url={url} />
|
<ProgressBar chapters={chapters} url={url} />
|
||||||
@ -172,15 +150,24 @@ export const Hover = ({
|
|||||||
flexWrap: "wrap",
|
flexWrap: "wrap",
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<LeftButtons previousSlug={previousSlug} nextSlug={nextSlug} />
|
<LeftButtons
|
||||||
|
previousSlug={previousSlug}
|
||||||
|
nextSlug={nextSlug}
|
||||||
|
/>
|
||||||
<RightButtons
|
<RightButtons
|
||||||
subtitles={subtitles}
|
subtitles={subtitles}
|
||||||
audios={audios}
|
audios={audios}
|
||||||
fonts={fonts}
|
fonts={fonts}
|
||||||
onMenuOpen={() => setHover((x) => ({ ...x, menuOpened: true }))}
|
onMenuOpen={() =>
|
||||||
|
setHover((x) => ({ ...x, menuOpened: true }))
|
||||||
|
}
|
||||||
onMenuClose={() => {
|
onMenuClose={() => {
|
||||||
// Disable hover since the menu overlay makes the mouseout unreliable.
|
// Disable hover since the menu overlay makes the mouseout unreliable.
|
||||||
setHover((x) => ({ ...x, menuOpened: false, mouseHover: false }));
|
setHover((x) => ({
|
||||||
|
...x,
|
||||||
|
menuOpened: false,
|
||||||
|
mouseHover: false,
|
||||||
|
}));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
@ -198,7 +185,9 @@ export const HoverTouch = ({ children, ...props }: { children: ReactNode }) => {
|
|||||||
const hover = useAtomValue(hoverAtom);
|
const hover = useAtomValue(hoverAtom);
|
||||||
const setHover = useSetAtom(hoverReasonAtom);
|
const setHover = useSetAtom(hoverReasonAtom);
|
||||||
const mouseCallback = useRef<NodeJS.Timeout | null>(null);
|
const mouseCallback = useRef<NodeJS.Timeout | null>(null);
|
||||||
const touch = useRef<{ count: number; timeout?: NodeJS.Timeout }>({ count: 0 });
|
const touch = useRef<{ count: number; timeout?: NodeJS.Timeout }>({
|
||||||
|
count: 0,
|
||||||
|
});
|
||||||
const playerWidth = useRef<number | null>(null);
|
const playerWidth = useRef<number | null>(null);
|
||||||
const isTouch = useIsTouch();
|
const isTouch = useIsTouch();
|
||||||
|
|
||||||
@ -226,7 +215,8 @@ export const HoverTouch = ({ children, ...props }: { children: ReactNode }) => {
|
|||||||
// It also serves to hide the tooltip.
|
// It also serves to hide the tooltip.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Platform.OS !== "web") return;
|
if (Platform.OS !== "web") return;
|
||||||
if (!hover && document.activeElement instanceof HTMLElement) document.activeElement.blur();
|
if (!hover && document.activeElement instanceof HTMLElement)
|
||||||
|
document.activeElement.blur();
|
||||||
}, [hover]);
|
}, [hover]);
|
||||||
|
|
||||||
const { css } = useYoshiki();
|
const { css } = useYoshiki();
|
||||||
@ -282,7 +272,8 @@ export const HoverTouch = ({ children, ...props }: { children: ReactNode }) => {
|
|||||||
<Pressable
|
<Pressable
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
onPointerLeave={(e) => {
|
onPointerLeave={(e) => {
|
||||||
if (e.nativeEvent.pointerType === "mouse") setHover((x) => ({ ...x, mouseMoved: false }));
|
if (e.nativeEvent.pointerType === "mouse")
|
||||||
|
setHover((x) => ({ ...x, mouseMoved: false }));
|
||||||
}}
|
}}
|
||||||
onPress={(e) => {
|
onPress={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -315,7 +306,13 @@ export const HoverTouch = ({ children, ...props }: { children: ReactNode }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ProgressBar = ({ url, chapters }: { url: string; chapters?: Chapter[] }) => {
|
const ProgressBar = ({
|
||||||
|
url,
|
||||||
|
chapters,
|
||||||
|
}: {
|
||||||
|
url: string;
|
||||||
|
chapters?: Chapter[];
|
||||||
|
}) => {
|
||||||
const [progress, setProgress] = useAtom(progressAtom);
|
const [progress, setProgress] = useAtom(progressAtom);
|
||||||
const buffered = useAtomValue(bufferedAtom);
|
const buffered = useAtomValue(bufferedAtom);
|
||||||
const duration = useAtomValue(durationAtom);
|
const duration = useAtomValue(durationAtom);
|
||||||
@ -353,10 +350,17 @@ const ProgressBar = ({ url, chapters }: { url: string; chapters?: Chapter[] }) =
|
|||||||
id={"progress-scrubber"}
|
id={"progress-scrubber"}
|
||||||
isOpen={hoverProgress !== null}
|
isOpen={hoverProgress !== null}
|
||||||
place="top"
|
place="top"
|
||||||
position={{ x: layout.x + (layout.width * hoverProgress!) / (duration ?? 1), y: layout.y }}
|
position={{
|
||||||
|
x: layout.x + (layout.width * hoverProgress!) / (duration ?? 1),
|
||||||
|
y: layout.y,
|
||||||
|
}}
|
||||||
render={() =>
|
render={() =>
|
||||||
hoverProgress ? (
|
hoverProgress ? (
|
||||||
<ScrubberTooltip seconds={hoverProgress} chapters={chapters} url={url} />
|
<ScrubberTooltip
|
||||||
|
seconds={hoverProgress}
|
||||||
|
chapters={chapters}
|
||||||
|
url={url}
|
||||||
|
/>
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
opacity={1}
|
opacity={1}
|
||||||
@ -449,9 +453,13 @@ const VideoPoster = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LoadingIndicator = () => {
|
export const LoadingIndicator = ({ player }: { player: VideoPlayer }) => {
|
||||||
const isLoading = useAtomValue(loadAtom);
|
|
||||||
const { css } = useYoshiki();
|
const { css } = useYoshiki();
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
useEvent(player, "onStatusChange", (status) => {
|
||||||
|
setLoading(status === "loading");
|
||||||
|
});
|
||||||
|
|
||||||
if (!isLoading) return null;
|
if (!isLoading) return null;
|
||||||
|
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import { Stack } from "expo-router";
|
import { Stack, useRouter } from "expo-router";
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import { StyleSheet, View } from "react-native";
|
import { StyleSheet, View } from "react-native";
|
||||||
import { useVideoPlayer, VideoView, VideoViewRef } from "react-native-video";
|
import {
|
||||||
|
useEvent,
|
||||||
|
useVideoPlayer,
|
||||||
|
VideoView,
|
||||||
|
VideoViewRef,
|
||||||
|
} from "react-native-video";
|
||||||
import { entryDisplayNumber } from "~/components/entries";
|
import { entryDisplayNumber } from "~/components/entries";
|
||||||
import { FullVideo, VideoInfo } from "~/models";
|
import { FullVideo, VideoInfo } from "~/models";
|
||||||
import { Head } from "~/primitives";
|
import { Head } from "~/primitives";
|
||||||
@ -9,6 +14,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 "./components/hover";
|
||||||
|
|
||||||
// import { Hover, LoadingIndicator } from "./components/hover";
|
// import { Hover, LoadingIndicator } from "./components/hover";
|
||||||
// import { useVideoKeyboard } from "./keyboard";
|
// import { useVideoKeyboard } from "./keyboard";
|
||||||
@ -33,7 +39,8 @@ const mapMetadata = (item: FullVideo | undefined) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Player = () => {
|
export const Player = () => {
|
||||||
const [slug] = useQueryState("slug", undefined!);
|
const [slug, setSlug] = useQueryState<string>("slug", 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));
|
||||||
@ -60,10 +67,25 @@ export const Player = () => {
|
|||||||
(p) => {
|
(p) => {
|
||||||
p.playWhenInactive = true;
|
p.playWhenInactive = true;
|
||||||
p.playInBackground = true;
|
p.playInBackground = true;
|
||||||
|
const seek = start ?? data?.progress.time;
|
||||||
|
// TODO: fix console.error bellow
|
||||||
|
if (seek) p.seekTo(seek);
|
||||||
|
else console.error("Player got ready before progress info was loaded.");
|
||||||
p.play();
|
p.play();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
useEvent(player, "onEnd", () => {
|
||||||
|
if (!data) return;
|
||||||
|
if (data.next) {
|
||||||
|
setStart(0);
|
||||||
|
setSlug(data.next.video);
|
||||||
|
} else {
|
||||||
|
router.navigate(data.show!.href);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// const [playbackError, setPlaybackError] = useState<string | undefined>(
|
// const [playbackError, setPlaybackError] = useState<string | undefined>(
|
||||||
// undefined,
|
// undefined,
|
||||||
// );
|
// );
|
||||||
@ -120,7 +142,7 @@ export const Player = () => {
|
|||||||
controls
|
controls
|
||||||
style={StyleSheet.absoluteFillObject}
|
style={StyleSheet.absoluteFillObject}
|
||||||
/>
|
/>
|
||||||
{/* <LoadingIndicator /> */}
|
<LoadingIndicator player={player} />
|
||||||
{/* <Hover {...mapData(data, info, previous, next)} url={`${type}/${slug}`} /> */}
|
{/* <Hover {...mapData(data, info, previous, next)} url={`${type}/${slug}`} /> */}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user