mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-15 15:48:05 +00:00
fix: offline checking (#989)
This commit is contained in:
@@ -11,7 +11,6 @@ import {
|
|||||||
getUserLibraryApi,
|
getUserLibraryApi,
|
||||||
getUserViewsApi,
|
getUserViewsApi,
|
||||||
} from "@jellyfin/sdk/lib/utils/api";
|
} from "@jellyfin/sdk/lib/utils/api";
|
||||||
import NetInfo from "@react-native-community/netinfo";
|
|
||||||
import { type QueryFunction, useQuery } from "@tanstack/react-query";
|
import { type QueryFunction, useQuery } from "@tanstack/react-query";
|
||||||
import { useNavigation, useRouter, useSegments } from "expo-router";
|
import { useNavigation, useRouter, useSegments } from "expo-router";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
@@ -33,6 +32,7 @@ import { ScrollingCollectionList } from "@/components/home/ScrollingCollectionLi
|
|||||||
import { Loader } from "@/components/Loader";
|
import { Loader } from "@/components/Loader";
|
||||||
import { MediaListSection } from "@/components/medialists/MediaListSection";
|
import { MediaListSection } from "@/components/medialists/MediaListSection";
|
||||||
import { Colors } from "@/constants/Colors";
|
import { Colors } from "@/constants/Colors";
|
||||||
|
import { useNetworkStatus } from "@/hooks/useNetworkStatus";
|
||||||
import { useInvalidatePlaybackProgressCache } from "@/hooks/useRevalidatePlaybackProgressCache";
|
import { useInvalidatePlaybackProgressCache } from "@/hooks/useRevalidatePlaybackProgressCache";
|
||||||
import { useDownload } from "@/providers/DownloadProvider";
|
import { useDownload } from "@/providers/DownloadProvider";
|
||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||||
@@ -72,9 +72,6 @@ export const HomeIndex = () => {
|
|||||||
refreshStreamyfinPluginSettings,
|
refreshStreamyfinPluginSettings,
|
||||||
] = useSettings(null);
|
] = useSettings(null);
|
||||||
|
|
||||||
const [isConnected, setIsConnected] = useState<boolean | null>(null);
|
|
||||||
const [loadingRetry, setLoadingRetry] = useState(false);
|
|
||||||
|
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
|
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
@@ -83,6 +80,7 @@ export const HomeIndex = () => {
|
|||||||
|
|
||||||
const { getDownloadedItems, cleanCacheDirectory } = useDownload();
|
const { getDownloadedItems, cleanCacheDirectory } = useDownload();
|
||||||
const prevIsConnected = useRef<boolean | null>(false);
|
const prevIsConnected = useRef<boolean | null>(false);
|
||||||
|
const { isConnected, loading: retryLoading, retryCheck } = useNetworkStatus();
|
||||||
const invalidateCache = useInvalidatePlaybackProgressCache();
|
const invalidateCache = useInvalidatePlaybackProgressCache();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Only invalidate cache when transitioning from offline to online
|
// Only invalidate cache when transitioning from offline to online
|
||||||
@@ -137,29 +135,6 @@ export const HomeIndex = () => {
|
|||||||
};
|
};
|
||||||
}, [segments]);
|
}, [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 {
|
const {
|
||||||
data,
|
data,
|
||||||
isError: e1,
|
isError: e1,
|
||||||
@@ -397,30 +372,30 @@ export const HomeIndex = () => {
|
|||||||
{t("home.no_internet_message")}
|
{t("home.no_internet_message")}
|
||||||
</Text>
|
</Text>
|
||||||
<View className='mt-4'>
|
<View className='mt-4'>
|
||||||
<Button
|
{!Platform.isTV && (
|
||||||
color='purple'
|
<Button
|
||||||
onPress={() => router.push("/(auth)/downloads")}
|
color='purple'
|
||||||
justify='center'
|
onPress={() => router.push("/(auth)/downloads")}
|
||||||
iconRight={
|
justify='center'
|
||||||
<Ionicons name='arrow-forward' size={20} color='white' />
|
iconRight={
|
||||||
}
|
<Ionicons name='arrow-forward' size={20} color='white' />
|
||||||
>
|
}
|
||||||
{t("home.go_to_downloads")}
|
>
|
||||||
</Button>
|
{t("home.go_to_downloads")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
color='black'
|
color='black'
|
||||||
onPress={() => {
|
onPress={retryCheck}
|
||||||
checkConnection();
|
|
||||||
}}
|
|
||||||
justify='center'
|
justify='center'
|
||||||
className='mt-2'
|
className='mt-2'
|
||||||
iconRight={
|
iconRight={
|
||||||
loadingRetry ? null : (
|
retryLoading ? null : (
|
||||||
<Ionicons name='refresh' size={20} color='white' />
|
<Ionicons name='refresh' size={20} color='white' />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{loadingRetry ? (
|
{retryLoading ? (
|
||||||
<ActivityIndicator size={"small"} color={"white"} />
|
<ActivityIndicator size={"small"} color={"white"} />
|
||||||
) : (
|
) : (
|
||||||
"Retry"
|
"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 type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
|
||||||
import { getPlaystateApi, getTvShowsApi } from "@jellyfin/sdk/lib/utils/api";
|
import { getPlaystateApi, getTvShowsApi } from "@jellyfin/sdk/lib/utils/api";
|
||||||
import { useNetInfo } from "@react-native-community/netinfo";
|
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useDownload } from "@/providers/DownloadProvider";
|
import { useDownload } from "@/providers/DownloadProvider";
|
||||||
import { DownloadedItem } from "@/providers/Downloads/types";
|
import { DownloadedItem } from "@/providers/Downloads/types";
|
||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||||
|
import { useNetworkStatus } from "./useNetworkStatus";
|
||||||
|
|
||||||
interface PlaybackManagerProps {
|
interface PlaybackManagerProps {
|
||||||
item?: BaseItemDto | null;
|
item?: BaseItemDto | null;
|
||||||
@@ -65,12 +65,12 @@ export const usePlaybackManager = ({
|
|||||||
}: PlaybackManagerProps = {}) => {
|
}: PlaybackManagerProps = {}) => {
|
||||||
const api = useAtomValue(apiAtom);
|
const api = useAtomValue(apiAtom);
|
||||||
const user = useAtomValue(userAtom);
|
const user = useAtomValue(userAtom);
|
||||||
const netInfo = useNetInfo();
|
const { isConnected } = useNetworkStatus();
|
||||||
const { getDownloadedItemById, updateDownloadedItem, getDownloadedItems } =
|
const { getDownloadedItemById, updateDownloadedItem, getDownloadedItems } =
|
||||||
useDownload();
|
useDownload();
|
||||||
|
|
||||||
/** Whether the device is online. actually it's connected to the internet. */
|
/** 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
|
// Adjacent episodes logic
|
||||||
const { data: adjacentItems } = useQuery({
|
const { data: adjacentItems } = useQuery({
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { getItemsApi, getUserLibraryApi } from "@jellyfin/sdk/lib/utils/api";
|
import { getItemsApi, getUserLibraryApi } from "@jellyfin/sdk/lib/utils/api";
|
||||||
import { useNetInfo } from "@react-native-community/netinfo";
|
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { useDownload } from "@/providers/DownloadProvider";
|
import { useDownload } from "@/providers/DownloadProvider";
|
||||||
import { apiAtom, userAtom } from "../providers/JellyfinProvider";
|
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
|
* 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 = () => {
|
export const useTwoWaySync = () => {
|
||||||
const api = useAtomValue(apiAtom);
|
const api = useAtomValue(apiAtom);
|
||||||
const user = useAtomValue(userAtom);
|
const user = useAtomValue(userAtom);
|
||||||
const netInfo = useNetInfo();
|
|
||||||
const { getDownloadedItemById, updateDownloadedItem } = useDownload();
|
const { getDownloadedItemById, updateDownloadedItem } = useDownload();
|
||||||
|
const { isConnected } = useNetworkStatus();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Syncs the playback state of an offline item with the server.
|
* 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).
|
* @returns A Promise<boolean> indicating whether a server update was made (true) or not (false).
|
||||||
*/
|
*/
|
||||||
const syncPlaybackState = async (itemId: string): Promise<boolean> => {
|
const syncPlaybackState = async (itemId: string): Promise<boolean> => {
|
||||||
if (!api || !user || !netInfo.isConnected) {
|
if (!api || !user || !isConnected) {
|
||||||
// Cannot sync if offline or not logged in
|
// Cannot sync if offline or not logged in
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user