From c2d81bc229143a245d6e832a08d9eeb9c8f03675 Mon Sep 17 00:00:00 2001 From: Uncle Fatso Date: Thu, 9 Apr 2026 14:30:55 +0300 Subject: [PATCH] stabilize connection state of the ghost connect Signed-off-by: Uncle Fatso --- package.json | 2 +- src/components/TopBar/SelectNetwork.jsx | 2 +- src/hooks/ghost/UnstableProvider.jsx | 89 ++++++++++++++----------- 3 files changed, 52 insertions(+), 41 deletions(-) diff --git a/package.json b/package.json index 293803b..399c2cb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ghost-dao-interface", "private": true, - "version": "0.7.9", + "version": "0.7.10", "type": "module", "scripts": { "dev": "vite", diff --git a/src/components/TopBar/SelectNetwork.jsx b/src/components/TopBar/SelectNetwork.jsx index ccb5e26..34d1b33 100644 --- a/src/components/TopBar/SelectNetwork.jsx +++ b/src/components/TopBar/SelectNetwork.jsx @@ -92,7 +92,7 @@ function SelectNetwork({ chainId, wrongNetworkToastId, setWrongNetworkToastId, s return ( - + {!small && {chain.name}} diff --git a/src/hooks/ghost/UnstableProvider.jsx b/src/hooks/ghost/UnstableProvider.jsx index e3656a5..3136ac4 100644 --- a/src/hooks/ghost/UnstableProvider.jsx +++ b/src/hooks/ghost/UnstableProvider.jsx @@ -1,14 +1,19 @@ -import { createContext, useEffect, useContext, useState, useMemo, useCallback } from "react" +import { createContext, useEffect, useContext, useState, useMemo, useCallback, useRef } from "react" import { Unstable } from "@substrate/connect-discovery" import { createClient } from "@polkadot-api/substrate-client" import { getObservableClient } from "@polkadot-api/observable-client" import useSWR from "swr" +const MAX_BLOCK_TIMEOUT = 15000 const DEFAULT_CHAIN_ID = "0x475e48fab52f3d0587b6b03101d224560c549e984d1dee197b7d8b55830e7da3" const UnstableProvider = createContext(null) export const useUnstableProvider = () => useContext(UnstableProvider) export const UnstableProviderProvider = ({ children }) => { + const [chainId, setChainId] = useState(DEFAULT_CHAIN_ID); + const [isConnected, setIsConnected] = useState(false); + const [reconnectTicket, setReconnectTicket] = useState(0); + const { data: providerDetails } = useSWR("getGhostProviders", () => Unstable.getSubstrateConnectExtensionProviders() ); @@ -19,60 +24,66 @@ export const UnstableProviderProvider = ({ children }) => { () => providerDetail ? providerDetail.provider : null ); - const [chainId, setChainId] = useState(DEFAULT_CHAIN_ID); - const [isConnected, setIsConnected] = useState(false); - const [reconnectTicket, setReconnectTicket] = useState(0); - - const reconnect = () => setReconnectTicket(prev => prev + 1); - const createConnectWithStatus = (originalConnect, onConnect) => { - return (observer) => { - onConnect(true); - return originalConnect(observer); - }; - }; - const client = useMemo(() => { if (!provider || !chainId) return undefined; + const chain = provider.getChains()[chainId]; if (!chain) return undefined; - const wrappedConnect = createConnectWithStatus(chain.connect, setIsConnected); - return createClient(wrappedConnect); + + return createClient(chain.connect) }, [provider, chainId, reconnectTicket]); - const observableClient = useMemo(() => { - return client ? getObservableClient(client) : undefined; - }, [client]); + const observableClient = useMemo(() => client ? getObservableClient(client) : undefined, [client]); + const chainHead$ = useMemo(() => observableClient?.chainHead$(), [observableClient]); - const chainHead$ = useMemo(() => { - return observableClient ? observableClient.chainHead$() : undefined; - }, [observableClient]); + const lastBlockNumber = useRef(0); useEffect(() => { if (!chainHead$) return; - const sub = chainHead$.runtime$.subscribe({ - next: () => setIsConnected(true), + + lastBlockNumber.current = 0; + let timeoutId; + + const sub = chainHead$.bestBlocks$.subscribe({ + next: (blocks) => { + const currentHeight = blocks.at(0)?.number ?? -1; + + if (currentHeight > lastBlockNumber.current) { + lastBlockNumber.current = currentHeight; + setIsConnected(true); + + clearTimeout(timeoutId); + timeoutId = setTimeout(() => { + setIsConnected(false); + setReconnectTicket(t => t + 1); + }, MAX_BLOCK_TIMEOUT); + } + }, error: (err) => { setIsConnected(false); - setTimeout(reconnect, 3000); + setTimeout(() => setReconnectTicket(t => t + 1), 1000); } }); - return () => sub.unsubscribe(); - }, [chainHead$, reconnect]); + + return () => { + sub.unsubscribe(); + clearTimeout(timeoutId); + }; + }, [chainHead$]); + + const value = useMemo(() => ({ + isConnected, + providerDetails, + providerDetail, + connectProviderDetail: setProviderDetail, + chainId, + client, + setChainId, + chainHead$ + }), [isConnected, providerDetails, providerDetail, chainId, client, chainHead$]); return ( - + {children} );