mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-15 23:59:08 +00:00
fix: remove splashscreen provider and handle loading in jellyfinprovider
This commit is contained in:
BIN
providers/.JellyfinProvider.tsx.swp
Normal file
BIN
providers/.JellyfinProvider.tsx.swp
Normal file
Binary file not shown.
@@ -1,5 +1,7 @@
|
||||
import "@/augmentations";
|
||||
import { useInterval } from "@/hooks/useInterval";
|
||||
import { JellyseerrApi, useJellyseerr } from "@/hooks/useJellyseerr";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { storage } from "@/utils/mmkv";
|
||||
import { Api, Jellyfin } from "@jellyfin/sdk";
|
||||
import { UserDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
@@ -7,6 +9,7 @@ import { getUserApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import axios, { AxiosError } from "axios";
|
||||
import { router, useSegments } from "expo-router";
|
||||
import * as SplashScreen from "expo-splash-screen";
|
||||
import { atom, useAtom } from "jotai";
|
||||
import React, {
|
||||
createContext,
|
||||
@@ -17,16 +20,10 @@ import React, {
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Platform } from "react-native";
|
||||
import uuid from "react-native-uuid";
|
||||
import { getDeviceName } from "react-native-device-info";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { JellyseerrApi, useJellyseerr } from "@/hooks/useJellyseerr";
|
||||
import {
|
||||
useSplashScreenLoading,
|
||||
useSplashScreenVisible,
|
||||
} from "./SplashScreenProvider";
|
||||
import { Platform } from "react-native";
|
||||
import { getDeviceName } from "react-native-device-info";
|
||||
import uuid from "react-native-uuid";
|
||||
|
||||
interface Server {
|
||||
address: string;
|
||||
@@ -88,22 +85,6 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
||||
] = useSettings();
|
||||
const { clearAllJellyseerData, setJellyseerrUser } = useJellyseerr();
|
||||
|
||||
useQuery({
|
||||
queryKey: ["user", api],
|
||||
queryFn: async () => {
|
||||
if (!api) return null;
|
||||
const response = await getUserApi(api).getCurrentUser();
|
||||
if (response.data) setUser(response.data);
|
||||
return user;
|
||||
},
|
||||
enabled: !!api,
|
||||
refetchOnWindowFocus: true,
|
||||
refetchInterval: 1000 * 60,
|
||||
refetchIntervalInBackground: true,
|
||||
refetchOnMount: true,
|
||||
refetchOnReconnect: true,
|
||||
});
|
||||
|
||||
const headers = useMemo(() => {
|
||||
if (!deviceId) return {};
|
||||
return {
|
||||
@@ -179,14 +160,13 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
||||
}
|
||||
}, [api, secret, headers]);
|
||||
|
||||
useInterval(pollQuickConnect, isPolling ? 1000 : null);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
await refreshStreamyfinPluginSettings();
|
||||
})();
|
||||
}, []);
|
||||
|
||||
useInterval(pollQuickConnect, isPolling ? 1000 : null);
|
||||
useInterval(refreshStreamyfinPluginSettings, 60 * 5 * 1000); // 5 min
|
||||
|
||||
const discoverServers = async (url: string): Promise<Server[]> => {
|
||||
@@ -312,33 +292,44 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
||||
},
|
||||
});
|
||||
|
||||
const { isLoading, isFetching } = useQuery({
|
||||
queryKey: [
|
||||
"initializeJellyfin",
|
||||
user?.Id,
|
||||
api?.basePath,
|
||||
jellyfin?.clientInfo,
|
||||
],
|
||||
queryFn: async () => {
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const [initialLoaded, setInitialLoaded] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (initialLoaded) {
|
||||
setLoaded(true);
|
||||
}
|
||||
}, [initialLoaded]);
|
||||
|
||||
useEffect(() => {
|
||||
const initializeJellyfin = async () => {
|
||||
if (!jellyfin) return;
|
||||
|
||||
try {
|
||||
const token = getTokenFromStorage();
|
||||
const serverUrl = getServerUrlFromStorage();
|
||||
const user = getUserFromStorage();
|
||||
if (serverUrl && token && user?.Id && jellyfin) {
|
||||
const storedUser = getUserFromStorage();
|
||||
|
||||
if (serverUrl && token) {
|
||||
const apiInstance = jellyfin.createApi(serverUrl, token);
|
||||
setApi(apiInstance);
|
||||
setUser(user);
|
||||
}
|
||||
|
||||
return true;
|
||||
if (storedUser?.Id) {
|
||||
setUser(storedUser);
|
||||
}
|
||||
|
||||
const response = await getUserApi(apiInstance).getCurrentUser();
|
||||
setUser(response.data);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return false;
|
||||
} finally {
|
||||
setInitialLoaded(true);
|
||||
}
|
||||
},
|
||||
staleTime: 0,
|
||||
enabled: !user?.Id || !api || !jellyfin,
|
||||
});
|
||||
};
|
||||
|
||||
initializeJellyfin();
|
||||
}, [jellyfin]);
|
||||
|
||||
const contextValue: JellyfinContextValue = {
|
||||
discoverServers,
|
||||
@@ -350,17 +341,17 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
||||
initiateQuickConnect,
|
||||
};
|
||||
|
||||
let isLoadingOrFetching = isLoading || isFetching;
|
||||
useProtectedRoute(user, isLoadingOrFetching);
|
||||
useEffect(() => {
|
||||
if (loaded) {
|
||||
SplashScreen.hideAsync();
|
||||
}
|
||||
}, [loaded]);
|
||||
|
||||
// show splash screen until everything loaded
|
||||
useSplashScreenLoading(isLoadingOrFetching);
|
||||
const splashScreenVisible = useSplashScreenVisible();
|
||||
useProtectedRoute(user, loaded);
|
||||
|
||||
return (
|
||||
<JellyfinContext.Provider value={contextValue}>
|
||||
{/* don't render login page when loading and splash screen visible */}
|
||||
{isLoadingOrFetching && splashScreenVisible ? undefined : children}
|
||||
{children}
|
||||
</JellyfinContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -372,20 +363,24 @@ export const useJellyfin = (): JellyfinContextValue => {
|
||||
return context;
|
||||
};
|
||||
|
||||
function useProtectedRoute(user: UserDto | null, loading = false) {
|
||||
function useProtectedRoute(user: UserDto | null, loaded = false) {
|
||||
const segments = useSegments();
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) return;
|
||||
if (loaded === false) return;
|
||||
|
||||
console.log("Loaded", user);
|
||||
|
||||
const inAuthGroup = segments[0] === "(auth)";
|
||||
|
||||
if (!user?.Id && inAuthGroup) {
|
||||
console.log("Redirected to login");
|
||||
router.replace("/login");
|
||||
} else if (user?.Id && !inAuthGroup) {
|
||||
console.log("Redirected to home");
|
||||
router.replace("/(auth)/(tabs)/(home)/");
|
||||
}
|
||||
}, [user, segments, loading]);
|
||||
}, [user, segments, loaded]);
|
||||
}
|
||||
|
||||
export function getTokenFromStorage(): string | null {
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
import {
|
||||
createContext,
|
||||
ReactNode,
|
||||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
useRef,
|
||||
} from "react";
|
||||
import * as SplashScreen from "expo-splash-screen";
|
||||
|
||||
type SplashScreenContextValue = {
|
||||
registerLoadingComponent: () => () => void;
|
||||
splashScreenVisible: boolean;
|
||||
};
|
||||
|
||||
const SplashScreenContext = createContext<SplashScreenContextValue | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
// Prevent splash screen from auto-hiding
|
||||
void SplashScreen.preventAutoHideAsync();
|
||||
|
||||
export const SplashScreenProvider: React.FC<{ children: ReactNode }> = ({
|
||||
children,
|
||||
}) => {
|
||||
const [splashScreenVisible, setSplashScreenVisible] = useState(true);
|
||||
const loadingComponentsCount = useRef(0);
|
||||
const isHidingRef = useRef(false);
|
||||
|
||||
const hideScreenIfNoLoadingComponents = async () => {
|
||||
if (loadingComponentsCount.current === 0 && !isHidingRef.current) {
|
||||
try {
|
||||
isHidingRef.current = true;
|
||||
await SplashScreen.hideAsync();
|
||||
setSplashScreenVisible(false);
|
||||
} catch (error) {
|
||||
console.warn("Failed to hide splash screen:", error);
|
||||
} finally {
|
||||
isHidingRef.current = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const registerLoadingComponent = () => {
|
||||
loadingComponentsCount.current += 1;
|
||||
|
||||
return () => {
|
||||
loadingComponentsCount.current -= 1;
|
||||
void hideScreenIfNoLoadingComponents();
|
||||
};
|
||||
};
|
||||
|
||||
const contextValue: SplashScreenContextValue = {
|
||||
registerLoadingComponent,
|
||||
splashScreenVisible,
|
||||
};
|
||||
|
||||
return (
|
||||
<SplashScreenContext.Provider value={contextValue}>
|
||||
{children}
|
||||
</SplashScreenContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the Splash Screen until component is ready to be displayed.
|
||||
*
|
||||
* @param isLoading The loading state of the component
|
||||
*
|
||||
* ## Usage
|
||||
* ```
|
||||
* const isLoading = loadSomething()
|
||||
* useSplashScreenLoading(isLoading) // splash screen visible until isLoading is false
|
||||
* ```
|
||||
*/
|
||||
export function useSplashScreenLoading(isLoading: boolean) {
|
||||
const context = useContext(SplashScreenContext);
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
"useSplashScreenLoading must be used within a SplashScreenProvider"
|
||||
);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoading) {
|
||||
return context.registerLoadingComponent();
|
||||
}
|
||||
}, [isLoading]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the visibility of the Splash Screen.
|
||||
* @returns the visibility of the Splash Screen
|
||||
*/
|
||||
export function useSplashScreenVisible() {
|
||||
const context = useContext(SplashScreenContext);
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
"useSplashScreenVisible must be used within a SplashScreenProvider"
|
||||
);
|
||||
}
|
||||
return context.splashScreenVisible;
|
||||
}
|
||||
Reference in New Issue
Block a user