This commit is contained in:
Fredrik Burmester
2024-10-08 18:43:25 +02:00
parent ec0843d737
commit 317e719460
12 changed files with 100 additions and 79 deletions

View File

@@ -1,7 +1,7 @@
import { apiAtom } from "@/providers/JellyfinProvider";
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { Image } from "expo-image";
import { useAtom } from "jotai";
import { useAtom, useAtomValue } from "jotai";
import { useMemo, useState } from "react";
import { View } from "react-native";
import { WatchedIndicator } from "./WatchedIndicator";
@@ -18,7 +18,7 @@ const ContinueWatchingPoster: React.FC<ContinueWatchingPosterProps> = ({
useEpisodePoster = false,
size = "normal",
}) => {
const [api] = useAtom(apiAtom);
const api = useAtomValue(apiAtom);
/**
* Get horrizontal poster for movie and episode, with failover to primary.
@@ -59,7 +59,7 @@ const ContinueWatchingPoster: React.FC<ContinueWatchingPosterProps> = ({
} else {
return item.UserData?.PlayedPercentage || 0;
}
}, []);
}, [item]);
if (!url)
return (

View File

@@ -1,9 +1,5 @@
import { AudioTrackSelector } from "@/components/AudioTrackSelector";
import {
Bitrate,
BITRATES,
BitrateSelector,
} from "@/components/BitrateSelector";
import { Bitrate, BitrateSelector } from "@/components/BitrateSelector";
import { DownloadItem } from "@/components/DownloadItem";
import { OverviewText } from "@/components/OverviewText";
import { ParallaxScrollView } from "@/components/ParallaxPage";
@@ -18,6 +14,8 @@ import { SeasonEpisodesCarousel } from "@/components/series/SeasonEpisodesCarous
import { useImageColors } from "@/hooks/useImageColors";
import { apiAtom } from "@/providers/JellyfinProvider";
import { usePlaySettings } from "@/providers/PlaySettingsProvider";
import { useSettings } from "@/utils/atoms/settings";
import { getDefaultPlaySettings } from "@/utils/jellyfin/getDefaultPlaySettings";
import { getLogoImageUrlById } from "@/utils/jellyfin/image/getLogoImageUrlById";
import {
BaseItemDto,
@@ -27,23 +25,13 @@ import { Image } from "expo-image";
import { useFocusEffect, useNavigation } from "expo-router";
import * as ScreenOrientation from "expo-screen-orientation";
import { useAtom } from "jotai";
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { View } from "react-native";
import { useCastDevice } from "react-native-google-cast";
import Animated from "react-native-reanimated";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { Chromecast } from "./Chromecast";
import { ItemHeader } from "./ItemHeader";
import { MediaSourceSelector } from "./MediaSourceSelector";
import { MoreMoviesWithActor } from "./MoreMoviesWithActor";
import { useSettings } from "@/utils/atoms/settings";
import { getDefaultPlaySettings } from "@/utils/jellyfin/getDefaultPlaySettings";
export const ItemContent: React.FC<{ item: BaseItemDto }> = React.memo(
({ item }) => {
@@ -135,7 +123,7 @@ export const ItemContent: React.FC<{ item: BaseItemDto }> = React.memo(
};
}, []);
const headerHeightRef = useRef(400);
const [headerHeight, setHeaderHeight] = useState(350);
useImageColors({ item });
@@ -154,26 +142,18 @@ export const ItemContent: React.FC<{ item: BaseItemDto }> = React.memo(
</View>
),
});
// setPlaySettings((prev) => ({
// audioIndex: undefined,
// subtitleIndex: undefined,
// mediaSourceId: undefined,
// bitrate: undefined,
// mediaSource: item.MediaSources?.[0],
// item,
// }));
}, [item]);
useEffect(() => {
// If landscape
if (orientation !== ScreenOrientation.Orientation.PORTRAIT_UP) {
headerHeightRef.current = 230;
setHeaderHeight(230);
return;
}
if (item.Type === "Episode") headerHeightRef.current = 400;
else if (item.Type === "Movie") headerHeightRef.current = 500;
else headerHeightRef.current = 400;
}, [item, orientation]);
if (item.Type === "Movie") setHeaderHeight(500);
else setHeaderHeight(350);
}, [item.Type, orientation]);
const logoUrl = useMemo(() => getLogoImageUrlById({ api, item }), [item]);
@@ -193,22 +173,20 @@ export const ItemContent: React.FC<{ item: BaseItemDto }> = React.memo(
>
<ParallaxScrollView
className={`flex-1 ${loading ? "opacity-0" : "opacity-100"}`}
headerHeight={headerHeightRef.current}
headerHeight={headerHeight}
headerImage={
<>
<Animated.View style={[{ flex: 1 }]}>
<ItemImage
variant={
item.Type === "Movie" && logoUrl ? "Backdrop" : "Primary"
}
item={item}
style={{
width: "100%",
height: "100%",
}}
/>
</Animated.View>
</>
<View style={[{ flex: 1 }]}>
<ItemImage
variant={
item.Type === "Movie" && logoUrl ? "Backdrop" : "Primary"
}
item={item}
style={{
width: "100%",
height: "100%",
}}
/>
</View>
}
logo={
<>

View File

@@ -4,11 +4,11 @@ import { getParentBackdropImageUrl } from "@/utils/jellyfin/image/getParentBackd
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
import { runtimeTicksToMinutes } from "@/utils/time";
import { useActionSheet } from "@expo/react-native-action-sheet";
import { Feather, Ionicons } from "@expo/vector-icons";
import { Feather, Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { useAtom } from "jotai";
import { useEffect, useMemo } from "react";
import { TouchableOpacity, View } from "react-native";
import { Linking, TouchableOpacity, View } from "react-native";
import CastContext, {
PlayServicesState,
useMediaStatus,
@@ -27,6 +27,7 @@ import Animated, {
import { Button } from "./Button";
import { Text } from "./common/Text";
import { useRouter } from "expo-router";
import { useSettings } from "@/utils/atoms/settings";
interface Props extends React.ComponentProps<typeof Button> {
item?: BaseItemDto | null;
@@ -55,6 +56,7 @@ export const PlayButton: React.FC<Props> = ({ item, url, ...props }) => {
const startColor = useSharedValue(memoizedColor);
const widthProgress = useSharedValue(0);
const colorChangeProgress = useSharedValue(0);
const [settings] = useSettings();
const directStream = useMemo(() => {
return !url?.includes("m3u8");
@@ -69,10 +71,18 @@ export const PlayButton: React.FC<Props> = ({ item, url, ...props }) => {
);
return;
}
if (!client) {
const vlcLink = "vlc://" + url;
if (vlcLink && settings?.openInVLC) {
Linking.openURL(vlcLink);
return;
}
router.push("/play-video");
return;
}
const options = ["Chromecast", "Device", "Cancel"];
const cancelButtonIndex = 2;
showActionSheetWithOptions(
@@ -310,6 +320,15 @@ export const PlayButton: React.FC<Props> = ({ item, url, ...props }) => {
<Feather name="cast" size={22} />
</Animated.Text>
)}
{!client && settings?.openInVLC && (
<Animated.Text style={animatedTextStyle}>
<MaterialCommunityIcons
name="vlc"
size={18}
color={animatedTextStyle.color}
/>
</Animated.Text>
)}
</View>
</View>
</TouchableOpacity>

View File

@@ -44,6 +44,9 @@ export const PlayedStatus: React.FC<Props> = ({ item, ...props }) => {
queryClient.invalidateQueries({
queryKey: ["nextUp-all"],
});
queryClient.invalidateQueries({
queryKey: ["home"],
});
};
return (

View File

@@ -1,4 +1,5 @@
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import React from "react";
import { View } from "react-native";
export const WatchedIndicator: React.FC<{ item: BaseItemDto }> = ({ item }) => {

View File

@@ -9,10 +9,8 @@ import {
import { ScrollView, View, ViewProps } from "react-native";
import ContinueWatchingPoster from "../ContinueWatchingPoster";
import { ItemCardText } from "../ItemCardText";
import { HorizontalScroll } from "../common/HorrizontalScroll";
import { TouchableItemRouter } from "../common/TouchableItemRouter";
import SeriesPoster from "../posters/SeriesPoster";
import { FlashList } from "@shopify/flash-list";
interface Props extends ViewProps {
title?: string | null;
@@ -34,7 +32,7 @@ export const ScrollingCollectionList: React.FC<Props> = ({
queryKey,
queryFn,
enabled: !disabled,
staleTime: 60 * 1000,
staleTime: 0,
});
if (disabled || !title) return null;

View File

@@ -31,10 +31,10 @@ export const MediaListSection: React.FC<Props> = ({
const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom);
const { data: collection, isLoading } = useQuery({
const { data: collection } = useQuery({
queryKey,
queryFn,
staleTime: 60 * 1000,
staleTime: 0,
});
const fetchItems = useCallback(