diff --git a/src/components/GlobalDataProvider.tsx b/src/components/GlobalDataProvider.tsx index 1097b9ea..f6fdbe36 100644 --- a/src/components/GlobalDataProvider.tsx +++ b/src/components/GlobalDataProvider.tsx @@ -1,31 +1,32 @@ +import { AO_CU_URL, log } from '@src/constants'; import { useEffectOnce } from '@src/hooks/useEffectOnce'; import { useGlobalState } from '@src/store'; import { cleanupDbCache } from '@src/store/db'; import { ReactElement, useEffect } from 'react'; -const GlobalDataProvider = ({ - children, -}: { - children: ReactElement; -}) => { - const twoMinutes = 120000; +// Time to wait in ms to check if the AO CU URL is congested +const CONGESTION_WINDOW = 5000; +const TWO_MINUTES = 120000; + +const GlobalDataProvider = ({ children }: { children: ReactElement }) => { const setBlockHeight = useGlobalState((state) => state.setBlockHeight); const setCurrentEpoch = useGlobalState((state) => state.setCurrentEpoch); const setTicker = useGlobalState((state) => state.setTicker); const arweave = useGlobalState((state) => state.arweave); const arioReadSDK = useGlobalState((state) => state.arIOReadSDK); + const setAoCongested = useGlobalState((state) => state.setAoCongested); useEffectOnce(() => { const update = async () => { - // perform this first as retrieving the current epic takes some time - const {Ticker} = await arioReadSDK.getInfo(); + // perform this first as retrieving the current epic takes some time + const { Ticker } = await arioReadSDK.getInfo(); setTicker(Ticker); const currentEpoch = await arioReadSDK.getCurrentEpoch(); setCurrentEpoch(currentEpoch); - if(currentEpoch?.epochIndex) { + if (currentEpoch?.epochIndex) { cleanupDbCache(currentEpoch.epochIndex); } }; @@ -34,15 +35,42 @@ const GlobalDataProvider = ({ }); useEffect(() => { + // Block Height Updater const updateBlockHeight = async () => { const blockHeight = await (await arweave.blocks.getCurrent()).height; setBlockHeight(blockHeight); }; updateBlockHeight(); - const interval = setInterval(updateBlockHeight, twoMinutes); + const interval = setInterval(updateBlockHeight, TWO_MINUTES); + + // AO congestion checker: Checks CU URL every 30 seconds and if it takes longer than 5 seconds will + // dispatch a warning to the user + + const checkAoCongestion = () => { + const startTime = Date.now(); + fetch(AO_CU_URL, { method: 'HEAD' }) + .then((res) => { + const endTime = Date.now(); + if (!res.ok) { + log.error('AO CU URL is down'); + setAoCongested(true); + } else if (endTime - startTime > CONGESTION_WINDOW) { + setAoCongested(true); + } + }) + .catch((error) => { + log.error('AO CU URL is down', error); + setAoCongested(true); + }); + }; + + checkAoCongestion(); + const congestionInterval = setInterval(checkAoCongestion, 30000); return () => { clearInterval(interval); + clearInterval(congestionInterval); + setAoCongested(false); }; }); diff --git a/src/components/NetworkStatusBanner.tsx b/src/components/NetworkStatusBanner.tsx new file mode 100644 index 00000000..0fd31feb --- /dev/null +++ b/src/components/NetworkStatusBanner.tsx @@ -0,0 +1,45 @@ +import { useGlobalState } from '@src/store'; +import { useEffect, useState } from 'react'; + +function NetworkStatusBanner() { + const [online, setOnline] = useState(navigator.onLine); + const aoCongested = useGlobalState((state) => state.aoCongested); + + const [statusMessage, setStatusMessage] = useState(''); + + useEffect(() => { + window.addEventListener('online', () => setOnline(true)); + window.addEventListener('offline', () => setOnline(false)); + + // Cleanup listeners + return () => { + window.removeEventListener('online', () => setOnline(true)); + window.removeEventListener('offline', () => setOnline(false)); + }; + }, []); + + useEffect(() => { + if (!online) { + setStatusMessage(`We can't connect to the Internet. Please check your connection + and try again.`); + } else if (aoCongested) { + setStatusMessage( + 'The AO network is experiencing congestion, load times may be longer than usual.', + ); + } else { + setStatusMessage(''); + } + }, [online, aoCongested]); + + return ( + <> + {statusMessage && ( +