84 lines
2.2 KiB
TypeScript
84 lines
2.2 KiB
TypeScript
import React, { useState, useRef, useContext, useCallback } from "react";
|
|
|
|
type MapLoadingProgress = {
|
|
count: number;
|
|
total: number;
|
|
};
|
|
|
|
type MapLoadingProgressUpdate = MapLoadingProgress & {
|
|
id: string;
|
|
};
|
|
|
|
type MapLoadingContextValue = {
|
|
isLoading: boolean;
|
|
assetLoadStart: (id: string) => void;
|
|
assetProgressUpdate: (update: MapLoadingProgressUpdate) => void;
|
|
loadingProgressRef: React.MutableRefObject<number>;
|
|
};
|
|
|
|
const MapLoadingContext =
|
|
React.createContext<MapLoadingContextValue | undefined>(undefined);
|
|
|
|
export function MapLoadingProvider({
|
|
children,
|
|
}: {
|
|
children: React.ReactNode;
|
|
}) {
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
// Mapping from asset id to the count and total number of pieces loaded
|
|
const assetProgressRef = useRef<Record<string, MapLoadingProgress>>({});
|
|
// Loading progress of all assets between 0 and 1
|
|
const loadingProgressRef = useRef<number>(0);
|
|
|
|
const assetLoadStart = useCallback((id) => {
|
|
setIsLoading(true);
|
|
// Add asset at a 0% progress
|
|
assetProgressRef.current = {
|
|
...assetProgressRef.current,
|
|
[id]: { count: 0, total: 1 },
|
|
};
|
|
}, []);
|
|
|
|
const assetProgressUpdate = useCallback(({ id, count, total }) => {
|
|
assetProgressRef.current = {
|
|
...assetProgressRef.current,
|
|
[id]: { count, total },
|
|
};
|
|
// Update loading progress
|
|
let complete = 0;
|
|
const progresses = Object.values(assetProgressRef.current);
|
|
for (let progress of progresses) {
|
|
complete += progress.count / progress.total;
|
|
}
|
|
loadingProgressRef.current = complete / progresses.length;
|
|
// All loading is complete
|
|
if (loadingProgressRef.current === 1) {
|
|
setIsLoading(false);
|
|
assetProgressRef.current = {};
|
|
}
|
|
}, []);
|
|
|
|
const value = {
|
|
assetLoadStart,
|
|
isLoading,
|
|
assetProgressUpdate,
|
|
loadingProgressRef,
|
|
};
|
|
|
|
return (
|
|
<MapLoadingContext.Provider value={value}>
|
|
{children}
|
|
</MapLoadingContext.Provider>
|
|
);
|
|
}
|
|
|
|
export function useMapLoading() {
|
|
const context = useContext(MapLoadingContext);
|
|
if (context === undefined) {
|
|
throw new Error("useMapLoading must be used within a MapLoadingProvider");
|
|
}
|
|
return context;
|
|
}
|
|
|
|
export default MapLoadingContext;
|