import React, { useContext, useEffect, createContext, useMemo, useState, useCallback } from "react"; import { RampCatalog } from "./types"; import { Loadable } from "../../types"; import api from "./api"; const initialState: Loadable = { isLoading: false, value: null, error: null, }; export type RampCatalogDataAPI = { fetchRampCatalog: () => Promise; }; type RampCatalogContextType = { state: Loadable; updateCatalog: () => Promise; }; export const rampCatalogContext = createContext({ state: initialState, updateCatalog: () => Promise.resolve(), }); type RampCatalogProviderProps = { children: React.ReactNode; updateFrequency: number; provider?: string; // "production" - only for backwards compatibility }; export function useRampCatalogContext() { const context = useContext(rampCatalogContext); if (context === undefined) { throw new Error("useRampCatalog must be used within a RampCatalogProvider"); } return context.state; } export function RampCatalogProvider({ children, updateFrequency, }: RampCatalogProviderProps): React.JSX.Element { const [state, setState] = useState>(initialState); const updateCatalog = useCallback(async () => { setState(currentState => ({ ...currentState, isLoading: true, error: null, })); const result = await api.fetchRampCatalog().then( (value): { value: RampCatalog; fetchError: null } => ({ value, fetchError: null }), (e): { value: null; fetchError: unknown } => ({ value: null, fetchError: e }), ); if (result.fetchError !== null) { setState(currentState => ({ ...currentState, isLoading: false, error: result.fetchError, })); } else { setState(() => ({ isLoading: false, value: result.value, error: null, })); } }, []); const value: RampCatalogContextType = useMemo( () => ({ state, updateCatalog, }), [state, updateCatalog], ); useEffect(() => { const interval = setInterval(() => { updateCatalog(); }, updateFrequency); updateCatalog(); return () => { clearInterval(interval); }; }, [updateFrequency, updateCatalog]); return {children}; }