diff --git a/src/Utils.ts b/src/Utils.ts index bb9798f7..cb13e928 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -12,3 +12,7 @@ export const formatInputString = (str: string, toLower = false): string => { return str; }; + +// Detect if an event is a CustomEvent by checking if it has a `detail` property. +export const isCustomEvent = (event: Event): event is CustomEvent => + 'detail' in event; diff --git a/src/contexts/Api/index.tsx b/src/contexts/Api/index.tsx index c17546ba..c6b8ca8a 100644 --- a/src/contexts/Api/index.tsx +++ b/src/contexts/Api/index.tsx @@ -2,11 +2,14 @@ // SPDX-License-Identifier: GPL-3.0-only import type { ReactNode } from 'react'; -import { createContext, useContext } from 'react'; +import { createContext, useContext, useRef, useState } from 'react'; import { defaultApiContext } from './defaults'; import type { ApiContextInterface } from './types'; import { ApiController } from 'controllers/ApiController'; import { useTabs } from 'contexts/Tabs'; +import { useEventListener } from 'usehooks-ts'; +import { isCustomEvent } from 'Utils'; +import type { ApiStatus } from 'model/Api/types'; export const Api = createContext(defaultApiContext); @@ -15,6 +18,18 @@ export const useApi = () => useContext(Api); export const ApiProvider = ({ children }: { children: ReactNode }) => { const { getActiveTab } = useTabs(); + // Store API connection status of each tab. NOTE: requires ref as it is used in event listener. + const [apiStatus, setApiStatusState] = useState>( + {} + ); + const apiStatusRef = useRef(apiStatus); + + // Setter for api status. + const setApiStatus = (status: Record) => { + apiStatusRef.current = status; + setApiStatusState(status); + }; + // Gets the Api instance of the active tab, if present. const getTabApi = () => { const chainId = getActiveTab()?.chainId; @@ -23,6 +38,35 @@ export const ApiProvider = ({ children }: { children: ReactNode }) => { } }; + // Handle incoming api status updates. + const handleNewApiStatus = (e: Event): void => { + if (isCustomEvent(e)) { + const { chainId, event } = e.detail; + + switch (event) { + case 'ready': + setApiStatus({ ...apiStatusRef.current, [chainId]: 'ready' }); + break; + case 'connecting': + setApiStatus({ ...apiStatusRef.current, [chainId]: 'connecting' }); + break; + case 'connected': + setApiStatus({ ...apiStatusRef.current, [chainId]: 'connected' }); + break; + case 'disconnected': + setApiStatus({ ...apiStatusRef.current, [chainId]: 'disconnected' }); + break; + case 'error': + setApiStatus({ ...apiStatusRef.current, [chainId]: 'disconnected' }); + break; + } + } + }; + + const documentRef = useRef(document); + + useEventListener('api-status', handleNewApiStatus, documentRef); + return ( ; + 'api-status': CustomEvent; } }