Skip to content

Commit

Permalink
handle api status updates in context
Browse files Browse the repository at this point in the history
  • Loading branch information
Ross Bulat committed Mar 5, 2024
1 parent bf88f04 commit a9618b5
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 6 deletions.
4 changes: 4 additions & 0 deletions src/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
46 changes: 45 additions & 1 deletion src/contexts/Api/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<ApiContextInterface>(defaultApiContext);

Expand All @@ -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<Record<string, ApiStatus>>(
{}
);
const apiStatusRef = useRef(apiStatus);

// Setter for api status.
const setApiStatus = (status: Record<string, ApiStatus>) => {
apiStatusRef.current = status;
setApiStatusState(status);
};

// Gets the Api instance of the active tab, if present.
const getTabApi = () => {
const chainId = getActiveTab()?.chainId;
Expand All @@ -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>(document);

useEventListener('api-status', handleNewApiStatus, documentRef);

return (
<Api.Provider
value={{
Expand Down
4 changes: 2 additions & 2 deletions src/model/Api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ApiPromise } from '@polkadot/api';
import type { VoidFn } from '@polkadot/api/types';
import { WsProvider } from '@polkadot/rpc-provider';
import type { ChainId } from 'config/networks';
import type { EventDetail, EventStatus } from './types';
import type { APIStatusEventDetail, EventStatus } from './types';

export class Api {
// ------------------------------------------------------
Expand Down Expand Up @@ -109,7 +109,7 @@ export class Api {
err?: string;
}
) {
const detail: EventDetail = { event };
const detail: APIStatusEventDetail = { event, chainId: this.chainId };
if (options?.err) {
detail['err'] = options.err;
}
Expand Down
5 changes: 4 additions & 1 deletion src/model/Api/types.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
// Copyright 2024 @rossbulat/console authors & contributors
// SPDX-License-Identifier: GPL-3.0-only

import type { ChainId } from 'config/networks';

export type ApiStatus = 'connecting' | 'connected' | 'disconnected' | 'ready';

export type EventStatus = ApiStatus | 'error';

export interface EventDetail {
export interface APIStatusEventDetail {
event: EventStatus;
chainId: ChainId;
err?: string;
}
4 changes: 2 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { EventDetail } from 'model/Api/types';
import type { APIStatusEventDetail } from 'model/Api/types';
import type { CSSProperties, ReactNode } from 'react';

declare global {
interface DocumentEventMap {
'api-status': CustomEvent<EventDetail>;
'api-status': CustomEvent<APIStatusEventDetail>;
}
}

Expand Down

0 comments on commit a9618b5

Please sign in to comment.