From 298d9d51ac39a97e2ef8a28e6f8ec4145e149c4f Mon Sep 17 00:00:00 2001 From: Magomed Khamidov <53529533+KosmosKey@users.noreply.github.com> Date: Mon, 26 Feb 2024 11:33:57 +0000 Subject: [PATCH 1/8] feat: chain modal (v2) --- .changeset/sweet-plants-joke.md | 37 ++++ packages/example/pages/_app.tsx | 24 +++ .../src/components/ChainModal/Chain.tsx | 4 +- .../src/components/ChainModal/ChainModal.tsx | 7 +- .../ConnectButton/ConnectButton.tsx | 181 ++++++++++-------- .../ConnectButton/ConnectButtonRenderer.tsx | 8 +- .../RainbowKitProvider/ModalContext.tsx | 5 +- .../RainbowKitChainContext.tsx | 10 +- .../RainbowKitProvider/RainbowKitProvider.tsx | 7 +- .../src/wallets/useWalletConnectors.ts | 43 +++-- 10 files changed, 221 insertions(+), 105 deletions(-) create mode 100644 .changeset/sweet-plants-joke.md diff --git a/.changeset/sweet-plants-joke.md b/.changeset/sweet-plants-joke.md new file mode 100644 index 0000000000..052f271a29 --- /dev/null +++ b/.changeset/sweet-plants-joke.md @@ -0,0 +1,37 @@ +--- +"@rainbow-me/rainbowkit": patch +"example": patch +--- + +`` now allows you to switch chains without having your wallet connected. + +You also have the option to specify a `ignoreChainModalOnConnect` prop to the `` if you want to wait until the user has connected their wallet. + +**Example usage** + +```ts +import "@rainbow-me/rainbowkit/styles.css"; + +import { RainbowKitProvider, getDefaultConfig } from "@rainbow-me/rainbowkit"; +import { WagmiProvider } from "wagmi"; +import { mainnet } from "wagmi/chains"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; + +const config = getDefaultConfig({ + appName: "RainbowKit demo", + projectId: "YOUR_PROJECT_ID", + chains: [mainnet], +}); + +const queryClient = new QueryClient(); + +const App = () => { + return ( + + + {/* Your App */} + + + ); +}; +``` diff --git a/packages/example/pages/_app.tsx b/packages/example/pages/_app.tsx index 1d91a33e4e..9b06b4ddb7 100644 --- a/packages/example/pages/_app.tsx +++ b/packages/example/pages/_app.tsx @@ -277,6 +277,8 @@ function RainbowKitApp({ const [coolModeEnabled, setCoolModeEnabled] = useState(false); const [modalSize, setModalSize] = useState('wide'); const [showDisclaimer, setShowDisclaimer] = useState(false); + const [ignoreChainModalOnConnect, setIgnoreChainModalOnConnect] = + useState(false); const [customAvatar, setCustomAvatar] = useState(false); const routerLocale = router.locale as Locale; @@ -321,6 +323,7 @@ function RainbowKitApp({ ...demoAppInfo, ...(showDisclaimer && { disclaimer: DisclaimerDemo }), }} + ignoreChainModalOnConnect={ignoreChainModalOnConnect} avatar={customAvatar ? CustomAvatar : undefined} locale={locale} coolMode={coolModeEnabled} @@ -462,6 +465,27 @@ function RainbowKitApp({ /> + + + + + + + setIgnoreChainModalOnConnect(e.target.checked) + } + type="checkbox" + /> + + modalSize diff --git a/packages/rainbowkit/src/components/ChainModal/Chain.tsx b/packages/rainbowkit/src/components/ChainModal/Chain.tsx index 7fa6ff72fd..d57b450354 100644 --- a/packages/rainbowkit/src/components/ChainModal/Chain.tsx +++ b/packages/rainbowkit/src/components/ChainModal/Chain.tsx @@ -10,6 +10,7 @@ import { useRainbowKitChains } from '../RainbowKitProvider/RainbowKitChainContex import { Text } from '../Text/Text'; interface ChainProps { + isConnected?: boolean; chainId: number; currentChainId: number; switchChain: ReturnType['switchChain']; @@ -31,6 +32,7 @@ const Chain = ({ name, iconBackground, idx, + isConnected, }: ChainProps) => { const mobile = isMobile(); const { i18n } = useContext(I18nContext); @@ -74,7 +76,7 @@ const Chain = ({ )}
{name ?? name}
- {isCurrentChain && ( + {isCurrentChain && isConnected && ( (null); const { switchChain } = useSwitchChain({ @@ -102,6 +104,7 @@ export function ChainModal({ onClose, open }: ChainModalProps) { chainId={id} currentChainId={chainId} switchChain={switchChain} + isConnected={isConnected} chainIconSize={chainIconSize} isLoading={pendingChainId === id} src={iconUrl} diff --git a/packages/rainbowkit/src/components/ConnectButton/ConnectButton.tsx b/packages/rainbowkit/src/components/ConnectButton/ConnectButton.tsx index 1f7146ae3c..f9b60fae87 100644 --- a/packages/rainbowkit/src/components/ConnectButton/ConnectButton.tsx +++ b/packages/rainbowkit/src/components/ConnectButton/ConnectButton.tsx @@ -12,7 +12,11 @@ import { Avatar } from '../Avatar/Avatar'; import { Box } from '../Box/Box'; import { DropdownIcon } from '../Icons/Dropdown'; import { I18nContext } from '../RainbowKitProvider/I18nContext'; -import { useRainbowKitChains } from '../RainbowKitProvider/RainbowKitChainContext'; +import { + useIgnoreChainModalOnConnect, + useInitialChainId, + useRainbowKitChains, +} from '../RainbowKitProvider/RainbowKitChainContext'; import { useShowBalance } from '../RainbowKitProvider/ShowBalanceContext'; import { ConnectButtonRenderer } from './ConnectButtonRenderer'; @@ -41,10 +45,11 @@ export function ConnectButton({ }: ConnectButtonProps) { const chains = useRainbowKitChains(); const connectionStatus = useConnectionStatus(); + const ignoreChainModalOnConnect = useIgnoreChainModalOnConnect(); const { setShowBalance } = useShowBalance(); - const [ready, setReady] = useState(false); - + const initialChainId = useInitialChainId(); const { i18n } = useContext(I18nContext); + const [ready, setReady] = useState(false); // biome-ignore lint/correctness/useExhaustiveDependencies: useEffect(() => { @@ -62,6 +67,14 @@ export function ConnectButton({ openChainModal, openConnectModal, }) => { + // Hide the chain button if the user has specified a custom "initialChainId". + // This prevents users from switching to a different chain when they have a desired + // chain that they want to switch to. We also hide the chain button if user is using + // our authentication provider and is not yet signed in. + const shouldHideChainButton = + ((initialChainId || ignoreChainModalOnConnect) && + connectionStatus !== 'connected') || + connectionStatus === 'unauthenticated'; const ready = mounted && connectionStatus !== 'loading'; const unsupportedChain = chain?.unsupported ?? false; @@ -78,98 +91,98 @@ export function ConnectButton({ }, })} > - {ready && account && connectionStatus === 'connected' ? ( - <> - {chain && (chains.length > 1 || unsupportedChain) && ( + {ready && !shouldHideChainButton && chain && chains.length > 1 && ( + + value === 'none' ? 'none' : 'flex', + )} + fontFamily="body" + fontWeight="bold" + gap="6" + key={ + // Force re-mount to prevent CSS transition + unsupportedChain ? 'unsupported' : 'supported' + } + onClick={openChainModal} + paddingX="10" + paddingY="8" + testId={ + unsupportedChain ? 'wrong-network-button' : 'chain-button' + } + transition="default" + type="button" + > + {unsupportedChain ? ( - value === 'none' ? 'none' : 'flex', - )} - fontFamily="body" - fontWeight="bold" - gap="6" - key={ - // Force re-mount to prevent CSS transition - unsupportedChain ? 'unsupported' : 'supported' - } - onClick={openChainModal} - paddingX="10" - paddingY="8" - testId={ - unsupportedChain ? 'wrong-network-button' : 'chain-button' - } - transition="default" - type="button" + display="flex" + height="24" + paddingX="4" > - {unsupportedChain ? ( + {i18n.t('connect_wallet.wrong_network.label')} + + ) : ( + + {chain.hasIcon ? ( + value === 'full' || value === 'icon' + ? 'block' + : 'none', + )} height="24" - paddingX="4" + width="24" > - {i18n.t('connect_wallet.wrong_network.label')} + - ) : ( - - {chain.hasIcon ? ( - - value === 'full' || value === 'icon' - ? 'block' - : 'none', - )} - height="24" - width="24" - > - - - ) : null} - { - if (value === 'icon' && !chain.iconUrl) { - return 'block'; // Show the chain name if there is no iconUrl - } + ) : null} + { + if (value === 'icon' && !chain.iconUrl) { + return 'block'; // Show the chain name if there is no iconUrl + } - return value === 'full' || value === 'name' - ? 'block' - : 'none'; - })} - > - {chain.name ?? chain.id} - - - )} - + return value === 'full' || value === 'name' + ? 'block' + : 'none'; + })} + > + {chain.name ?? chain.id} + )} + + + )} + {ready && account && connectionStatus === 'connected' ? ( + <> {!unsupportedChain && ( chain.id === chainId, @@ -145,7 +147,7 @@ export function ConnectButtonRenderer({ iconUrl: resolvedChainIconUrl, id: chainId, name: chainName, - unsupported: !isCurrentChainSupported, + unsupported: isConnected && !isCurrentChainSupported, } : undefined, chainModalOpen, diff --git a/packages/rainbowkit/src/components/RainbowKitProvider/ModalContext.tsx b/packages/rainbowkit/src/components/RainbowKitProvider/ModalContext.tsx index 29c72f0b09..a5973b9603 100644 --- a/packages/rainbowkit/src/components/RainbowKitProvider/ModalContext.tsx +++ b/packages/rainbowkit/src/components/RainbowKitProvider/ModalContext.tsx @@ -109,7 +109,10 @@ export function ModalProvider({ children }: ModalProviderProps) { ? openAccountModal : undefined, openChainModal: - connectionStatus === 'connected' ? openChainModal : undefined, + connectionStatus === 'connected' || + connectionStatus === 'disconnected' + ? openChainModal + : undefined, openConnectModal: connectionStatus === 'disconnected' || connectionStatus === 'unauthenticated' diff --git a/packages/rainbowkit/src/components/RainbowKitProvider/RainbowKitChainContext.tsx b/packages/rainbowkit/src/components/RainbowKitProvider/RainbowKitChainContext.tsx index 42e11a647c..35badec38a 100644 --- a/packages/rainbowkit/src/components/RainbowKitProvider/RainbowKitChainContext.tsx +++ b/packages/rainbowkit/src/components/RainbowKitProvider/RainbowKitChainContext.tsx @@ -11,20 +11,24 @@ export interface RainbowKitChain extends Chain { interface RainbowKitChainContextValue { chains: RainbowKitChain[]; initialChainId?: number; + ignoreChainModalOnConnect: boolean; } const RainbowKitChainContext = createContext({ chains: [], + ignoreChainModalOnConnect: false, }); interface RainbowKitChainProviderProps { initialChain?: Chain | number; + ignoreChainModalOnConnect: boolean; children: ReactNode; } export function RainbowKitChainProvider({ children, initialChain, + ignoreChainModalOnConnect, }: RainbowKitChainProviderProps) { const { chains } = useConfig(); @@ -35,8 +39,9 @@ export function RainbowKitChainProvider({ chains: provideRainbowKitChains(chains), initialChainId: typeof initialChain === 'number' ? initialChain : initialChain?.id, + ignoreChainModalOnConnect, }), - [chains, initialChain], + [chains, initialChain, ignoreChainModalOnConnect], )} > {children} @@ -50,6 +55,9 @@ export const useRainbowKitChains = () => export const useInitialChainId = () => useContext(RainbowKitChainContext).initialChainId; +export const useIgnoreChainModalOnConnect = () => + useContext(RainbowKitChainContext).ignoreChainModalOnConnect; + export const useRainbowKitChainsById = () => { const rainbowkitChains = useRainbowKitChains(); diff --git a/packages/rainbowkit/src/components/RainbowKitProvider/RainbowKitProvider.tsx b/packages/rainbowkit/src/components/RainbowKitProvider/RainbowKitProvider.tsx index fde6e7d5b6..38045183f2 100644 --- a/packages/rainbowkit/src/components/RainbowKitProvider/RainbowKitProvider.tsx +++ b/packages/rainbowkit/src/components/RainbowKitProvider/RainbowKitProvider.tsx @@ -65,6 +65,7 @@ export interface RainbowKitProviderProps { avatar?: AvatarComponent; modalSize?: ModalSizes; locale?: Locale; + ignoreChainModalOnConnect?: boolean; } const defaultTheme = lightTheme(); @@ -80,6 +81,7 @@ export function RainbowKitProvider({ modalSize = ModalSizeOptions.WIDE, showRecentTransactions = false, theme = defaultTheme, + ignoreChainModalOnConnect = false, }: RainbowKitProviderProps) { usePreloadImages(); useFingerprint(); @@ -102,7 +104,10 @@ export function RainbowKitProvider({ const avatarContext = avatar ?? defaultAvatar; return ( - + diff --git a/packages/rainbowkit/src/wallets/useWalletConnectors.ts b/packages/rainbowkit/src/wallets/useWalletConnectors.ts index fa12af79d5..7c7fc05fcc 100644 --- a/packages/rainbowkit/src/wallets/useWalletConnectors.ts +++ b/packages/rainbowkit/src/wallets/useWalletConnectors.ts @@ -1,10 +1,11 @@ -import { Config, Connector, useConnect } from 'wagmi'; +import { Config, Connector, useChainId, useConnect } from 'wagmi'; import { ConnectMutateAsync } from 'wagmi/query'; -import { indexBy } from '../utils/indexBy'; import { + useIgnoreChainModalOnConnect, useInitialChainId, useRainbowKitChains, -} from './../components/RainbowKitProvider/RainbowKitChainContext'; +} from '../components/RainbowKitProvider/RainbowKitChainContext'; +import { indexBy } from '../utils/indexBy'; import { WagmiConnectorInstance, WalletInstance } from './Wallet'; import { getDesktopDownloadUrl, @@ -37,7 +38,9 @@ export function useWalletConnectors( mergeEIP6963WithRkConnectors = false, ): WalletConnector[] { const rainbowKitChains = useRainbowKitChains(); + const currentChainId = useChainId(); const intialChainId = useInitialChainId(); + const ignoreChainModalOnConnect = useIgnoreChainModalOnConnect(); const { connectAsync, connectors: defaultConnectors_untyped } = useConnect(); const defaultCreatedConnectors = defaultConnectors_untyped as WagmiConnectorInstance[]; @@ -50,17 +53,33 @@ export function useWalletConnectors( ...(connector.rkDetails || {}), })) as WalletInstance[]; - async function connectWallet(connector: Connector) { - const walletChainId = await connector.getChainId(); - const result = await connectAsync({ - chainId: - // The goal here is to ensure users are always on a supported chain when connecting. - // If an `initialChain` prop was provided to RainbowKitProvider, use that. - intialChainId ?? - // Otherwise, if the wallet is already on a supported chain, use that to avoid a chain switch prompt. + const computeChainid = async (connector: Connector) => { + // Return inital chain if provided + if (intialChainId) return intialChainId; + + // If user chooses to ignore the modal chain before connection + // we will make sure to use one of our rainbowkit chains + if (ignoreChainModalOnConnect) { + const walletChainId = await connector.getChainId(); + + // Otherwise, if the wallet is already on a supported chain, use that to avoid a chain switch prompt. + return ( rainbowKitChains.find(({ id }) => id === walletChainId)?.id ?? // Finally, fall back to the first chain provided to RainbowKitProvider. - rainbowKitChains[0]?.id, + rainbowKitChains[0]?.id + ); + } + + // Otherwise return the current chain id which wagmi v2 provides + // out of the box without having your wallet connected. + return currentChainId; + }; + + async function connectWallet(connector: Connector) { + const chainId = await computeChainid(connector); + + const result = await connectAsync({ + chainId, connector, }); From 01055b6b39d76b931deffb752165540ad1f8ee89 Mon Sep 17 00:00:00 2001 From: Mago Khamidov <53529533+KosmosKey@users.noreply.github.com> Date: Mon, 26 Feb 2024 13:00:20 +0000 Subject: [PATCH 2/8] chore: tweak changeset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Lukas Lücke --- .changeset/sweet-plants-joke.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/sweet-plants-joke.md b/.changeset/sweet-plants-joke.md index 052f271a29..d8135175fd 100644 --- a/.changeset/sweet-plants-joke.md +++ b/.changeset/sweet-plants-joke.md @@ -28,8 +28,8 @@ const queryClient = new QueryClient(); const App = () => { return ( - - {/* Your App */} + + {/* Your App */} ); From 7431bdf2caae8f2862102b27c6dbaf11017d07c2 Mon Sep 17 00:00:00 2001 From: Magomed Khamidov <53529533+KosmosKey@users.noreply.github.com> Date: Thu, 29 Feb 2024 00:08:44 +0000 Subject: [PATCH 3/8] fix: merge conflicts --- .changeset/sweet-plants-joke.md | 5 +++-- packages/example/pages/_app.tsx | 2 +- .../components/RainbowKitProvider/RainbowKitProvider.tsx | 2 +- site/data/en-US/docs/chains.mdx | 8 ++++++++ 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.changeset/sweet-plants-joke.md b/.changeset/sweet-plants-joke.md index d8135175fd..39e5fc8d3b 100644 --- a/.changeset/sweet-plants-joke.md +++ b/.changeset/sweet-plants-joke.md @@ -1,11 +1,12 @@ --- "@rainbow-me/rainbowkit": patch "example": patch +"site": patch --- `` now allows you to switch chains without having your wallet connected. -You also have the option to specify a `ignoreChainModalOnConnect` prop to the `` if you want to wait until the user has connected their wallet. +You just need to set `ignoreChainModalOnConnect` prop to `false` in `` if you'd like to use the chain modal without having your wallet connected. **Example usage** @@ -29,7 +30,7 @@ const App = () => { return ( - {/* Your App */} + {/* Your App */} ); diff --git a/packages/example/pages/_app.tsx b/packages/example/pages/_app.tsx index 9b06b4ddb7..0dfc6ffb19 100644 --- a/packages/example/pages/_app.tsx +++ b/packages/example/pages/_app.tsx @@ -278,7 +278,7 @@ function RainbowKitApp({ const [modalSize, setModalSize] = useState('wide'); const [showDisclaimer, setShowDisclaimer] = useState(false); const [ignoreChainModalOnConnect, setIgnoreChainModalOnConnect] = - useState(false); + useState(true); const [customAvatar, setCustomAvatar] = useState(false); const routerLocale = router.locale as Locale; diff --git a/packages/rainbowkit/src/components/RainbowKitProvider/RainbowKitProvider.tsx b/packages/rainbowkit/src/components/RainbowKitProvider/RainbowKitProvider.tsx index 38045183f2..a13e11e4a7 100644 --- a/packages/rainbowkit/src/components/RainbowKitProvider/RainbowKitProvider.tsx +++ b/packages/rainbowkit/src/components/RainbowKitProvider/RainbowKitProvider.tsx @@ -81,7 +81,7 @@ export function RainbowKitProvider({ modalSize = ModalSizeOptions.WIDE, showRecentTransactions = false, theme = defaultTheme, - ignoreChainModalOnConnect = false, + ignoreChainModalOnConnect = true, }: RainbowKitProviderProps) { usePreloadImages(); useFingerprint(); diff --git a/site/data/en-US/docs/chains.mdx b/site/data/en-US/docs/chains.mdx index 8ac0d33fc1..c33534370a 100644 --- a/site/data/en-US/docs/chains.mdx +++ b/site/data/en-US/docs/chains.mdx @@ -25,6 +25,14 @@ As a convenience, you can also pass a chain object. ``` +### Chain Modal v2 + +If you want to switch chains before connecting your wallet, just set `ignoreChainModalOnConnect` prop to `false` in ``. This will let you interact with the chain modal before your wallet is connected, and also while it is connected. + +```tsx + +``` + ### Custom chain metadata Several chain icons and backgrounds are provided by default, but you can customize the icon and background for each chain using the `iconUrl` and `iconBackground` properties. From a55b48fa4a99cc6a967838203f1953f18a3357ae Mon Sep 17 00:00:00 2001 From: Magomed Khamidov <53529533+KosmosKey@users.noreply.github.com> Date: Wed, 13 Mar 2024 16:02:22 +0000 Subject: [PATCH 4/8] refactor: ignoreChainModalOnConnect replace with chainModalOnConnect prop --- .changeset/sweet-plants-joke.md | 4 ++-- packages/example/pages/_app.tsx | 17 ++++++++--------- .../components/ConnectButton/ConnectButton.tsx | 11 +++++------ .../RainbowKitChainContext.tsx | 16 ++++++++-------- .../RainbowKitProvider/RainbowKitProvider.tsx | 6 +++--- .../src/wallets/useWalletConnectors.ts | 6 +++--- site/data/en-US/docs/chains.mdx | 4 ++-- 7 files changed, 31 insertions(+), 33 deletions(-) diff --git a/.changeset/sweet-plants-joke.md b/.changeset/sweet-plants-joke.md index 39e5fc8d3b..a453ccb398 100644 --- a/.changeset/sweet-plants-joke.md +++ b/.changeset/sweet-plants-joke.md @@ -6,7 +6,7 @@ `` now allows you to switch chains without having your wallet connected. -You just need to set `ignoreChainModalOnConnect` prop to `false` in `` if you'd like to use the chain modal without having your wallet connected. +You just need to set `chainModalOnConnect` prop to `true` in `` if you'd like to use the chain modal without having your wallet connected. **Example usage** @@ -30,7 +30,7 @@ const App = () => { return ( - {/* Your App */} + {/* Your App */} ); diff --git a/packages/example/pages/_app.tsx b/packages/example/pages/_app.tsx index 0dfc6ffb19..bc094e9cb1 100644 --- a/packages/example/pages/_app.tsx +++ b/packages/example/pages/_app.tsx @@ -277,8 +277,7 @@ function RainbowKitApp({ const [coolModeEnabled, setCoolModeEnabled] = useState(false); const [modalSize, setModalSize] = useState('wide'); const [showDisclaimer, setShowDisclaimer] = useState(false); - const [ignoreChainModalOnConnect, setIgnoreChainModalOnConnect] = - useState(true); + const [chainModalOnConnect, setChainModalOnConnect] = useState(false); const [customAvatar, setCustomAvatar] = useState(false); const routerLocale = router.locale as Locale; @@ -323,7 +322,7 @@ function RainbowKitApp({ ...demoAppInfo, ...(showDisclaimer && { disclaimer: DisclaimerDemo }), }} - ignoreChainModalOnConnect={ignoreChainModalOnConnect} + chainModalOnConnect={chainModalOnConnect} avatar={customAvatar ? CustomAvatar : undefined} locale={locale} coolMode={coolModeEnabled} @@ -468,19 +467,19 @@ function RainbowKitApp({ - setIgnoreChainModalOnConnect(e.target.checked) + setChainModalOnConnect(e.target.checked) } type="checkbox" /> diff --git a/packages/rainbowkit/src/components/ConnectButton/ConnectButton.tsx b/packages/rainbowkit/src/components/ConnectButton/ConnectButton.tsx index f9b60fae87..1cd983fe40 100644 --- a/packages/rainbowkit/src/components/ConnectButton/ConnectButton.tsx +++ b/packages/rainbowkit/src/components/ConnectButton/ConnectButton.tsx @@ -13,7 +13,7 @@ import { Box } from '../Box/Box'; import { DropdownIcon } from '../Icons/Dropdown'; import { I18nContext } from '../RainbowKitProvider/I18nContext'; import { - useIgnoreChainModalOnConnect, + useChainModalOnConnect, useInitialChainId, useRainbowKitChains, } from '../RainbowKitProvider/RainbowKitChainContext'; @@ -45,7 +45,7 @@ export function ConnectButton({ }: ConnectButtonProps) { const chains = useRainbowKitChains(); const connectionStatus = useConnectionStatus(); - const ignoreChainModalOnConnect = useIgnoreChainModalOnConnect(); + const chainModalOnConnect = useChainModalOnConnect(); const { setShowBalance } = useShowBalance(); const initialChainId = useInitialChainId(); const { i18n } = useContext(I18nContext); @@ -56,7 +56,7 @@ export function ConnectButton({ setShowBalance(showBalance); if (!ready) setReady(true); }, [showBalance, setShowBalance]); - + return ready ? ( {({ @@ -72,9 +72,8 @@ export function ConnectButton({ // chain that they want to switch to. We also hide the chain button if user is using // our authentication provider and is not yet signed in. const shouldHideChainButton = - ((initialChainId || ignoreChainModalOnConnect) && - connectionStatus !== 'connected') || - connectionStatus === 'unauthenticated'; + (initialChainId || !chainModalOnConnect) && + connectionStatus !== 'connected'; const ready = mounted && connectionStatus !== 'loading'; const unsupportedChain = chain?.unsupported ?? false; diff --git a/packages/rainbowkit/src/components/RainbowKitProvider/RainbowKitChainContext.tsx b/packages/rainbowkit/src/components/RainbowKitProvider/RainbowKitChainContext.tsx index 35badec38a..651d7bf90a 100644 --- a/packages/rainbowkit/src/components/RainbowKitProvider/RainbowKitChainContext.tsx +++ b/packages/rainbowkit/src/components/RainbowKitProvider/RainbowKitChainContext.tsx @@ -11,24 +11,24 @@ export interface RainbowKitChain extends Chain { interface RainbowKitChainContextValue { chains: RainbowKitChain[]; initialChainId?: number; - ignoreChainModalOnConnect: boolean; + chainModalOnConnect: boolean; } const RainbowKitChainContext = createContext({ chains: [], - ignoreChainModalOnConnect: false, + chainModalOnConnect: false, }); interface RainbowKitChainProviderProps { initialChain?: Chain | number; - ignoreChainModalOnConnect: boolean; + chainModalOnConnect: boolean; children: ReactNode; } export function RainbowKitChainProvider({ children, initialChain, - ignoreChainModalOnConnect, + chainModalOnConnect, }: RainbowKitChainProviderProps) { const { chains } = useConfig(); @@ -39,9 +39,9 @@ export function RainbowKitChainProvider({ chains: provideRainbowKitChains(chains), initialChainId: typeof initialChain === 'number' ? initialChain : initialChain?.id, - ignoreChainModalOnConnect, + chainModalOnConnect, }), - [chains, initialChain, ignoreChainModalOnConnect], + [chains, initialChain, chainModalOnConnect], )} > {children} @@ -55,8 +55,8 @@ export const useRainbowKitChains = () => export const useInitialChainId = () => useContext(RainbowKitChainContext).initialChainId; -export const useIgnoreChainModalOnConnect = () => - useContext(RainbowKitChainContext).ignoreChainModalOnConnect; +export const useChainModalOnConnect = () => + useContext(RainbowKitChainContext).chainModalOnConnect; export const useRainbowKitChainsById = () => { const rainbowkitChains = useRainbowKitChains(); diff --git a/packages/rainbowkit/src/components/RainbowKitProvider/RainbowKitProvider.tsx b/packages/rainbowkit/src/components/RainbowKitProvider/RainbowKitProvider.tsx index a13e11e4a7..8ec881327f 100644 --- a/packages/rainbowkit/src/components/RainbowKitProvider/RainbowKitProvider.tsx +++ b/packages/rainbowkit/src/components/RainbowKitProvider/RainbowKitProvider.tsx @@ -65,7 +65,7 @@ export interface RainbowKitProviderProps { avatar?: AvatarComponent; modalSize?: ModalSizes; locale?: Locale; - ignoreChainModalOnConnect?: boolean; + chainModalOnConnect?: boolean; } const defaultTheme = lightTheme(); @@ -81,7 +81,7 @@ export function RainbowKitProvider({ modalSize = ModalSizeOptions.WIDE, showRecentTransactions = false, theme = defaultTheme, - ignoreChainModalOnConnect = true, + chainModalOnConnect = false, }: RainbowKitProviderProps) { usePreloadImages(); useFingerprint(); @@ -106,7 +106,7 @@ export function RainbowKitProvider({ return ( diff --git a/packages/rainbowkit/src/wallets/useWalletConnectors.ts b/packages/rainbowkit/src/wallets/useWalletConnectors.ts index e254fb2abb..fb654450a7 100644 --- a/packages/rainbowkit/src/wallets/useWalletConnectors.ts +++ b/packages/rainbowkit/src/wallets/useWalletConnectors.ts @@ -2,7 +2,7 @@ import { Config, Connector, useChainId, useConnect } from 'wagmi'; import { ConnectMutateAsync } from 'wagmi/query'; import { useWalletConnectOpenState } from '../components/RainbowKitProvider/ModalContext'; import { - useIgnoreChainModalOnConnect, + useChainModalOnConnect, useInitialChainId, useRainbowKitChains, } from '../components/RainbowKitProvider/RainbowKitChainContext'; @@ -41,7 +41,7 @@ export function useWalletConnectors( const rainbowKitChains = useRainbowKitChains(); const currentChainId = useChainId(); const intialChainId = useInitialChainId(); - const ignoreChainModalOnConnect = useIgnoreChainModalOnConnect(); + const chainModalOnConnect = useChainModalOnConnect(); const { connectAsync, connectors: defaultConnectors_untyped } = useConnect(); const defaultCreatedConnectors = defaultConnectors_untyped as WagmiConnectorInstance[]; @@ -62,7 +62,7 @@ export function useWalletConnectors( // If user chooses to ignore the modal chain before connection // we will make sure to use one of our rainbowkit chains - if (ignoreChainModalOnConnect) { + if (!chainModalOnConnect) { const walletChainId = await connector.getChainId(); // Otherwise, if the wallet is already on a supported chain, use that to avoid a chain switch prompt. diff --git a/site/data/en-US/docs/chains.mdx b/site/data/en-US/docs/chains.mdx index c33534370a..9d01b8cc2b 100644 --- a/site/data/en-US/docs/chains.mdx +++ b/site/data/en-US/docs/chains.mdx @@ -27,10 +27,10 @@ As a convenience, you can also pass a chain object. ### Chain Modal v2 -If you want to switch chains before connecting your wallet, just set `ignoreChainModalOnConnect` prop to `false` in ``. This will let you interact with the chain modal before your wallet is connected, and also while it is connected. +If you want to switch chains before connecting your wallet, just set `chainModalOnConnect` prop to `true` in ``. This will let you interact with the chain modal before your wallet is connected, and also while it is connected. ```tsx - + ``` ### Custom chain metadata From 193cbab4617dfe11ae3b002678d14df03758b14d Mon Sep 17 00:00:00 2001 From: Magomed Khamidov <53529533+KosmosKey@users.noreply.github.com> Date: Wed, 13 Mar 2024 16:05:55 +0000 Subject: [PATCH 5/8] chore: update rainbowkit changeset to minor --- .changeset/sweet-plants-joke.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/sweet-plants-joke.md b/.changeset/sweet-plants-joke.md index a453ccb398..8187c527c9 100644 --- a/.changeset/sweet-plants-joke.md +++ b/.changeset/sweet-plants-joke.md @@ -1,5 +1,5 @@ --- -"@rainbow-me/rainbowkit": patch +"@rainbow-me/rainbowkit": minor "example": patch "site": patch --- From 9d111d6a3cc65fcee109f71afd556c2abaecab61 Mon Sep 17 00:00:00 2001 From: Magomed Khamidov <53529533+KosmosKey@users.noreply.github.com> Date: Wed, 13 Mar 2024 16:33:35 +0000 Subject: [PATCH 6/8] chore: refactor ConnectButton and ModalContext for openChainModal --- .../ConnectButton/ConnectButton.tsx | 7 ++++--- .../RainbowKitProvider/ModalContext.tsx | 20 ++++++++++++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/packages/rainbowkit/src/components/ConnectButton/ConnectButton.tsx b/packages/rainbowkit/src/components/ConnectButton/ConnectButton.tsx index 1cd983fe40..caba8beff8 100644 --- a/packages/rainbowkit/src/components/ConnectButton/ConnectButton.tsx +++ b/packages/rainbowkit/src/components/ConnectButton/ConnectButton.tsx @@ -56,7 +56,7 @@ export function ConnectButton({ setShowBalance(showBalance); if (!ready) setReady(true); }, [showBalance, setShowBalance]); - + return ready ? ( {({ @@ -72,8 +72,9 @@ export function ConnectButton({ // chain that they want to switch to. We also hide the chain button if user is using // our authentication provider and is not yet signed in. const shouldHideChainButton = - (initialChainId || !chainModalOnConnect) && - connectionStatus !== 'connected'; + ((initialChainId || !chainModalOnConnect) && + connectionStatus !== 'connected') || + connectionStatus === 'unauthenticated'; const ready = mounted && connectionStatus !== 'loading'; const unsupportedChain = chain?.unsupported ?? false; diff --git a/packages/rainbowkit/src/components/RainbowKitProvider/ModalContext.tsx b/packages/rainbowkit/src/components/RainbowKitProvider/ModalContext.tsx index 88fc7b435d..5e7d47d6c9 100644 --- a/packages/rainbowkit/src/components/RainbowKitProvider/ModalContext.tsx +++ b/packages/rainbowkit/src/components/RainbowKitProvider/ModalContext.tsx @@ -13,6 +13,10 @@ import { AccountModal } from '../AccountModal/AccountModal'; import { ChainModal } from '../ChainModal/ChainModal'; import { ConnectModal } from '../ConnectModal/ConnectModal'; import { useAuthenticationStatus } from './AuthenticationContext'; +import { + useChainModalOnConnect, + useInitialChainId, +} from './RainbowKitChainContext'; function useModalStateValue() { const [isModalOpen, setModalOpen] = useState(false); @@ -74,6 +78,9 @@ export function ModalProvider({ children }: ModalProviderProps) { const { chainId } = useAccount(); const { chains } = useConfig(); + const chainModalOnConnect = useChainModalOnConnect(); + const initialChainId = useInitialChainId(); + const isCurrentChainSupported = chains.some((chain) => chain.id === chainId); interface CloseModalsOptions { @@ -104,6 +111,12 @@ export function ModalProvider({ children }: ModalProviderProps) { if (isUnauthenticated) closeModals(); }, [isUnauthenticated]); + const shouldHideChainModal = + ((initialChainId || !chainModalOnConnect) && + connectionStatus !== 'connected') || + connectionStatus === 'unauthenticated' || + connectionStatus === 'loading'; + return ( Date: Tue, 9 Apr 2024 01:43:59 +0100 Subject: [PATCH 7/8] chore: if chainStatus is not none show the current chain status --- .../ConnectButton/ConnectButton.tsx | 29 +++++++++++++++---- .../RainbowKitProvider/ModalContext.tsx | 8 +++-- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/packages/rainbowkit/src/components/ConnectButton/ConnectButton.tsx b/packages/rainbowkit/src/components/ConnectButton/ConnectButton.tsx index caba8beff8..b4fb53ae94 100644 --- a/packages/rainbowkit/src/components/ConnectButton/ConnectButton.tsx +++ b/packages/rainbowkit/src/components/ConnectButton/ConnectButton.tsx @@ -57,6 +57,22 @@ export function ConnectButton({ if (!ready) setReady(true); }, [showBalance, setShowBalance]); + const computeResponsiveChainStatus = () => { + if (typeof chainStatus === 'string') { + return chainStatus; + } + + if (chainStatus) { + return normalizeResponsiveValue(chainStatus)[ + isMobile() ? 'smallScreen' : 'largeScreen' + ]; + } + }; + + const responsiveChainStatus = computeResponsiveChainStatus(); + + const isChainStatusHidden = responsiveChainStatus === 'none'; + return ready ? ( {({ @@ -67,17 +83,18 @@ export function ConnectButton({ openChainModal, openConnectModal, }) => { - // Hide the chain button if the user has specified a custom "initialChainId". - // This prevents users from switching to a different chain when they have a desired - // chain that they want to switch to. We also hide the chain button if user is using - // our authentication provider and is not yet signed in. - const shouldHideChainButton = + const isChainSwitchingDisabled = ((initialChainId || !chainModalOnConnect) && connectionStatus !== 'connected') || connectionStatus === 'unauthenticated'; + const ready = mounted && connectionStatus !== 'loading'; const unsupportedChain = chain?.unsupported ?? false; + const hasMultipleChains = chains.length > 1; + const canShowCurrentChain = + chain && (!isChainStatusHidden || hasMultipleChains); + return ( - {ready && !shouldHideChainButton && chain && chains.length > 1 && ( + {ready && !isChainSwitchingDisabled && canShowCurrentChain && ( Date: Tue, 9 Apr 2024 01:50:20 +0100 Subject: [PATCH 8/8] chore: tweak changeset --- .changeset/sweet-plants-joke.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.changeset/sweet-plants-joke.md b/.changeset/sweet-plants-joke.md index 8187c527c9..7bc5ca7b7a 100644 --- a/.changeset/sweet-plants-joke.md +++ b/.changeset/sweet-plants-joke.md @@ -8,6 +8,8 @@ You just need to set `chainModalOnConnect` prop to `true` in `` if you'd like to use the chain modal without having your wallet connected. +With this update, the `` will display the chains even when only one chain is configured. You can disable this behaviour by setting `chainStatus` to `none`. + **Example usage** ```ts