Merge branch 'develop' into feature/newarch

This commit is contained in:
Gauvain
2025-09-03 17:09:32 +02:00
committed by GitHub
8 changed files with 65 additions and 60 deletions

View File

@@ -31,13 +31,13 @@ jobs:
fetch-depth: 0
- name: 🏁 Initialize CodeQL
uses: github/codeql-action/init@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
uses: github/codeql-action/init@2d92b76c45b91eb80fc44c74ce3fce0ee94e8f9d # v3.30.0
with:
languages: ${{ matrix.language }}
queries: +security-extended,security-and-quality
- name: 🛠️ Autobuild
uses: github/codeql-action/autobuild@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
uses: github/codeql-action/autobuild@2d92b76c45b91eb80fc44c74ce3fce0ee94e8f9d # v3.30.0
- name: 🧪 Perform CodeQL Analysis
uses: github/codeql-action/analyze@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
uses: github/codeql-action/analyze@2d92b76c45b91eb80fc44c74ce3fce0ee94e8f9d # v3.30.0

View File

@@ -34,7 +34,7 @@ export const ListItem: React.FC<PropsWithChildren<Props>> = ({
<TouchableOpacity
disabled={disabled}
onPress={onPress}
className={`flex flex-row items-center justify-between bg-neutral-900 h-11 pr-4 pl-4 ${
className={`flex flex-row items-center justify-between bg-neutral-900 min-h-[42px] py-2 pr-4 pl-4 ${
disabled ? "opacity-50" : ""
}`}
{...(viewProps as any)}
@@ -54,7 +54,7 @@ export const ListItem: React.FC<PropsWithChildren<Props>> = ({
);
return (
<View
className={`flex flex-row items-center justify-between bg-neutral-900 h-11 pr-4 pl-4 ${
className={`flex flex-row items-center justify-between bg-neutral-900 min-h-[42px] py-2 pr-4 pl-4 ${
disabled ? "opacity-50" : ""
}`}
{...viewProps}
@@ -106,7 +106,10 @@ const ListItemContent = ({
{title}
</Text>
{subtitle && (
<Text className='text-[#9899A1] text-sm mt-0.5' numberOfLines={2}>
<Text
className='text-[#9899A1] text-[12px] mt-0.5'
numberOfLines={2}
>
{subtitle}
</Text>
)}

View File

@@ -98,10 +98,6 @@ export const SeasonPicker: React.FC<Props> = ({ item }) => {
return res.data.Items;
},
select: (data) =>
[...(data || [])].sort(
(a, b) => (a.IndexNumber ?? 0) - (b.IndexNumber ?? 0),
),
enabled: !!api && !!user?.Id && !!item.Id && !!selectedSeasonId,
});

View File

@@ -11,7 +11,6 @@ import {
getUserLibraryApi,
getUserViewsApi,
} from "@jellyfin/sdk/lib/utils/api";
import NetInfo from "@react-native-community/netinfo";
import { type QueryFunction, useQuery } from "@tanstack/react-query";
import { useNavigation, useRouter, useSegments } from "expo-router";
import { useAtomValue } from "jotai";
@@ -33,6 +32,7 @@ import { ScrollingCollectionList } from "@/components/home/ScrollingCollectionLi
import { Loader } from "@/components/Loader";
import { MediaListSection } from "@/components/medialists/MediaListSection";
import { Colors } from "@/constants/Colors";
import { useNetworkStatus } from "@/hooks/useNetworkStatus";
import { useInvalidatePlaybackProgressCache } from "@/hooks/useRevalidatePlaybackProgressCache";
import { useDownload } from "@/providers/DownloadProvider";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
@@ -72,9 +72,6 @@ export const HomeIndex = () => {
refreshStreamyfinPluginSettings,
] = useSettings(null);
const [isConnected, setIsConnected] = useState<boolean | null>(null);
const [loadingRetry, setLoadingRetry] = useState(false);
const navigation = useNavigation();
const insets = useSafeAreaInsets();
@@ -83,6 +80,7 @@ export const HomeIndex = () => {
const { getDownloadedItems, cleanCacheDirectory } = useDownload();
const prevIsConnected = useRef<boolean | null>(false);
const { isConnected, loading: retryLoading, retryCheck } = useNetworkStatus();
const invalidateCache = useInvalidatePlaybackProgressCache();
useEffect(() => {
// Only invalidate cache when transitioning from offline to online
@@ -137,29 +135,6 @@ export const HomeIndex = () => {
};
}, [segments]);
const checkConnection = useCallback(async () => {
setLoadingRetry(true);
const state = await NetInfo.fetch();
setIsConnected(state.isConnected);
setLoadingRetry(false);
}, []);
useEffect(() => {
const unsubscribe = NetInfo.addEventListener((state) => {
if (state.isConnected === false || state.isInternetReachable === false)
setIsConnected(false);
else setIsConnected(true);
});
NetInfo.fetch().then((state) => {
setIsConnected(state.isConnected);
});
return () => {
unsubscribe();
};
}, []);
const {
data,
isError: e1,
@@ -341,7 +316,7 @@ export const HomeIndex = () => {
for (const [index, section] of settings.home.sections.entries()) {
const id = section.items?.title || `section-${index}`;
ss.push({
title: id,
title: t(`${id}`),
queryKey: ["home", id],
queryFn: async () => {
if (section.items) {
@@ -397,30 +372,30 @@ export const HomeIndex = () => {
{t("home.no_internet_message")}
</Text>
<View className='mt-4'>
<Button
color='purple'
onPress={() => router.push("/(auth)/downloads")}
justify='center'
iconRight={
<Ionicons name='arrow-forward' size={20} color='white' />
}
>
{t("home.go_to_downloads")}
</Button>
{!Platform.isTV && (
<Button
color='purple'
onPress={() => router.push("/(auth)/downloads")}
justify='center'
iconRight={
<Ionicons name='arrow-forward' size={20} color='white' />
}
>
{t("home.go_to_downloads")}
</Button>
)}
<Button
color='black'
onPress={() => {
checkConnection();
}}
onPress={retryCheck}
justify='center'
className='mt-2'
iconRight={
loadingRetry ? null : (
retryLoading ? null : (
<Ionicons name='refresh' size={20} color='white' />
)
}
>
{loadingRetry ? (
{retryLoading ? (
<ActivityIndicator size={"small"} color={"white"} />
) : (
"Retry"

30
hooks/useNetworkStatus.ts Normal file
View File

@@ -0,0 +1,30 @@
import NetInfo from "@react-native-community/netinfo";
import { useCallback, useEffect, useState } from "react";
export function useNetworkStatus() {
const [isConnected, setIsConnected] = useState(false);
const [loading, setLoading] = useState(false);
// Manual check (optional)
const retryCheck = useCallback(async () => {
setLoading(true);
const state = await NetInfo.fetch();
setIsConnected(!!state.isConnected && !!state.isInternetReachable);
setLoading(false);
}, []);
useEffect(() => {
const unsubscribe = NetInfo.addEventListener((state) => {
setIsConnected(!!state.isConnected && !!state.isInternetReachable);
});
// Initial state
NetInfo.fetch().then((state) => {
setIsConnected(!!state.isConnected && !!state.isInternetReachable);
});
return () => unsubscribe();
}, []);
return { isConnected, loading, retryCheck };
}

View File

@@ -1,12 +1,12 @@
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
import { getPlaystateApi, getTvShowsApi } from "@jellyfin/sdk/lib/utils/api";
import { useNetInfo } from "@react-native-community/netinfo";
import { useQuery } from "@tanstack/react-query";
import { useAtomValue } from "jotai";
import { useMemo } from "react";
import { useDownload } from "@/providers/DownloadProvider";
import { DownloadedItem } from "@/providers/Downloads/types";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { useNetworkStatus } from "./useNetworkStatus";
interface PlaybackManagerProps {
item?: BaseItemDto | null;
@@ -65,12 +65,12 @@ export const usePlaybackManager = ({
}: PlaybackManagerProps = {}) => {
const api = useAtomValue(apiAtom);
const user = useAtomValue(userAtom);
const netInfo = useNetInfo();
const { isConnected } = useNetworkStatus();
const { getDownloadedItemById, updateDownloadedItem, getDownloadedItems } =
useDownload();
/** Whether the device is online. actually it's connected to the internet. */
const isOnline = useMemo(() => netInfo.isConnected, [netInfo.isConnected]);
const isOnline = isConnected;
// Adjacent episodes logic
const { data: adjacentItems } = useQuery({

View File

@@ -1,8 +1,8 @@
import { getItemsApi, getUserLibraryApi } from "@jellyfin/sdk/lib/utils/api";
import { useNetInfo } from "@react-native-community/netinfo";
import { useAtomValue } from "jotai";
import { useDownload } from "@/providers/DownloadProvider";
import { apiAtom, userAtom } from "../providers/JellyfinProvider";
import { useNetworkStatus } from "./useNetworkStatus";
/**
* This hook is used to sync the playback state of a downloaded item with the server
@@ -11,8 +11,8 @@ import { apiAtom, userAtom } from "../providers/JellyfinProvider";
export const useTwoWaySync = () => {
const api = useAtomValue(apiAtom);
const user = useAtomValue(userAtom);
const netInfo = useNetInfo();
const { getDownloadedItemById, updateDownloadedItem } = useDownload();
const { isConnected } = useNetworkStatus();
/**
* Syncs the playback state of an offline item with the server.
@@ -21,7 +21,7 @@ export const useTwoWaySync = () => {
* @returns A Promise<boolean> indicating whether a server update was made (true) or not (false).
*/
const syncPlaybackState = async (itemId: string): Promise<boolean> => {
if (!api || !user || !netInfo.isConnected) {
if (!api || !user || !isConnected) {
// Cannot sync if offline or not logged in
return false;
}

View File

@@ -3,6 +3,7 @@ import { useJellyseerr } from "@/hooks/useJellyseerr";
import {
MediaRequestStatus,
MediaStatus,
MediaType,
} from "@/utils/jellyseerr/server/constants/media";
import {
hasPermission,
@@ -40,7 +41,7 @@ export const useJellyseerrCanRequest = (
const userHasPermission = hasPermission(
[
Permission.REQUEST,
item?.mediaInfo?.mediaType
item?.mediaInfo?.mediaType === MediaType.MOVIE
? Permission.REQUEST_MOVIE
: Permission.REQUEST_TV,
],