import React from "react"; import { AccountBalancePair, Connection } from "@solana/web3.js"; import { useCluster, ClusterStatus } from "./cluster"; export enum Status { Idle, Disconnected, Connecting } type RichList = { accounts: AccountBalancePair[]; totalSupply: number; circulatingSupply: number; }; type State = RichList | Status | string; type Dispatch = React.Dispatch>; const StateContext = React.createContext(undefined); const DispatchContext = React.createContext(undefined); type Props = { children: React.ReactNode }; export function RichListProvider({ children }: Props) { const [state, setState] = React.useState(Status.Idle); const { status: clusterStatus, url } = useCluster(); React.useEffect(() => { if (state !== Status.Idle) { if (clusterStatus === ClusterStatus.Connecting) setState(Status.Disconnected); if (clusterStatus === ClusterStatus.Connected) fetch(setState, url); } }, [clusterStatus, url]); // eslint-disable-line react-hooks/exhaustive-deps return ( {children} ); } async function fetch(dispatch: Dispatch, url: string) { dispatch(Status.Connecting); try { const connection = new Connection(url, "max"); const supply = (await connection.getSupply()).value; const accounts = (await connection.getLargestAccounts()).value; // Update state if still connecting dispatch(state => { if (state !== Status.Connecting) return state; return { accounts, totalSupply: supply.total, circulatingSupply: supply.circulating }; }); } catch (err) { console.error("Failed to fetch", err); dispatch("Failed to fetch top accounts"); } } export function useRichList() { const state = React.useContext(StateContext); if (state === undefined) { throw new Error(`useRichList must be used within a RichListProvider`); } return state; } export function useFetchRichList() { const dispatch = React.useContext(DispatchContext); if (!dispatch) { throw new Error(`useFetchRichList must be used within a RichListProvider`); } const { url } = useCluster(); return () => fetch(dispatch, url); }