grungnet/src/contexts/MapLoadingContext.tsx

84 lines
2.2 KiB
TypeScript
Raw Normal View History

import React, { useState, useRef, useContext, useCallback } from "react";
2021-07-02 01:54:54 -04:00
type MapLoadingProgress = {
count: number;
total: number;
};
2021-07-02 01:54:54 -04:00
type MapLoadingProgressUpdate = MapLoadingProgress & {
id: string;
};
2021-07-17 04:39:49 -04:00
type MapLoadingContextValue = {
2021-07-02 01:54:54 -04:00
isLoading: boolean;
assetLoadStart: (id: string) => void;
assetProgressUpdate: (update: MapLoadingProgressUpdate) => void;
2021-07-16 04:59:29 -04:00
loadingProgressRef: React.MutableRefObject<number>;
2021-07-02 01:54:54 -04:00
};
2021-07-02 01:54:54 -04:00
const MapLoadingContext =
2021-07-17 04:39:49 -04:00
React.createContext<MapLoadingContextValue | undefined>(undefined);
2021-07-02 01:54:54 -04:00
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
2021-07-02 01:54:54 -04:00
const assetProgressRef = useRef<Record<string, MapLoadingProgress>>({});
// Loading progress of all assets between 0 and 1
2021-07-16 04:59:29 -04:00
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;