From 231ac0c90f6dd4c8c1e650b19d55932e667df6f1 Mon Sep 17 00:00:00 2001 From: iamacook Date: Fri, 20 Oct 2023 12:33:56 +0200 Subject: [PATCH] refactor: create useDeferredListener hook --- .../walletconnect/HeaderWidget/index.tsx | 108 ++++-------------- src/hooks/useDefferedListener.ts | 36 ++++++ .../walletconnect/WalletConnectWallet.ts | 4 +- src/services/walletconnect/utils.ts | 9 +- 4 files changed, 71 insertions(+), 86 deletions(-) create mode 100644 src/hooks/useDefferedListener.ts diff --git a/src/components/walletconnect/HeaderWidget/index.tsx b/src/components/walletconnect/HeaderWidget/index.tsx index c4777681af..1158b03e78 100644 --- a/src/components/walletconnect/HeaderWidget/index.tsx +++ b/src/components/walletconnect/HeaderWidget/index.tsx @@ -1,6 +1,4 @@ -import { useCallback, useContext, useEffect, useRef, useState } from 'react' -import type { Dispatch, SetStateAction } from 'react' -import type { CoreTypes, SessionTypes } from '@walletconnect/types' +import { useCallback, useContext, useEffect, useRef } from 'react' import type { ReactElement } from 'react' import { WalletConnectContext } from '@/services/walletconnect/WalletConnectContext' @@ -12,7 +10,8 @@ import SessionManager from '../SessionManager' import Popup from '../Popup' import { ConnectionBanner } from '../ConnectionBanner' import useSafeInfo from '@/hooks/useSafeInfo' -import { getEip155ChainId, getSupportedEip155ChainIds } from '@/services/walletconnect/utils' +import { isUnsupportedChain } from '@/services/walletconnect/utils' +import { useDeferredListener } from '@/hooks/useDefferedListener' const usePrepopulatedUri = (): [string, () => void] => { const [searchParamWcUri, setSearchParamWcUri] = useWalletConnectSearchParamUri() @@ -30,87 +29,28 @@ const usePrepopulatedUri = (): [string, () => void] => { const BANNER_TIMEOUT = 2_000 -const useSuccessMetadata = ( - onCloseSessionManager: () => void, -): [CoreTypes.Metadata | undefined, Dispatch>] => { +const useSuccessSession = (onCloseSessionManager: () => void) => { const { walletConnect } = useContext(WalletConnectContext) - const [metadata, setMetadata] = useState() - const onSuccess = useCallback( - ({ peer }: SessionTypes.Struct) => { - onCloseSessionManager() - - // Show success banner - setMetadata(peer.metadata) - - return setTimeout(() => { - setMetadata(undefined) - }, BANNER_TIMEOUT) - }, - [onCloseSessionManager], - ) - - useEffect(() => { - if (!walletConnect) return - - let timeout: NodeJS.Timeout - - walletConnect.onSessionAdd((session) => { - timeout = onSuccess(session) - }) - - return () => clearTimeout(timeout) - }, [onSuccess, walletConnect]) - - return [metadata, setMetadata] + return useDeferredListener({ + listener: walletConnect?.onSessionAdd, + cb: onCloseSessionManager, + ms: BANNER_TIMEOUT, + }) } -const useDeleteMetadata = (): [ - CoreTypes.Metadata | undefined, - Dispatch>, -] => { +const useDeleteSession = () => { const { walletConnect } = useContext(WalletConnectContext) - const { safe } = useSafeInfo() - const [metadata, setMetadata] = useState() - - const onDelete = useCallback( - ({ optionalNamespaces, requiredNamespaces, peer }: SessionTypes.Struct) => { - const supportedEip155ChainIds = getSupportedEip155ChainIds(requiredNamespaces, optionalNamespaces) - const eipChainId = getEip155ChainId(safe.chainId) - const isUnsupportedChain = !supportedEip155ChainIds.includes(eipChainId) - - if (!isUnsupportedChain) { - return - } - - // Show success banner - setMetadata(peer.metadata) - - return setTimeout(() => { - setMetadata(undefined) - }, BANNER_TIMEOUT * 2) - }, - [safe.chainId], - ) - - useEffect(() => { - if (!walletConnect) return - - let timeout: NodeJS.Timeout | undefined - - walletConnect.onSessionDelete((session) => { - timeout = onDelete(session) - }) - - return () => clearTimeout(timeout) - }, [onDelete, walletConnect]) - - return [metadata, setMetadata] + return useDeferredListener({ + listener: walletConnect?.onSessionDelete, + ms: BANNER_TIMEOUT * 2, + }) } const WalletConnectHeaderWidget = (): ReactElement => { const { walletConnect, setError, open, setOpen } = useContext(WalletConnectContext) + const { safe } = useSafeInfo() const iconRef = useRef(null) const sessions = useWalletConnectSessions() const [uri, clearUri] = usePrepopulatedUri() @@ -123,15 +63,17 @@ const WalletConnectHeaderWidget = (): ReactElement => { setError(null) }, [setOpen, clearUri, setError]) - const [successMetadata, setSuccessMetadata] = useSuccessMetadata(onCloseSessionManager) - const [deleteMetadata, setDeleteMetadata] = useDeleteMetadata() + const [successSession, setSuccessSession] = useSuccessSession(onCloseSessionManager) + const [deleteSession, setDeleteSession] = useDeleteSession() - const bannerMetadata = successMetadata || deleteMetadata + const session = successSession || deleteSession + const metadata = session?.peer?.metadata + const isUnsupported = deleteSession ? isUnsupportedChain(deleteSession, safe.chainId) : false const onCloseConnectionBanner = useCallback(() => { - setSuccessMetadata(undefined) - setDeleteMetadata(undefined) - }, [setDeleteMetadata, setSuccessMetadata]) + setSuccessSession(undefined) + setDeleteSession(undefined) + }, [setSuccessSession, setDeleteSession]) // Clear search param/clipboard state to prevent it being automatically entered again useEffect(() => { @@ -155,8 +97,8 @@ const WalletConnectHeaderWidget = (): ReactElement => { - - + + ) diff --git a/src/hooks/useDefferedListener.ts b/src/hooks/useDefferedListener.ts new file mode 100644 index 0000000000..388bf99821 --- /dev/null +++ b/src/hooks/useDefferedListener.ts @@ -0,0 +1,36 @@ +import { useState, useEffect } from 'react' +import type { Dispatch, SetStateAction } from 'react' + +export const useDeferredListener = ({ + listener, + cb, + ms, +}: { + listener?: (handler: (e: T) => void) => () => void + cb?: () => void + ms: number +}): [T | undefined, Dispatch>] => { + const [value, setValue] = useState() + + useEffect(() => { + if (!listener) { + return + } + + const unsubscribe = listener((newValue) => { + setValue(newValue) + cb?.() + }) + + const timeout = setTimeout(() => { + setValue(undefined) + }, ms) + + return () => { + unsubscribe() + clearTimeout(timeout) + } + }, [cb, listener, ms]) + + return [value, setValue] +} diff --git a/src/services/walletconnect/WalletConnectWallet.ts b/src/services/walletconnect/WalletConnectWallet.ts index 58ccdbae04..9bcf0b18f3 100644 --- a/src/services/walletconnect/WalletConnectWallet.ts +++ b/src/services/walletconnect/WalletConnectWallet.ts @@ -221,7 +221,7 @@ class WalletConnectWallet { /** * Subscribe to session add */ - public onSessionAdd(handler: (e: SessionTypes.Struct) => void) { + public onSessionAdd = (handler: (e: SessionTypes.Struct) => void) => { // @ts-expect-error - custom event payload this.web3Wallet?.on(SESSION_ADD_EVENT, handler) @@ -234,7 +234,7 @@ class WalletConnectWallet { /** * Subscribe to session delete */ - public onSessionDelete(handler: (session: SessionTypes.Struct) => void) { + public onSessionDelete = (handler: (session: SessionTypes.Struct) => void) => { // @ts-expect-error - custom event payload this.web3Wallet?.on('session_delete', handler) diff --git a/src/services/walletconnect/utils.ts b/src/services/walletconnect/utils.ts index 9c50d5fd65..c7e825eec7 100644 --- a/src/services/walletconnect/utils.ts +++ b/src/services/walletconnect/utils.ts @@ -1,5 +1,5 @@ import type { ChainInfo } from '@safe-global/safe-apps-sdk' -import type { ProposalTypes } from '@walletconnect/types' +import type { ProposalTypes, SessionTypes } from '@walletconnect/types' import { EIP155 } from './constants' @@ -38,3 +38,10 @@ export const getSupportedChainIds = ( }) .map((chain) => chain.chainId) } + +export const isUnsupportedChain = (session: SessionTypes.Struct, chainId: string) => { + const supportedEip155ChainIds = getSupportedEip155ChainIds(session.requiredNamespaces, session.optionalNamespaces) + + const eipChainId = getEip155ChainId(chainId) + return !supportedEip155ChainIds.includes(eipChainId) +}