diff --git a/src/_hooks/useHomeSocket.ts b/src/_hooks/useHomeSocket.ts new file mode 100644 index 00000000..fa0e02b8 --- /dev/null +++ b/src/_hooks/useHomeSocket.ts @@ -0,0 +1,61 @@ +import { useEffect, useMemo, useState } from 'react'; +import Socket from '@_socket'; +import { IBlocksResponseItem, ITransactionsResponseItem, TChainID } from '@_api/type'; +interface IIntervalData { + blocks: Array; + transactions: ITransactionsResponseItem[]; + blocksLoading: boolean; + transactionsLoading: boolean; +} +const useHomeSocket = (chain: TChainID) => { + const [blocks, setBlocks] = useState>([]); + const [transactions, setTransactions] = useState([]); + const [blocksLoading, setBlocksLoading] = useState(true); + const [transactionsLoading, setTransactionsLoading] = useState(true); + console.log('signalR----------refresh'); + const socket = Socket(); + + const data: IIntervalData = useMemo(() => { + // console.log('xxxxx', isCanBeBid, auctionInfo?.finishIdentifier); + return { + blocks, + blocksLoading, + transactionsLoading, + transactions, + }; + }, [blocks, blocksLoading, transactions, transactionsLoading]); + + useEffect(() => { + function fetchAndReceiveWs() { + if (!socket) { + return; + } + + socket.registerHandler('ReceiveLatestBlocks', (data) => { + console.log('blocks---1', data); + setBlocks(data?.blocks || []); + setBlocksLoading(false); + }); + socket.registerHandler('ReceiveLatestTransactions', (data) => { + setTransactions(data.transactions); + console.log('transactions---2', data); + setTransactionsLoading(false); + }); + socket.sendEvent('RequestLatestTransactions', { chainId: chain }); + socket.sendEvent('RequestLatestBlocks', { chainId: chain }); + } + + fetchAndReceiveWs(); + + return () => { + console.log('signalR----destroy'); + socket?.destroy(); + // socket?.sendEvent('UnSubscribeLatestTransactions'); + // socket?.sendEvent('UnSubscribeLatestBlocks'); + }; + }, [chain, socket]); + + return data; +}; + +export default useHomeSocket; diff --git a/src/_socket/index.ts b/src/_socket/index.ts new file mode 100644 index 00000000..d68e830f --- /dev/null +++ b/src/_socket/index.ts @@ -0,0 +1,27 @@ +import { useEffect, useState } from 'react'; +import { usePathname } from 'next/navigation'; +import SignalR from './signalr'; + +export default function Socket() { + // const [error, setError] = useState(null); + const [socket, setSocket] = useState(null); + const pathName = usePathname(); + useEffect(() => { + const signalR = new SignalR({ url: '/api/app/blockchain/explore' }); + console.log('signalR---', signalR); + // if (error !== false) { + signalR + .initAndStart() + .then(() => { + setSocket(signalR); + // setError(false); + }) + .catch((e) => { + setSocket(signalR); + // setError(e); + }); + // } + }, [pathName]); + + return socket; +} diff --git a/src/_socket/signalr.ts b/src/_socket/signalr.ts new file mode 100644 index 00000000..65c81574 --- /dev/null +++ b/src/_socket/signalr.ts @@ -0,0 +1,124 @@ +import { HubConnection, HubConnectionBuilder, HttpTransportType } from '@microsoft/signalr'; +// import { IBidInfo, IBidInfosResponse } from './types'; + +type SignalRParams = { + url: string; +}; + +// type ReceiveDataType = IBidInfo | IBidInfosResponse; + +type HandlerFn = (data: any) => void; + +const messageType: Array = ['ReceiveLatestTransactions', 'ReceiveLatestBlocks']; + +export default class SignalR { + private connection: HubConnection | null; + private url: string; + private handlerMap: Map>; + + constructor({ url }: SignalRParams) { + this.url = url; + this.connection = new HubConnectionBuilder() + .withUrl(this.url, { + skipNegotiation: true, + transport: HttpTransportType.WebSockets, + }) + .withAutomaticReconnect() + .build(); + this.handlerMap = new Map(); + } + + initAndStart = () => { + this.connection?.onclose((err) => { + console.log('signalR---onclose', err); + }); + console.log('signalR---initAndStart'); + this.listen(); + + return new Promise((resolve, reject) => { + this.connection + ?.start() + .then(() => { + resolve(this.connection); + }) + .catch((e) => { + reject(e); + }); + }); + }; + + listen = () => { + try { + messageType.length && + messageType.forEach((name) => { + this.connection?.on(name, (data) => { + this.dispatchMessage(name, data); + }); + }); + } catch (err) { + console.log('listen err', err); + } + }; + + registerHandler = (message: string, handler: HandlerFn) => { + try { + const handlers = this.handlerMap.get(message); + if (handlers) { + this.handlerMap.set(message, [...handlers, handler]); + } else { + this.handlerMap.set(message, [handler]); + } + } catch (err) { + console.log('registerHandler err', err); + } + }; + + unRegisterHandler = (message: string, handler: HandlerFn) => { + try { + const handlers = this.handlerMap.get(message); + if (handlers) { + this.handlerMap.set( + message, + handlers.filter((fn) => fn !== handler), + ); + } + } catch (err) { + console.log('unsubscribe err', err); + } + }; + + dispatchMessage = (message: string, data: any) => { + try { + const handlers = this.handlerMap.get(message); + handlers && + handlers.forEach((handler) => { + handler(data); + }); + } catch (err) { + console.log('dispatchMessage err', err); + } + }; + + sendEvent = (name: string, ...rest: any[]) => { + try { + if (rest.length === 0) { + this.connection?.invoke(name); + } else if (rest.length === 1) { + this.connection?.invoke(name, rest[0]); + } else if (rest.length === 2) { + this.connection?.invoke(name, rest[0], rest[1]); + } else if (rest.length === 3) { + this.connection?.invoke(name, rest[0], rest[1], rest[2]); + } else { + console.log('too much params'); + } + } catch (err) { + console.log('subscribeEvent err', err); + } + }; + + destroy(): void { + // this.connection?.stop(); + this.handlerMap.clear(); + } +} diff --git a/src/pageComponents/home/page.tsx b/src/pageComponents/home/page.tsx index 79ec9b90..a6e6391a 100644 --- a/src/pageComponents/home/page.tsx +++ b/src/pageComponents/home/page.tsx @@ -11,24 +11,23 @@ import InfoSection from './_components/InfoSection'; import SearchComp from './_components/SearchWithClient'; import clsx from 'clsx'; import './index.css'; -import { useEffect, useState, useCallback } from 'react'; +import { useEffect, useState } from 'react'; import { HubConnection, HubConnectionBuilder, HttpTransportType } from '@microsoft/signalr'; import { IOverviewSSR } from './type'; // import { MessagePackHubProtocol } from '@microsoft/signalr-protocol-msgpack'; import Latest from './_components/Latest'; import TPSChart from './_components/TPSChart'; import tpsData from './mock'; -import useResponsive, { useMobileAll } from '@_hooks/useResponsive'; +import { useMobileAll } from '@_hooks/useResponsive'; const BannerPc = '/image/banner_pc.png'; const BannerMobile = '/image/banner_mobile.png'; const clsPrefix = 'home-container'; import { useEnvContext } from 'next-runtime-env'; -import { IBlocksResponseItem, ITransactionsResponseItem, TChainID } from '@_api/type'; -import { fetchLatestBlocksList } from '@_api/fetchBlocks'; import { Spin } from 'antd'; -import { fetchLatestTransactionList } from '@_api/fetchTransactions'; import { useAppSelector } from '@_store'; import { useSearchParams } from 'next/navigation'; +import useHomeSocket from '@_hooks/useHomeSocket'; +import { TChainID } from '@_api/type'; interface IProps { overviewSSR: IOverviewSSR; @@ -46,43 +45,13 @@ const getConnectionBuilder = (url: string) => { }; export default function Home({ overviewSSR }: IProps) { const { NEXT_PUBLIC_API_URL: HOST } = useEnvContext(); - const [loading, setLoading] = useState(false); - const [transactionsLoading, setTransactionsLoading] = useState(false); - const [blocks, setBlocks] = useState([]); const { defaultChain } = useAppSelector((state) => state.getChainId); const searchParmas = useSearchParams(); const chain = searchParmas.get('chainId') || defaultChain; - const [transactions, setTransactions] = useState([]); - const fetchBlocksData = useCallback(async () => { - setLoading(true); - try { - const data = await fetchLatestBlocksList({ chainId: chain as TChainID }); - console.log(data, 'data'); - setLoading(false); - setBlocks(data.blocks); - } catch (error) { - setLoading(false); - } - }, [chain]); - - const fetchTransactionData = useCallback(async () => { - setTransactionsLoading(true); - try { - const data = await fetchLatestTransactionList({ chainId: chain as TChainID, maxResultCount: 25 }); - console.log(data, 'data'); - setTransactionsLoading(false); - setTransactions(data.transactions); - } catch (error) { - setTransactionsLoading(false); - } - }, [chain]); + const { blocks, blocksLoading, transactionsLoading, transactions } = useHomeSocket(chain as TChainID); const isMobile = useMobileAll(); - useEffect(() => { - fetchBlocksData(); - fetchTransactionData(); - }, []); const OverView: React.FC = () => { const [connection, setConnection] = useState(null); @@ -130,7 +99,7 @@ export default function Home({ overviewSSR }: IProps) { return (
- +