From 0f4b309cd3dee83ae5a158399d5018dd1eb94407 Mon Sep 17 00:00:00 2001 From: Kevin Rodriguez <_@kevinrodriguez.io> Date: Wed, 29 Sep 2021 21:42:02 -0600 Subject: [PATCH 1/2] feat: added connection switch --- .../core/react/src/ConnectionProvider.tsx | 19 ++++++++++++++----- packages/core/react/src/useConnection.tsx | 6 ++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/packages/core/react/src/ConnectionProvider.tsx b/packages/core/react/src/ConnectionProvider.tsx index 71623c145..4db168e0e 100644 --- a/packages/core/react/src/ConnectionProvider.tsx +++ b/packages/core/react/src/ConnectionProvider.tsx @@ -1,6 +1,6 @@ import { Connection, ConnectionConfig } from '@solana/web3.js'; -import React, { FC, ReactNode, useMemo } from 'react'; -import { ConnectionContext } from './useConnection'; +import React, { FC, ReactNode, useEffect, useMemo, useReducer } from 'react'; +import { ConnectionContext, ConnectionDispatchContext } from './useConnection'; export interface ConnectionProviderProps { children: ReactNode; @@ -13,7 +13,16 @@ export const ConnectionProvider: FC = ({ endpoint, config = { commitment: 'confirmed' }, }) => { - const connection = useMemo(() => new Connection(endpoint, config), [endpoint, config]); - - return {children}; + const [connection, setConnection] = useReducer( + (_: Connection, newConnection: Connection) => newConnection, + new Connection(endpoint, config) + ); + useEffect(() => { + setConnection(new Connection(endpoint, config)); + }, [endpoint, config]); + return ( + + {children} + + ); }; diff --git a/packages/core/react/src/useConnection.tsx b/packages/core/react/src/useConnection.tsx index 55175260d..9d584e9b2 100644 --- a/packages/core/react/src/useConnection.tsx +++ b/packages/core/react/src/useConnection.tsx @@ -6,7 +6,13 @@ export interface ConnectionContextState { } export const ConnectionContext = createContext({} as ConnectionContextState); +export const ConnectionDispatchContext = createContext>({} as React.Dispatch); export function useConnection(): ConnectionContextState { return useContext(ConnectionContext); } + +export function useSetConnection(): (connection: Connection) => void { + const dispatch = useContext(ConnectionDispatchContext); + return (connection: Connection) => dispatch(connection); +} From e03f5da09bc607733a82fe817b1c3e31849ab56d Mon Sep 17 00:00:00 2001 From: Kevin Rodriguez <_@kevinrodriguez.io> Date: Thu, 30 Sep 2021 01:15:13 -0600 Subject: [PATCH 2/2] feat: added connection switcher --- packages/starter/example/pages/index.tsx | 208 ++++++++++-------- .../ui/ant-design/src/ConnectionMenuItem.tsx | 19 ++ .../ui/ant-design/src/ConnectionModal.tsx | 67 ++++++ .../src/ConnectionModalProvider.tsx | 14 ++ .../ui/ant-design/src/WalletMultiButton.tsx | 9 + packages/ui/ant-design/src/index.ts | 3 + .../ui/ant-design/src/useConnectionModal.ts | 12 + packages/ui/ant-design/styles.css | 7 + 8 files changed, 245 insertions(+), 94 deletions(-) create mode 100644 packages/ui/ant-design/src/ConnectionMenuItem.tsx create mode 100644 packages/ui/ant-design/src/ConnectionModal.tsx create mode 100644 packages/ui/ant-design/src/ConnectionModalProvider.tsx create mode 100644 packages/ui/ant-design/src/useConnectionModal.ts diff --git a/packages/starter/example/pages/index.tsx b/packages/starter/example/pages/index.tsx index 511201dae..09e696cf4 100644 --- a/packages/starter/example/pages/index.tsx +++ b/packages/starter/example/pages/index.tsx @@ -5,6 +5,7 @@ import { WalletModalButton as AntDesignWalletModalButton, WalletModalProvider as AntDesignWalletModalProvider, WalletMultiButton as AntDesignWalletMultiButton, + ConnectionModalProvider as AntDesignConnectionModalProvider, } from '@solana/wallet-adapter-ant-design'; import { WalletConnectButton as MaterialUIWalletConnectButton, @@ -26,6 +27,8 @@ import { useAutoConnect } from '../components/AutoConnectProvider'; import RequestAirdrop from '../components/RequestAirdrop'; import SendTransaction from '../components/SendTransaction'; import SignMessage from '../components/SignMessage'; +import { clusterApiUrl } from '@solana/web3.js'; +import { WalletAdapterNetwork } from '@solana/wallet-adapter-base'; const Index: NextPage = () => { const { autoConnect, setAutoConnect } = useAutoConnect(); @@ -33,100 +36,117 @@ const Index: NextPage = () => { return ( - - - - - Component - Material UI - Ant Design - React UI - Example v{pkg.version} - - - - - Connect Button - - - - - - - - - - - - - Disconnect Button - - - - - - - - - - - - - Dialog/Modal Button - - - - - - - - - - - - - Multi Button - - - - - - - - - - - - - - - - setAutoConnect(checked)} - /> - } - label="AutoConnect" - /> - - - - - - - - - - - - - -
-
+ + + + + + Component + Material UI + Ant Design + React UI + Example v{pkg.version} + + + + + Connect Button + + + + + + + + + + + + + Disconnect Button + + + + + + + + + + + + + Dialog/Modal Button + + + + + + + + + + + + + Multi Button + + + + + + + + + + + + + + + + setAutoConnect(checked)} + /> + } + label="AutoConnect" + /> + + + + + + + + + + + + + +
+
+
); diff --git a/packages/ui/ant-design/src/ConnectionMenuItem.tsx b/packages/ui/ant-design/src/ConnectionMenuItem.tsx new file mode 100644 index 000000000..72e931631 --- /dev/null +++ b/packages/ui/ant-design/src/ConnectionMenuItem.tsx @@ -0,0 +1,19 @@ +import { Button, Menu, MenuItemProps } from 'antd'; +import React, { FC, MouseEventHandler } from 'react'; + +interface ConnectionMenuItemProps extends Omit { + onClick: MouseEventHandler; + connection: { + name: string; + }; +} + +export const ConnectionMenuItem: FC = ({ onClick, connection, ...props }) => { + return ( + + + + ); +}; diff --git a/packages/ui/ant-design/src/ConnectionModal.tsx b/packages/ui/ant-design/src/ConnectionModal.tsx new file mode 100644 index 000000000..fdf12059d --- /dev/null +++ b/packages/ui/ant-design/src/ConnectionModal.tsx @@ -0,0 +1,67 @@ +import { useSetConnection } from '@solana/wallet-adapter-react'; +import { Menu, Modal, ModalProps } from 'antd'; +import React, { FC, MouseEvent, useCallback, useMemo, useState } from 'react'; +import { useConnectionModal } from './useConnectionModal'; +import { ConnectionMenuItem } from './ConnectionMenuItem'; +import { Connection } from '@solana/web3.js'; + +export interface ConnectionModalProps extends Omit { + connections: { + name: string; + endpoint: string; + }[]; +} + +export const ConnectionModal: FC = ({ title = 'Select connection', onCancel, ...props }) => { + const { visible, setVisible } = useConnectionModal(); + const setConnection = useSetConnection(); + const [expanded, setExpanded] = useState(false); + + const handleCancel = useCallback( + (event: MouseEvent) => { + if (onCancel) onCancel(event); + if (!event.defaultPrevented) setVisible(false); + }, + [onCancel, setVisible] + ); + + const handleConnectionClick = useCallback( + ( + event: MouseEvent, + connection: { + name: string; + endpoint: string; + } + ) => { + // TODO: Add a way to replace commitment. + setConnection(new Connection(connection.endpoint, { commitment: 'confirmed' })); + handleCancel(event); + }, + [handleCancel] + ); + + const onOpenChange = useCallback(() => setExpanded(!expanded), [setExpanded, expanded]); + + return ( + + + {props.connections.map((connection) => ( + handleConnectionClick(event, connection)} + connection={connection} + /> + ))} + + + ); +}; diff --git a/packages/ui/ant-design/src/ConnectionModalProvider.tsx b/packages/ui/ant-design/src/ConnectionModalProvider.tsx new file mode 100644 index 000000000..231d5dbb4 --- /dev/null +++ b/packages/ui/ant-design/src/ConnectionModalProvider.tsx @@ -0,0 +1,14 @@ +import React, { FC, useState } from 'react'; +import { ConnectionModalContext } from './useConnectionModal'; +import { ConnectionModal, ConnectionModalProps } from './ConnectionModal'; + +export const ConnectionModalProvider: FC = ({ children, ...props }) => { + const [visible, setVisible] = useState(false); + + return ( + + {children} + + + ); +}; diff --git a/packages/ui/ant-design/src/WalletMultiButton.tsx b/packages/ui/ant-design/src/WalletMultiButton.tsx index 03cf47db2..07753bcfd 100644 --- a/packages/ui/ant-design/src/WalletMultiButton.tsx +++ b/packages/ui/ant-design/src/WalletMultiButton.tsx @@ -7,6 +7,7 @@ import { useWallet } from '@solana/wallet-adapter-react'; import { Button, ButtonProps, Dropdown, Menu } from 'antd'; import React, { FC, useMemo } from 'react'; import { useWalletModal } from './useWalletModal'; +import { useConnectionModal } from './useConnectionModal'; import { WalletConnectButton } from './WalletConnectButton'; import { WalletIcon } from './WalletIcon'; import { WalletModalButton } from './WalletModalButton'; @@ -14,6 +15,7 @@ import { WalletModalButton } from './WalletModalButton'; export const WalletMultiButton: FC = ({ type = 'primary', size = 'large', children, ...props }) => { const { publicKey, wallet, disconnect } = useWallet(); const { setVisible } = useWalletModal(); + const { setVisible: setConnetionModalVisible } = useConnectionModal(); const base58 = useMemo(() => publicKey?.toBase58(), [publicKey]); const content = useMemo(() => { @@ -69,6 +71,13 @@ export const WalletMultiButton: FC = ({ type = 'primary', size = 'l > Connect a different wallet + setTimeout(() => setConnetionModalVisible(true), 100)} + icon={} + className="wallet-adapter-multi-button-item" + > + Use a different endpoint + { // eslint-disable-next-line @typescript-eslint/no-empty-function diff --git a/packages/ui/ant-design/src/index.ts b/packages/ui/ant-design/src/index.ts index b45a08581..30de2037e 100644 --- a/packages/ui/ant-design/src/index.ts +++ b/packages/ui/ant-design/src/index.ts @@ -1,4 +1,7 @@ export * from './useWalletModal'; +export * from './useConnectionModal'; +export * from './ConnectionModal'; +export * from './ConnectionModalProvider'; export * from './WalletConnectButton'; export * from './WalletModal'; export * from './WalletModalButton'; diff --git a/packages/ui/ant-design/src/useConnectionModal.ts b/packages/ui/ant-design/src/useConnectionModal.ts new file mode 100644 index 000000000..b1b90bfbd --- /dev/null +++ b/packages/ui/ant-design/src/useConnectionModal.ts @@ -0,0 +1,12 @@ +import { createContext, useContext } from 'react'; + +export interface ConnectionModalContextState { + visible: boolean; + setVisible: (open: boolean) => void; +} + +export const ConnectionModalContext = createContext({} as ConnectionModalContextState); + +export function useConnectionModal(): ConnectionModalContextState { + return useContext(ConnectionModalContext); +} diff --git a/packages/ui/ant-design/styles.css b/packages/ui/ant-design/styles.css index 0a4be5b18..1d41daf32 100644 --- a/packages/ui/ant-design/styles.css +++ b/packages/ui/ant-design/styles.css @@ -52,6 +52,13 @@ align-items: center; } +.wallet-adapter-connection-modal-menu-button { + height: 44px; + padding-left: 24px; + display: flex; + align-items: center; +} + .wallet-adapter-modal-menu-button-icon { width: 24px; height: 24px;