mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-24 12:08:05 +00:00
Merge branch 'develop' into feature/newarch
This commit is contained in:
6
.github/workflows/ci-codeql.yml
vendored
6
.github/workflows/ci-codeql.yml
vendored
@@ -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
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
|
||||
@@ -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
30
hooks/useNetworkStatus.ts
Normal 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 };
|
||||
}
|
||||
@@ -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({
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user