diff --git a/src/assets/networks/coretime/index.ts b/src/assets/networks/coretime/index.ts
new file mode 100644
index 00000000..6b832342
--- /dev/null
+++ b/src/assets/networks/coretime/index.ts
@@ -0,0 +1,4 @@
+import KusamaCoretime from './kusama.png';
+import RococoCoretime from './rococo.png';
+
+export { KusamaCoretime, RococoCoretime };
diff --git a/src/assets/networks/coretime/kusama.png b/src/assets/networks/coretime/kusama.png
new file mode 100644
index 00000000..ce8a3ef7
Binary files /dev/null and b/src/assets/networks/coretime/kusama.png differ
diff --git a/src/assets/networks/coretime.png b/src/assets/networks/coretime/rococo.png
similarity index 100%
rename from src/assets/networks/coretime.png
rename to src/assets/networks/coretime/rococo.png
diff --git a/src/assets/networks/index.tsx b/src/assets/networks/index.tsx
new file mode 100644
index 00000000..8a8035a8
--- /dev/null
+++ b/src/assets/networks/index.tsx
@@ -0,0 +1,5 @@
+import RegionX from './regionx.png';
+
+export * from './coretime';
+export * from './relay';
+export { RegionX };
diff --git a/src/assets/networks/relay/index.ts b/src/assets/networks/relay/index.ts
new file mode 100644
index 00000000..c5d0bf51
--- /dev/null
+++ b/src/assets/networks/relay/index.ts
@@ -0,0 +1,4 @@
+import Kusama from './kusama.png';
+import Rococo from './rococo.png';
+
+export { Kusama, Rococo };
diff --git a/src/assets/networks/kusama.png b/src/assets/networks/relay/kusama.png
similarity index 100%
rename from src/assets/networks/kusama.png
rename to src/assets/networks/relay/kusama.png
diff --git a/src/assets/networks/rococo.png b/src/assets/networks/relay/rococo.png
similarity index 100%
rename from src/assets/networks/rococo.png
rename to src/assets/networks/relay/rococo.png
diff --git a/src/components/Elements/NetworkSelect/index.tsx b/src/components/Elements/NetworkSelect/index.tsx
index afc3b201..1cbbf169 100644
--- a/src/components/Elements/NetworkSelect/index.tsx
+++ b/src/components/Elements/NetworkSelect/index.tsx
@@ -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(
@@ -16,19 +19,21 @@ const RelaySelect = () => {
);
};
- return (
+ return network !== NetworkType.NONE ? (
Network
+ ) : (
+ <>>
);
};
diff --git a/src/components/Elements/Selectors/ChainSelector/index.tsx b/src/components/Elements/Selectors/ChainSelector/index.tsx
index 7ddcf621..ced01401 100644
--- a/src/components/Elements/Selectors/ChainSelector/index.tsx
+++ b/src/components/Elements/Selectors/ChainSelector/index.tsx
@@ -1,5 +1,6 @@
import {
Box,
+ CircularProgress,
FormControl,
InputLabel,
MenuItem,
@@ -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,
},
@@ -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) => (
))}
diff --git a/src/contexts/apis/CoretimeApi/index.tsx b/src/contexts/apis/CoretimeApi/index.tsx
index 7289ddf3..d19b14dd 100644
--- a/src/contexts/apis/CoretimeApi/index.tsx
+++ b/src/contexts/apis/CoretimeApi/index.tsx
@@ -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';
@@ -36,8 +37,7 @@ 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`);
@@ -45,28 +45,25 @@ const CoretimeApiContextProvider = (props: any) => {
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 (
diff --git a/src/contexts/apis/RelayApi/index.tsx b/src/contexts/apis/RelayApi/index.tsx
index 7fbb2c4a..360eae43 100644
--- a/src/contexts/apis/RelayApi/index.tsx
+++ b/src/contexts/apis/RelayApi/index.tsx
@@ -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';
@@ -25,8 +25,7 @@ const RelayApiContextProvider = (props: any) => {
const { toastError, toastSuccess } = useToast();
const [paraIds, setParaIds] = useState([]);
- const router = useRouter();
- const { network } = router.query;
+ const { network } = useNetwork();
useEffect(() => {
state.apiError && toastError(`Failed to connect to relay chain`);
@@ -34,28 +33,25 @@ const RelayApiContextProvider = (props: any) => {
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(() => {
diff --git a/src/contexts/apis/common.ts b/src/contexts/apis/common.ts
index ad96b28f..22cfa972 100644
--- a/src/contexts/apis/common.ts
+++ b/src/contexts/apis/common.ts
@@ -14,6 +14,7 @@ export type State = {
apiError: any;
apiState: ApiState;
symbol: string;
+ name: string;
};
export const initialState: State = {
@@ -24,6 +25,7 @@ export const initialState: State = {
apiError: null,
apiState: ApiState.DISCONNECTED,
symbol: '',
+ name: '',
};
///
@@ -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}`);
}
@@ -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();
};
diff --git a/src/contexts/network/index.tsx b/src/contexts/network/index.tsx
new file mode 100644
index 00000000..90e72f9f
--- /dev/null
+++ b/src/contexts/network/index.tsx
@@ -0,0 +1,61 @@
+import { useRouter } from 'next/router';
+import React, { createContext, useContext, useEffect, useState } from 'react';
+
+import { NetworkType } from '@/models';
+
+interface NetworkData {
+ network: NetworkType;
+ setNetwork: (_network: NetworkType) => void;
+}
+
+const defaultNetworkData: NetworkData = {
+ network: NetworkType.ROCOCO,
+ setNetwork: (_network: NetworkType) => {
+ /** */
+ },
+};
+
+const NetworkDataContext = createContext(defaultNetworkData);
+
+interface Props {
+ children: React.ReactNode;
+}
+
+const NetworkProvider = ({ children }: Props) => {
+ const [activeNetwork, setActiveNetwork] = useState(NetworkType.NONE);
+
+ const router = useRouter();
+ const { network } = router.query;
+
+ useEffect(() => {
+ if (!router.isReady) return;
+ if (network === 'rococo') setActiveNetwork(NetworkType.ROCOCO);
+ else if (network === 'kusama') setActiveNetwork(NetworkType.KUSAMA);
+ else {
+ // invalid network param. redirect to the default chain: rococo
+ router.push(
+ {
+ pathname: router.pathname,
+ query: {
+ ...router.query,
+ network: 'rococo',
+ },
+ },
+ undefined,
+ { shallow: false }
+ );
+ }
+ }, [network, router.isReady]);
+
+ const setNetwork = (network: NetworkType) => setActiveNetwork(network);
+
+ return (
+
+ {children}
+
+ );
+};
+
+const useNetwork = () => useContext(NetworkDataContext);
+
+export { NetworkProvider, useNetwork };
diff --git a/src/models/types.ts b/src/models/types.ts
index 73a11532..05118c28 100644
--- a/src/models/types.ts
+++ b/src/models/types.ts
@@ -36,6 +36,15 @@ export enum ChainType {
REGIONX = 3,
}
+export enum NetworkType {
+ // eslint-disable-next-line no-unused-vars
+ NONE = 'none',
+ // eslint-disable-next-line no-unused-vars
+ ROCOCO = 'rococo',
+ // eslint-disable-next-line no-unused-vars
+ KUSAMA = 'kusama',
+}
+
export type Sender = {
address: string;
signer: Signer;
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
index c9a20c51..8eb733b7 100644
--- a/src/pages/_app.tsx
+++ b/src/pages/_app.tsx
@@ -25,6 +25,7 @@ import {
} from '@/contexts/apis/consts';
import { ContextDataProvider } from '@/contexts/common';
import { MarketProvider } from '@/contexts/market';
+import { NetworkProvider } from '@/contexts/network';
import { RegionDataProvider } from '@/contexts/regions';
import { SaleInfoProvider } from '@/contexts/sales';
import { TaskDataProvider } from '@/contexts/tasks';
@@ -69,32 +70,34 @@ export default function MyApp(props: MyAppProps) {
-
-
-
-
-
-
-
-
- {getLayout()}
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ {getLayout()}
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/transfer.tsx b/src/pages/transfer.tsx
index 85d40431..91c34cd0 100644
--- a/src/pages/transfer.tsx
+++ b/src/pages/transfer.tsx
@@ -1,7 +1,6 @@
import ArrowDownward from '@mui/icons-material/ArrowDownwardOutlined';
import { LoadingButton } from '@mui/lab';
import {
- Alert,
Box,
Button,
DialogActions,
@@ -65,12 +64,10 @@ const TransferPage = () => {
const [working, setWorking] = useState(false);
const [newOwner, setNewOwner] = useState('');
- const [originChain, setOriginChain] = useState(ChainType.NONE);
+ const [originChain, setOriginChain] = useState(ChainType.CORETIME);
const [destinationChain, setDestinationChain] = useState(
- ChainType.NONE
+ ChainType.CORETIME
);
- const [statusLabel, _setStatusLabel] = useState('');
-
const [selectedRegion, setSelectedRegion] = useState(
null
);
@@ -308,11 +305,6 @@ const TransferPage = () => {
/>
)}
- {statusLabel && (
-
- {statusLabel}
-
- )}