Skip to content

Commit

Permalink
Chain icon (#80)
Browse files Browse the repository at this point in the history
* create a new context for networks
use different icons based on chains

* fix
  • Loading branch information
TopETH authored Apr 23, 2024
1 parent d470dbc commit 33d71e7
Show file tree
Hide file tree
Showing 16 changed files with 231 additions and 106 deletions.
4 changes: 4 additions & 0 deletions src/assets/networks/coretime/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import KusamaCoretime from './kusama.png';
import RococoCoretime from './rococo.png';

export { KusamaCoretime, RococoCoretime };
Binary file added src/assets/networks/coretime/kusama.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
5 changes: 5 additions & 0 deletions src/assets/networks/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import RegionX from './regionx.png';

export * from './coretime';
export * from './relay';
export { RegionX };
4 changes: 4 additions & 0 deletions src/assets/networks/relay/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import Kusama from './kusama.png';
import Rococo from './rococo.png';

export { Kusama, Rococo };
File renamed without changes
File renamed without changes
15 changes: 10 additions & 5 deletions src/components/Elements/NetworkSelect/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { FormControl, InputLabel, MenuItem, Select } from '@mui/material';
import { useRouter } from 'next/router';

import { useNetwork } from '@/contexts/network';
import { NetworkType } from '@/models';

const RelaySelect = () => {
const router = useRouter();
const { network } = router.query;
const { network } = useNetwork();

const handleChange = (e: any) => {
router.push(
Expand All @@ -16,19 +19,21 @@ const RelaySelect = () => {
);
};

return (
return network !== NetworkType.NONE ? (
<FormControl sx={{ m: 2, minWidth: 150 }} fullWidth>
<InputLabel>Network</InputLabel>
<Select
id='network-select'
value={network ? network : 'rococo'}
value={network}
label='Relay chain'
onChange={handleChange}
>
<MenuItem value='rococo'>Rococo</MenuItem>
<MenuItem value='kusama'>Kusama</MenuItem>
<MenuItem value={NetworkType.ROCOCO}>Rococo</MenuItem>
<MenuItem value={NetworkType.KUSAMA}>Kusama</MenuItem>
</Select>
</FormControl>
) : (
<></>
);
};

Expand Down
64 changes: 49 additions & 15 deletions src/components/Elements/Selectors/ChainSelector/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
Box,
CircularProgress,
FormControl,
InputLabel,
MenuItem,
Expand All @@ -8,32 +9,61 @@ import {
} from '@mui/material';
import Image from 'next/image';

import CoretimeIcon from '@/assets/networks/coretime.png';
import RegionXIcon from '@/assets/networks/regionx.png';
// import KusamaIcon from '@/assets/networks/kusama.png';
import RococoIcon from '@/assets/networks/rococo.png';
import { ChainType } from '@/models';
import { useCoretimeApi, useRelayApi } from '@/contexts/apis';
import { ChainType, NetworkType } from '@/models';

interface ChainSelectorProps {
chain: ChainType;
setChain: (_: ChainType) => void;
}

import {
Kusama,
KusamaCoretime,
RegionX,
Rococo,
RococoCoretime,
} from '@/assets/networks';
import { ApiState } from '@/contexts/apis/types';
import { useNetwork } from '@/contexts/network';

const coretimeIcons = {
[NetworkType.NONE]: RococoCoretime,
[NetworkType.KUSAMA]: KusamaCoretime,
[NetworkType.ROCOCO]: RococoCoretime,
};

const relayIcons = {
[NetworkType.NONE]: Rococo,
[NetworkType.KUSAMA]: Kusama,
[NetworkType.ROCOCO]: Rococo,
};

export const ChainSelector = ({ chain, setChain }: ChainSelectorProps) => {
const { network } = useNetwork();
const {
state: { name: coretimeChain, apiState: coretimeState },
} = useCoretimeApi();
const {
state: { name: relayChain, apiState: relayState },
} = useRelayApi();

const menuItems = [
{
icon: RococoIcon,
label: 'Relay Chain',
icon: relayIcons[network],
label: relayChain,
value: ChainType.RELAY,
loading: coretimeState !== ApiState.READY,
},
{
icon: CoretimeIcon,
label: 'Coretime Chain',
icon: coretimeIcons[network],
label: coretimeChain,
value: ChainType.CORETIME,
loading: relayState !== ApiState.READY,
},

{
icon: RegionXIcon,
icon: RegionX,
label: 'RegionX Chain',
value: ChainType.REGIONX,
},
Expand All @@ -48,17 +78,21 @@ export const ChainSelector = ({ chain, setChain }: ChainSelectorProps) => {
label='Origin'
onChange={(e) => setChain(e.target.value as ChainType)}
>
{menuItems.map(({ icon, label, value }, index) => (
{menuItems.map(({ icon, label, value, loading }, index) => (
<MenuItem value={value} key={index}>
<Box sx={{ display: 'flex', gap: '0.5rem' }}>
<Box sx={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
<Image
src={icon}
alt='icon'
style={{ width: '2rem', height: '2rem', borderRadius: '100%' }}
/>
<Typography sx={{ lineHeight: 1.5, fontSize: '1.25rem' }}>
{label}
</Typography>
{loading ? (
<CircularProgress size={'1.5rem'} />
) : (
<Typography sx={{ lineHeight: 1.5, fontSize: '1.25rem' }}>
{label}
</Typography>
)}
</Box>
</MenuItem>
))}
Expand Down
33 changes: 15 additions & 18 deletions src/contexts/apis/CoretimeApi/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useRouter } from 'next/router';
import React, { useContext, useEffect, useReducer } from 'react';

import { ApiState } from '@/contexts/apis/types';
import { useNetwork } from '@/contexts/network';
import { useToast } from '@/contexts/toast';
import { NetworkType } from '@/models';

import { connect, disconnect, initialState, reducer } from '../common';
import { WS_KUSAMA_CORETIME_CHAIN, WS_ROCOCO_CORETIME_CHAIN } from '../consts';
Expand Down Expand Up @@ -36,37 +37,33 @@ const CoretimeApiContextProvider = (props: any) => {
const [state, dispatch] = useReducer(reducer, initialState);
const { toastError, toastSuccess } = useToast();

const router = useRouter();
const { network } = router.query;
const { network } = useNetwork();

useEffect(() => {
state.apiError && toastError(`Failed to connect to Coretime chain`);
}, [state.apiError, toastError]);

useEffect(() => {
state.apiState === ApiState.READY &&
toastSuccess('Successfully connected to the Coretime chain');
}, [state.apiState, toastSuccess]);
state.name &&
toastSuccess(`Successfully connected to ${state.name}`);
}, [state.apiState, state.name, toastSuccess]);

const getUrl = (network: any): string => {
if (!network || network == 'rococo') {
return WS_ROCOCO_CORETIME_CHAIN;
} else if (network == 'kusama') {
return WS_KUSAMA_CORETIME_CHAIN;
} else {
/* eslint-disable no-console */
console.error(`Network: ${network} not recognized`);
// Default to rococo.
return WS_ROCOCO_CORETIME_CHAIN;
}
return network === NetworkType.ROCOCO
? WS_ROCOCO_CORETIME_CHAIN
: WS_KUSAMA_CORETIME_CHAIN;
};

const disconnectCoretime = () => disconnect(state);

useEffect(() => {
if (state.socket == getUrl(network)) return;
const updateNetwork = network != '' && state.socket != getUrl(network);
connect(state, getUrl(network), dispatch, updateNetwork, types);
if (network === NetworkType.NONE || state.socket == getUrl(network)) return;
const updateNetwork = state.socket != getUrl(network);
if (updateNetwork) {
disconnectCoretime();
connect(state, getUrl(network), dispatch, updateNetwork, types);
}
}, [network, state]);

return (
Expand Down
34 changes: 15 additions & 19 deletions src/contexts/apis/RelayApi/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useRouter } from 'next/router';
import React, { useContext, useEffect, useReducer, useState } from 'react';

import { parseHNString } from '@/utils/functions';

import { ApiState } from '@/contexts/apis/types';
import { useNetwork } from '@/contexts/network';
import { useToast } from '@/contexts/toast';
import { ParaId } from '@/models';
import { NetworkType, ParaId } from '@/models';

import { connect, disconnect, initialState, reducer } from '../common';
import { WS_KUSAMA_RELAY_CHAIN, WS_ROCOCO_RELAY_CHAIN } from '../consts';
Expand All @@ -25,37 +25,33 @@ const RelayApiContextProvider = (props: any) => {
const { toastError, toastSuccess } = useToast();
const [paraIds, setParaIds] = useState<ParaId[]>([]);

const router = useRouter();
const { network } = router.query;
const { network } = useNetwork();

useEffect(() => {
state.apiError && toastError(`Failed to connect to relay chain`);
}, [state.apiError, toastError]);

useEffect(() => {
state.apiState === ApiState.READY &&
toastSuccess('Successfully connected to the relay chain');
}, [state.apiState, toastSuccess]);
state.name &&
toastSuccess(`Successfully connected to ${state.name}`);
}, [state.apiState, state.name, toastSuccess]);

const disconnectRelay = () => disconnect(state);

const getUrl = (network: any): string => {
if (!network || network == 'rococo') {
return WS_ROCOCO_RELAY_CHAIN;
} else if (network == 'kusama') {
return WS_KUSAMA_RELAY_CHAIN;
} else {
/* eslint-disable no-console */
console.error(`Network: ${network} not recognized`);
// Default to rococo.
return WS_ROCOCO_RELAY_CHAIN;
}
return network === NetworkType.ROCOCO
? WS_ROCOCO_RELAY_CHAIN
: WS_KUSAMA_RELAY_CHAIN;
};

useEffect(() => {
if (state.socket == getUrl(network)) return;
const updateNetwork = network != '' && state.socket != getUrl(network);
connect(state, getUrl(network), dispatch, updateNetwork);
if (network === NetworkType.NONE || state.socket == getUrl(network)) return;
const updateNetwork = state.socket != getUrl(network);
if (updateNetwork) {
disconnectRelay();
connect(state, getUrl(network), dispatch, updateNetwork);
}
}, [network, state]);

useEffect(() => {
Expand Down
41 changes: 28 additions & 13 deletions src/contexts/apis/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export type State = {
apiError: any;
apiState: ApiState;
symbol: string;
name: string;
};

export const initialState: State = {
Expand All @@ -24,6 +25,7 @@ export const initialState: State = {
apiError: null,
apiState: ApiState.DISCONNECTED,
symbol: '',
name: '',
};

///
Expand All @@ -49,9 +51,16 @@ export const reducer = (state: any, action: any) => {
case 'CONNECT_ERROR':
return { ...state, apiState: ApiState.ERROR, apiError: action.payload };
case 'DISCONNECTED':
return { ...state, apiState: ApiState.DISCONNECTED };
return {
...state,
apiState: ApiState.DISCONNECTED,
symbol: '',
name: '',
};
case 'SET_SYMBOL':
return { ...state, symbol: action.payload };
case 'SET_NAME':
return { ...state, name: action.payload };
default:
throw new Error(`Unknown type: ${action.type}`);
}
Expand Down Expand Up @@ -79,25 +88,31 @@ export const connect = (
_api.on('connected', () => {
dispatch({ type: 'CONNECT', payload: _api, socket });
// `ready` event is not emitted upon reconnection and is checked explicitly here.
_api.isReady.then(() => {
dispatch({ type: 'CONNECT_SUCCESS' });
const chainInfo = _api.registry.getChainProperties();
if (chainInfo?.tokenSymbol.isSome) {
const [symbol] = chainInfo.tokenSymbol.toHuman() as [string];
dispatch({
type: 'SET_SYMBOL',
payload: symbol,
});
}
_api.isReady.then(() => dispatch({ type: 'CONNECT_SUCCESS' }));
});
_api.on('ready', async () => {
dispatch({ type: 'CONNECT_SUCCESS' });
const chainInfo = _api.registry.getChainProperties();
if (chainInfo?.tokenSymbol.isSome) {
const [symbol] = chainInfo.tokenSymbol.toHuman() as [string];
dispatch({
type: 'SET_SYMBOL',
payload: symbol,
});
}

const name = await _api.rpc.system.chain();
dispatch({
type: 'SET_NAME',
payload: name,
});
});
_api.on('ready', () => dispatch({ type: 'CONNECT_SUCCESS' }));
_api.on('error', (err) => dispatch({ type: 'CONNECT_ERROR', payload: err }));
_api.on('disconnected', () => dispatch({ type: 'DISCONNECTED' }));
};

export const disconnect = (state: any) => {
const { api, apiState } = state;
if (apiState === ApiState.DISCONNECTED) return;
if (!api || apiState === ApiState.DISCONNECTED) return;
api.disconnect();
};
Loading

0 comments on commit 33d71e7

Please sign in to comment.