mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-06-03 04:28:31 +01:00
chore
This commit is contained in:
@@ -1,115 +1,103 @@
|
||||
import { createContext, ReactNode, useContext, useEffect, useMemo, useState } from "react";
|
||||
import * as Crypto from 'expo-crypto';
|
||||
import {
|
||||
createContext,
|
||||
ReactNode,
|
||||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
useRef,
|
||||
} from "react";
|
||||
import * as SplashScreen from "expo-splash-screen";
|
||||
|
||||
class ChangeListenerMap<K, V> extends Map<K, V> {
|
||||
constructor(private readonly onChange: (e: { self: ChangeListenerMap<K, V>, key: K, oldValue: V | undefined, newValue: V }) => void) {
|
||||
super()
|
||||
}
|
||||
|
||||
public set(key: K, value: V): this {
|
||||
const oldValue = this.get(key);
|
||||
super.set(key, value);
|
||||
if(oldValue !== value) {
|
||||
this.onChange({ self: this, key, oldValue, newValue: value })
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
type SplashScreenContextValue = {
|
||||
splashScreenVisible: boolean,
|
||||
componentLoaded: Map<string, boolean>
|
||||
}
|
||||
registerLoadingComponent: () => () => void;
|
||||
splashScreenVisible: boolean;
|
||||
};
|
||||
|
||||
const SplashScreenContext = createContext<SplashScreenContextValue | undefined>(undefined)
|
||||
const SplashScreenContext = createContext<SplashScreenContextValue | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
SplashScreen.preventAutoHideAsync();
|
||||
// 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 [splashScreenVisible, setSplashScreenVisible] = useState(true)
|
||||
|
||||
const contextValue: SplashScreenContextValue = {
|
||||
splashScreenVisible,
|
||||
componentLoaded: new ChangeListenerMap(({ self }) => {
|
||||
for(const entry of self.entries()) {
|
||||
if(!entry[1]) {
|
||||
// one component not loaded yet, not hiding splash screen
|
||||
return
|
||||
}
|
||||
}
|
||||
SplashScreen.hideAsync()
|
||||
setSplashScreenVisible(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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<SplashScreenContext.Provider value={contextValue}>
|
||||
{children}
|
||||
</SplashScreenContext.Provider>
|
||||
)
|
||||
}
|
||||
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.
|
||||
*
|
||||
* This only has an effect when component is mounted before Splash Screen is hidden,
|
||||
* so it should only be used in components that show up on launch.
|
||||
*
|
||||
*
|
||||
* @param isLoading The loading state of the component
|
||||
*
|
||||
*
|
||||
* ## Usage
|
||||
* ```
|
||||
* // Example 1:
|
||||
* const isLoading = loadSomething()
|
||||
* useSplashScreenLoading(isLoading) // splash screen visible until isLoading is false
|
||||
* ```
|
||||
* ```
|
||||
*
|
||||
* // Example 2: multiple loading states
|
||||
* const isLoading1 = loadSomething()
|
||||
* useSplashScreenLoading(isLoading1) // splash screen visible until isLoading1 and isLoading2 are false
|
||||
*
|
||||
* // this could be in different component and still have the same effect
|
||||
* const isLoading2 = loadSomethingElse()
|
||||
* useSplashScreenLoading(isLoading2)
|
||||
* ```
|
||||
*/
|
||||
export function useSplashScreenLoading(isLoading: boolean) {
|
||||
const id = useMemo(() => Crypto.randomUUID(), []);
|
||||
const context = useContext(SplashScreenContext);
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
"useSplashScreenLoading must be used within a SplashScreenProvider"
|
||||
);
|
||||
}
|
||||
|
||||
const context = useContext(SplashScreenContext);
|
||||
if(!context) {
|
||||
throw new Error("useSplashScreenLoading must be used within a SplashScreenProvider");
|
||||
useEffect(() => {
|
||||
if (isLoading) {
|
||||
return context.registerLoadingComponent();
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// update the loading state of component
|
||||
context.componentLoaded.set(id, !isLoading)
|
||||
|
||||
// cleanup when unmounting component
|
||||
return () => {
|
||||
context.componentLoaded.delete(id);
|
||||
};
|
||||
}, [isLoading])
|
||||
}, [isLoading]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the visiblity of the Splash Screen.
|
||||
* Get the visibility of the Splash Screen.
|
||||
* @returns the visibility of the Splash Screen
|
||||
*
|
||||
* ## Usage
|
||||
* ```
|
||||
* const splashScreenIsVisible = useSplashScreenVisible()
|
||||
* ```
|
||||
*/
|
||||
export function useSplashScreenVisible() {
|
||||
const context = useContext(SplashScreenContext);
|
||||
if(!context) {
|
||||
throw new Error("useSplashScreenVisible must be used within a SplashScreenProvider");
|
||||
}
|
||||
return context.splashScreenVisible
|
||||
}
|
||||
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