From 460205d1265aac4548a2d7ad4fbf5c74fc6507ab Mon Sep 17 00:00:00 2001 From: iamacook Date: Wed, 4 Oct 2023 14:30:59 +0200 Subject: [PATCH] feat: connect to session in URL --- .../walletconnect/HeaderWidget/index.tsx | 4 +- .../walletconnect/SessionManager/index.tsx | 12 ++- .../useWalletConnectSearchParamUri.test.ts | 73 +++++++++++++++++++ .../useWalletConnectSearchParamUri.ts | 27 +++++++ 4 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 src/services/walletconnect/__tests__/useWalletConnectSearchParamUri.test.ts create mode 100644 src/services/walletconnect/useWalletConnectSearchParamUri.ts diff --git a/src/components/walletconnect/HeaderWidget/index.tsx b/src/components/walletconnect/HeaderWidget/index.tsx index 96f26df263..b60fd52a16 100644 --- a/src/components/walletconnect/HeaderWidget/index.tsx +++ b/src/components/walletconnect/HeaderWidget/index.tsx @@ -6,10 +6,12 @@ import useWalletConnectSessions from '@/services/walletconnect/useWalletConnectS import Icon from './Icon' import SessionManager from '../SessionManager' import Popup from '../Popup' +import { useWalletConnectSearchParamUri } from '@/services/walletconnect/useWalletConnectSearchParamUri' const WalletConnectHeaderWidget = () => { const { error } = useContext(WalletConnectContext) const [popupOpen, setPopupOpen] = useState(false) + const [wcUri] = useWalletConnectSearchParamUri() const iconRef = useRef(null) const sessions = useWalletConnectSessions() @@ -31,7 +33,7 @@ const WalletConnectHeaderWidget = () => { - + diff --git a/src/components/walletconnect/SessionManager/index.tsx b/src/components/walletconnect/SessionManager/index.tsx index d0e1881b76..83f37bb5b4 100644 --- a/src/components/walletconnect/SessionManager/index.tsx +++ b/src/components/walletconnect/SessionManager/index.tsx @@ -9,11 +9,13 @@ import ProposalForm from '../ProposalForm' import WcInput from '../WcInput' import ErrorMessage from '@/components/tx/ErrorMessage' import SessionList from '../SessionList' +import { useWalletConnectSearchParamUri } from '@/services/walletconnect/useWalletConnectSearchParamUri' const SessionManager = ({ sessions }: { sessions: SessionTypes.Struct[] }) => { const { safe, safeAddress } = useSafeInfo() const { chainId } = safe const { walletConnect, error: walletConnectError } = useContext(WalletConnectContext) + const [wcUri, setWcUri] = useWalletConnectSearchParamUri() const [proposal, setProposal] = useState() const [error, setError] = useState() @@ -28,8 +30,9 @@ const SessionManager = ({ sessions }: { sessions: SessionTypes.Struct[] }) => { return } + setWcUri(null) setProposal(undefined) - }, [proposal, walletConnect, chainId, safeAddress]) + }, [walletConnect, chainId, safeAddress, proposal, setWcUri]) // On session reject const onReject = useCallback(async () => { @@ -61,6 +64,13 @@ const SessionManager = ({ sessions }: { sessions: SessionTypes.Struct[] }) => { return walletConnect.onSessionPropose(setProposal) }, [walletConnect]) + // Connect to session present in URL + useEffect(() => { + if (!walletConnect || !wcUri) return + + walletConnect.connect(wcUri).catch(setError) + }, [walletConnect, wcUri]) + return ( <> {error && ( diff --git a/src/services/walletconnect/__tests__/useWalletConnectSearchParamUri.test.ts b/src/services/walletconnect/__tests__/useWalletConnectSearchParamUri.test.ts new file mode 100644 index 0000000000..3fc8d34822 --- /dev/null +++ b/src/services/walletconnect/__tests__/useWalletConnectSearchParamUri.test.ts @@ -0,0 +1,73 @@ +import * as router from 'next/router' + +import { renderHook, act } from '@/tests/test-utils' +import { useWalletConnectSearchParamUri } from '../useWalletConnectSearchParamUri' + +describe('useWalletConnectSearchParamUri', () => { + const mockRouter = { + query: {}, + replace: jest.fn(), + } as unknown as router.NextRouter + + beforeEach(() => { + jest.spyOn(router, 'useRouter').mockReturnValue(mockRouter) + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + it('should return null when wc uri search param is not present', () => { + const { result } = renderHook(() => useWalletConnectSearchParamUri()) + const [wcUri] = result.current + + expect(wcUri).toBeNull() + }) + + it('should return the wc uri search param value when present', () => { + mockRouter.query = { wc: 'wc:123' } + + const { result } = renderHook(() => useWalletConnectSearchParamUri()) + const [wcUri] = result.current + + expect(wcUri).toBe('wc:123') + }) + + it('should update the wc uri search param value when setWcUri is called', () => { + const { result } = renderHook(() => useWalletConnectSearchParamUri()) + const [wcUri, setWcUri] = result.current + + expect(wcUri).toBe('wc:123') + + act(() => { + setWcUri('wc:456') + }) + + expect(mockRouter.replace).toHaveBeenCalledWith({ + pathname: mockRouter.pathname, + query: { wc: 'wc:456' }, + }) + + expect(wcUri).toBe('wc:456') + }) + + it('should remove the wc uri search param when setWcUri is called with null', () => { + mockRouter.query = { wc: 'wc:123' } + + const { result } = renderHook(() => useWalletConnectSearchParamUri()) + const [wcUri, setWcUri] = result.current + + expect(wcUri).toBe('wc:123') + + act(() => { + setWcUri(null) + }) + + expect(mockRouter.replace).toHaveBeenCalledWith({ + pathname: mockRouter.pathname, + query: {}, + }) + + expect(wcUri).toBeNull() + }) +}) diff --git a/src/services/walletconnect/useWalletConnectSearchParamUri.ts b/src/services/walletconnect/useWalletConnectSearchParamUri.ts new file mode 100644 index 0000000000..bfae4cc727 --- /dev/null +++ b/src/services/walletconnect/useWalletConnectSearchParamUri.ts @@ -0,0 +1,27 @@ +import { useRouter } from 'next/router' + +const WC_URI_SEARCH_PARAM = 'wc' + +export function useWalletConnectSearchParamUri(): [string | null, (wcUri: string | null) => void] { + const router = useRouter() + + const wcUriQuery = router.query[WC_URI_SEARCH_PARAM] + const wcUri = wcUriQuery ? (Array.isArray(wcUriQuery) ? wcUriQuery[0] : wcUriQuery) : null + + const setWcUri = (wcUri: string | null) => { + const newQuery = { ...router.query } + + if (!wcUri) { + delete newQuery[WC_URI_SEARCH_PARAM] + } else { + newQuery[WC_URI_SEARCH_PARAM] = wcUri + } + + router.replace({ + pathname: router.pathname, + query: newQuery, + }) + } + + return [wcUri, setWcUri] +}