fix: refactor

This commit is contained in:
Fredrik Burmester
2024-08-15 16:46:50 +02:00
parent 92a260b6f5
commit aa0fbd6fad
5 changed files with 229 additions and 140 deletions

View File

@@ -31,6 +31,8 @@ import NetInfo, { NetInfoState } from "@react-native-community/netinfo";
import { Button } from "@/components/Button";
import { Ionicons } from "@expo/vector-icons";
import MoviePoster from "@/components/MoviePoster";
import { ScrollingCollectionList } from "@/components/home/ScrollingCollectionList";
import { useSettings } from "@/utils/atoms/settings";
export default function index() {
const router = useRouter();
@@ -40,6 +42,7 @@ export default function index() {
const [user] = useAtom(userAtom);
const [loading, setLoading] = useState(false);
const [settings, _] = useSettings();
const { data, isLoading, isError } = useQuery<BaseItemDto[]>({
queryKey: ["resumeItems", user?.Id],
@@ -55,7 +58,7 @@ export default function index() {
staleTime: 60,
});
const { data: _nextUpData } = useQuery({
const { data: _nextUpData, isLoading: isLoadingNextUp } = useQuery({
queryKey: ["nextUp-all", user?.Id],
queryFn: async () =>
(api &&
@@ -74,7 +77,7 @@ export default function index() {
return _nextUpData?.filter((i) => !data?.find((d) => d.Id === i.Id));
}, [_nextUpData]);
const { data: collections } = useQuery({
const { data: collections, isLoading: isLoadingCollections } = useQuery({
queryKey: ["collections", user?.Id],
queryFn: async () => {
if (!api || !user?.Id) {
@@ -87,6 +90,8 @@ export default function index() {
})
).data;
console.log("Collections", JSON.stringify(data.Items));
const order = ["boxsets", "tvshows", "movies"];
const cs = data.Items?.sort((a, b) => {
@@ -113,7 +118,10 @@ export default function index() {
return collections?.find((c) => c.CollectionType === "tvshows")?.Id;
}, [collections]);
const { data: recentlyAddedInMovies } = useQuery<BaseItemDto[]>({
const {
data: recentlyAddedInMovies,
isLoading: isLoadingRecentlyAddedMovies,
} = useQuery<BaseItemDto[]>({
queryKey: ["recentlyAddedInMovies", user?.Id, movieCollectionId],
queryFn: async () =>
(api &&
@@ -132,7 +140,10 @@ export default function index() {
staleTime: 60,
});
const { data: recentlyAddedInTVShows } = useQuery<BaseItemDto[]>({
const {
data: recentlyAddedInTVShows,
isLoading: isLoadingRecentlyAddedTVShows,
} = useQuery<BaseItemDto[]>({
queryKey: ["recentlyAddedInTVShows", user?.Id, tvShowCollectionId],
queryFn: async () =>
(api &&
@@ -151,7 +162,9 @@ export default function index() {
staleTime: 60,
});
const { data: suggestions } = useQuery<BaseItemDto[]>({
const { data: suggestions, isLoading: isLoadingSuggestions } = useQuery<
BaseItemDto[]
>({
queryKey: ["suggestions", user?.Id],
queryFn: async () =>
(api &&
@@ -167,6 +180,46 @@ export default function index() {
staleTime: 60,
});
const { data: mediaListCollection } = useQuery<string | null>({
queryKey: ["mediaListCollection", user?.Id],
queryFn: async () => {
if (!api || !user?.Id) return null;
const response = await getItemsApi(api).getItems({
userId: user.Id,
tags: ["medialist", "promoted"],
recursive: true,
fields: ["Tags"],
includeItemTypes: ["BoxSet"],
});
return response.data.Items?.[0].Id || null;
},
enabled: !!api && !!user?.Id && settings?.usePopularPlugin === true,
staleTime: 60,
});
const { data: popularItems, isLoading: isLoadingPopular } = useQuery<
BaseItemDto[]
>({
queryKey: ["popular", user?.Id],
queryFn: async () => {
if (!api || !user?.Id || !mediaListCollection) return [];
const response = await getItemsApi(api).getItems({
userId: user.Id,
parentId: mediaListCollection,
limit: 10,
});
console.log("Popular", response.data.Items?.length);
return response.data.Items || [];
},
enabled: !!api && !!user?.Id && !!mediaListCollection,
staleTime: 60,
});
const refetch = useCallback(async () => {
setLoading(true);
await queryClient.refetchQueries({ queryKey: ["resumeItems", user?.Id] });
@@ -243,123 +296,52 @@ export default function index() {
}
>
<View className="flex flex-col pt-4 pb-24 gap-y-4">
<View>
<Text className="px-4 text-2xl font-bold mb-2">
Continue Watching
</Text>
<HorizontalScroll<BaseItemDto>
data={data}
renderItem={(item, index) => (
<TouchableOpacity
key={index}
onPress={() => router.push(`/items/${item.Id}/page`)}
className="flex flex-col w-48"
>
<View>
<ContinueWatchingPoster item={item} />
<ItemCardText item={item} />
</View>
</TouchableOpacity>
)}
/>
</View>
<ScrollingCollectionList
title="Continue Watching"
data={data}
loading={isLoading}
orientation="horizontal"
/>
<View>
<Text className="px-4 text-2xl font-bold mb-2">Next Up</Text>
<HorizontalScroll<BaseItemDto>
data={nextUpData}
renderItem={(item, index) => (
<TouchableOpacity
key={index}
onPress={() => router.push(`/items/${item.Id}/page`)}
className="flex flex-col w-48"
>
<View>
<ContinueWatchingPoster item={item} />
<ItemCardText item={item} />
</View>
</TouchableOpacity>
)}
/>
</View>
<ScrollingCollectionList
title="Popular"
data={popularItems}
loading={isLoadingPopular}
disabled={!mediaListCollection}
/>
<View>
<Text className="px-4 text-2xl font-bold mb-2">
Recently Added in Movies
</Text>
<HorizontalScroll<BaseItemDto>
data={recentlyAddedInMovies}
renderItem={(item, index) => (
<TouchableOpacity
key={index}
onPress={() => router.push(`/items/${item.Id}/page`)}
className="flex flex-col w-32"
>
<View>
<MoviePoster item={item} />
<ItemCardText item={item} />
</View>
</TouchableOpacity>
)}
/>
</View>
<ScrollingCollectionList
title="Next Up"
data={nextUpData}
loading={isLoadingNextUp}
orientation="horizontal"
/>
<View>
<Text className="px-4 text-2xl font-bold mb-2">
Recently Added in TV-Shows
</Text>
<HorizontalScroll<BaseItemDto>
data={recentlyAddedInTVShows}
renderItem={(item, index) => (
<TouchableOpacity
key={index}
onPress={() => router.push(`/series/${item.Id}/page`)}
className="flex flex-col w-32"
>
<View>
<MoviePoster item={item} />
<ItemCardText item={item} />
</View>
</TouchableOpacity>
)}
/>
</View>
<ScrollingCollectionList
title="Recently Added in Movies"
data={recentlyAddedInMovies}
loading={isLoadingRecentlyAddedMovies}
/>
<View>
<Text className="px-4 text-2xl font-bold mb-2">Collections</Text>
<HorizontalScroll<BaseItemDto>
data={collections}
renderItem={(item, index) => (
<TouchableOpacity
key={index}
onPress={() => router.push(`/collections/${item.Id}/page`)}
className="flex flex-col w-48"
>
<View>
<ContinueWatchingPoster item={item} />
<ItemCardText item={item} />
</View>
</TouchableOpacity>
)}
/>
</View>
<ScrollingCollectionList
title="Recently Added in TV-Shows"
data={recentlyAddedInTVShows}
loading={isLoadingRecentlyAddedTVShows}
/>
<View>
<Text className="px-4 text-2xl font-bold mb-2">Suggestions</Text>
<HorizontalScroll<BaseItemDto>
data={suggestions}
renderItem={(item, index) => (
<TouchableOpacity
key={index}
onPress={() => router.push(`/items/${item.Id}/page`)}
className="flex flex-col w-48"
>
<ContinueWatchingPoster item={item} />
<ItemCardText item={item} />
</TouchableOpacity>
)}
/>
</View>
<ScrollingCollectionList
title="Collections"
data={collections}
loading={isLoadingCollections}
orientation="horizontal"
/>
<ScrollingCollectionList
title="Suggestions"
data={suggestions}
loading={isLoadingSuggestions}
orientation="horizontal"
/>
</View>
</ScrollView>
);

View File

@@ -11,6 +11,7 @@ import Animated, {
useSharedValue,
withTiming,
} from "react-native-reanimated";
import { Text } from "./Text";
interface HorizontalScrollProps<T> extends ScrollViewProps {
data?: T[] | null;
@@ -19,18 +20,33 @@ interface HorizontalScrollProps<T> extends ScrollViewProps {
contentContainerStyle?: ViewStyle;
loadingContainerStyle?: ViewStyle;
height?: number;
loading?: boolean;
}
export function HorizontalScroll<T>({
data,
data = [],
renderItem,
containerStyle,
contentContainerStyle,
loadingContainerStyle,
loading = false,
height = 164,
...props
}: HorizontalScrollProps<T>): React.ReactElement {
if (!data) {
const animatedOpacity = useSharedValue(0);
const animatedStyle1 = useAnimatedStyle(() => {
return {
opacity: withTiming(animatedOpacity.value, { duration: 250 }),
};
});
useEffect(() => {
if (data) {
animatedOpacity.value = 1;
}
}, [data]);
if (data === undefined || data === null || loading) {
return (
<View
style={[
@@ -38,7 +54,6 @@ export function HorizontalScroll<T>({
flex: 1,
justifyContent: "center",
alignItems: "center",
height,
},
loadingContainerStyle,
]}
@@ -48,18 +63,6 @@ export function HorizontalScroll<T>({
);
}
const opacity = useSharedValue(0);
const animatedStyle = useAnimatedStyle(() => {
return {
opacity: withTiming(opacity.value, { duration: 250 }),
};
});
useEffect(() => {
if (data && data.length > 0) opacity.value = 1;
}, [data]);
return (
<ScrollView
horizontal
@@ -72,13 +75,18 @@ export function HorizontalScroll<T>({
className={`
flex flex-row px-4
`}
style={[animatedStyle]}
style={[animatedStyle1]}
>
{data?.map((item, index) => (
{data.map((item, index) => (
<View className="mr-2" key={index}>
<React.Fragment>{renderItem(item, index)}</React.Fragment>
</View>
))}
{data.length === 0 && (
<View className="flex-1 justify-center items-center">
<Text className="text-center text-gray-500">No data available</Text>
</View>
)}
</Animated.View>
</ScrollView>
);

View File

@@ -1,3 +1,4 @@
import { useFocusEffect } from "expo-router";
import React, { useEffect } from "react";
import { TextInputProps, TextProps } from "react-native";
import { TextInput } from "react-native";
@@ -5,9 +6,11 @@ export function Input(props: TextInputProps) {
const { style, ...otherProps } = props;
const inputRef = React.useRef<TextInput>(null);
useEffect(() => {
inputRef.current?.focus();
}, []);
useFocusEffect(
React.useCallback(() => {
inputRef.current?.focus();
}, []),
);
return (
<TextInput
@@ -17,6 +20,7 @@ export function Input(props: TextInputProps) {
style={[{ color: "white" }, style]}
{...otherProps}
placeholderTextColor={"#9CA3AF"}
clearButtonMode="while-editing"
/>
);
}

View File

@@ -0,0 +1,64 @@
import { TouchableOpacity, View, ViewProps } from "react-native";
import { Text } from "@/components/common/Text";
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { router } from "expo-router";
import ContinueWatchingPoster from "../ContinueWatchingPoster";
import { ItemCardText } from "../ItemCardText";
import { HorizontalScroll } from "../common/HorrizontalScroll";
import MoviePoster from "../MoviePoster";
interface Props extends ViewProps {
title: string;
loading?: boolean;
orientation?: "horizontal" | "vertical";
data?: BaseItemDto[] | null;
height?: "small" | "large";
disabled?: boolean;
}
export const ScrollingCollectionList: React.FC<Props> = ({
title,
data,
orientation = "vertical",
height = "small",
loading = false,
disabled = false,
...props
}) => {
if (disabled) return null;
return (
<View {...props}>
<Text className="px-4 text-2xl font-bold mb-2">{title}</Text>
<HorizontalScroll<BaseItemDto>
data={data}
height={orientation === "vertical" ? 247 : 164}
loading={loading}
renderItem={(item, index) => (
<TouchableOpacity
key={index}
onPress={() => {
if (item.Type === "Series")
router.push(`/series/${item.Id}/page`);
else if (item.Type === "CollectionFolder")
router.push(`/collections/${item.Id}/page`);
else router.push(`/items/${item.Id}/page`);
}}
className={`flex flex-col
${orientation === "vertical" ? "w-32" : "w-48"}
`}
>
<View>
{orientation === "vertical" ? (
<MoviePoster item={item} />
) : (
<ContinueWatchingPoster item={item} />
)}
<ItemCardText item={item} />
</View>
</TouchableOpacity>
)}
/>
</View>
);
};

View File

@@ -1,4 +1,4 @@
import { Switch, View } from "react-native";
import { Linking, Switch, TouchableOpacity, View } from "react-native";
import { Text } from "../common/Text";
import { useAtom } from "jotai";
import { useSettings } from "@/utils/atoms/settings";
@@ -9,14 +9,26 @@ export const SettingToggles: React.FC = () => {
return (
<View className="flex flex-col rounded-xl mb-4 overflow-hidden border-neutral-800 divide-y-2 divide-solid divide-neutral-800 ">
<View className="flex flex-row items-center justify-between bg-neutral-900 p-4">
<Text>Auto rotate</Text>
<View className="shrink">
<Text className="font-semibold">Auto rotate</Text>
<Text className="text-xs opacity-50">
Important on android since the video player orientation is locked to
the app orientation.
</Text>
</View>
<Switch
value={settings?.autoRotate}
onValueChange={(value) => updateSettings({ autoRotate: value })}
/>
</View>
<View className="flex flex-row items-center justify-between bg-neutral-900 p-4">
<Text>Start videos in fullscreen</Text>
<View className="shrink">
<Text className="font-semibold">Start videos in fullscreen</Text>
<Text className="text-xs opacity-50">
Clicking a video will start it in fullscreen mode, instead of
inline.
</Text>
</View>
<Switch
value={settings?.openFullScreenVideoPlayerByDefault}
onValueChange={(value) =>
@@ -24,6 +36,25 @@ export const SettingToggles: React.FC = () => {
}
/>
</View>
<View className="flex flex-row items-center justify-between bg-neutral-900 p-4">
<View className="flex flex-col">
<Text className="font-semibold">Use popular lists plugin</Text>
<Text className="text-xs opacity-50">Made by: lostb1t</Text>
<TouchableOpacity
onPress={() => {
Linking.openURL(
"https://github.com/lostb1t/jellyfin-plugin-media-lists",
);
}}
>
<Text className="text-xs text-purple-600">More info</Text>
</TouchableOpacity>
</View>
<Switch
value={settings?.usePopularPlugin}
onValueChange={(value) => updateSettings({ usePopularPlugin: value })}
/>
</View>
</View>
);
};