From cd95cf27931aa6ba304ea06f59c1498b6a0e9423 Mon Sep 17 00:00:00 2001 From: Adam Gall Date: Fri, 24 May 2024 14:51:04 -0400 Subject: [PATCH] Consolidate logic in useDAOName, but need two hooks still --- .../ui/menus/SafesMenu/SafeMenuItem.tsx | 10 +- src/hooks/DAO/loaders/useFractalNode.ts | 9 +- src/hooks/DAO/loaders/useLoadDAONode.ts | 11 +- src/hooks/DAO/useDAOName.ts | 108 ------------------ src/hooks/DAO/useGetDAOName.ts | 81 +++++++++++++ src/pages/home/SafeDisplayRow.tsx | 9 +- 6 files changed, 103 insertions(+), 125 deletions(-) delete mode 100644 src/hooks/DAO/useDAOName.ts create mode 100644 src/hooks/DAO/useGetDAOName.ts diff --git a/src/components/ui/menus/SafesMenu/SafeMenuItem.tsx b/src/components/ui/menus/SafesMenu/SafeMenuItem.tsx index 9c133be13f..83280eef25 100644 --- a/src/components/ui/menus/SafesMenu/SafeMenuItem.tsx +++ b/src/components/ui/menus/SafesMenu/SafeMenuItem.tsx @@ -2,8 +2,9 @@ import { Box, Button, MenuItem, Text } from '@chakra-ui/react'; import { Star } from '@phosphor-icons/react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; +import { getAddress } from 'viem'; import { DAO_ROUTES } from '../../../../constants/routes'; -import useDAOName from '../../../../hooks/DAO/useDAOName'; +import { useGetDAOName } from '../../../../hooks/DAO/useGetDAOName'; export interface SafeMenuItemProps { network: string; @@ -13,7 +14,7 @@ export interface SafeMenuItemProps { } export function SafeMenuItem({ network, address }: SafeMenuItemProps) { - const { daoRegistryName } = useDAOName({ address }); + const { daoName } = useGetDAOName({ address: getAddress(address) }); const navigate = useNavigate(); const { t } = useTranslation('dashboard'); @@ -31,7 +32,6 @@ export function SafeMenuItem({ network, address }: SafeMenuItemProps) { h="3rem" onClick={onClickNav} noOfLines={1} - data-testid={'favorites-' + daoRegistryName} display="flex" alignItems="center" justifyContent="flex-start" @@ -42,9 +42,7 @@ export function SafeMenuItem({ network, address }: SafeMenuItemProps) { weight="fill" /> - - {daoRegistryName ?? t('loadingFavorite')} - + {daoName ?? t('loadingFavorite')} ); diff --git a/src/hooks/DAO/loaders/useFractalNode.ts b/src/hooks/DAO/loaders/useFractalNode.ts index 875a897dfe..223c5871c5 100644 --- a/src/hooks/DAO/loaders/useFractalNode.ts +++ b/src/hooks/DAO/loaders/useFractalNode.ts @@ -8,7 +8,7 @@ import { NodeAction } from '../../../providers/App/node/action'; import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; import { Node } from '../../../types'; import { mapChildNodes } from '../../../utils/hierarchy'; -import { useLazyDAOName } from '../useDAOName'; +import { useGetDAONameDeferred } from '../useGetDAOName'; import { useFractalModules } from './useFractalModules'; const ONE_MINUTE = 60 * 1000; @@ -29,7 +29,7 @@ export const useFractalNode = ( const { action } = useFractal(); const safeAPI = useSafeAPI(); - const { getDaoName } = useLazyDAOName(); + const { getDAOName } = useGetDAONameDeferred(); const lookupModules = useFractalModules(); @@ -64,7 +64,10 @@ export const useFractalNode = ( onCompleted: async data => { if (!daoAddress) return; const graphNodeInfo = formatDAOQuery({ data }, getAddress(daoAddress)); - const daoName = await getDaoName(getAddress(daoAddress), graphNodeInfo?.daoName); + const daoName = await getDAOName({ + address: getAddress(daoAddress), + registryName: graphNodeInfo?.daoName, + }); action.dispatch({ type: NodeAction.SET_DAO_INFO, diff --git a/src/hooks/DAO/loaders/useLoadDAONode.ts b/src/hooks/DAO/loaders/useLoadDAONode.ts index 7939e9a803..38deadbd9e 100644 --- a/src/hooks/DAO/loaders/useLoadDAONode.ts +++ b/src/hooks/DAO/loaders/useLoadDAONode.ts @@ -7,12 +7,12 @@ import { useSafeAPI } from '../../../providers/App/hooks/useSafeAPI'; import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider'; import { FractalNode, Node, WithError } from '../../../types'; import { mapChildNodes } from '../../../utils/hierarchy'; -import { useLazyDAOName } from '../useDAOName'; +import { useGetDAONameDeferred } from '../useGetDAOName'; import { useFractalModules } from './useFractalModules'; export const useLoadDAONode = () => { const safeAPI = useSafeAPI(); - const { getDaoName } = useLazyDAOName(); + const { getDAOName } = useGetDAONameDeferred(); const lookupModules = useFractalModules(); const { subgraph } = useNetworkConfig(); const [getDAOInfo] = useLazyQuery(DAOQueryDocument, { @@ -63,7 +63,10 @@ export const useLoadDAONode = () => { const safeInfoWithGuard = await safeAPI.getSafeData(sanitizedDaoAddress); const node: FractalNode = Object.assign(graphNodeInfo, { - daoName: await getDaoName(sanitizedDaoAddress, graphNodeInfo.daoName), + daoName: await getDAOName({ + address: sanitizedDaoAddress, + registryName: graphNodeInfo.daoName, + }), safe: safeInfoWithGuard, fractalModules: await lookupModules(safeInfoWithGuard.modules), }); @@ -81,7 +84,7 @@ export const useLoadDAONode = () => { return { error: 'errorFailedSearch' }; } }, - [safeAPI, lookupModules, formatDAOQuery, getDAOInfo, getDaoName], + [formatDAOQuery, getDAOInfo, lookupModules, safeAPI, getDAOName], ); return { loadDao }; diff --git a/src/hooks/DAO/useDAOName.ts b/src/hooks/DAO/useDAOName.ts deleted file mode 100644 index e24f14fb63..0000000000 --- a/src/hooks/DAO/useDAOName.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { FractalRegistry } from '@fractal-framework/fractal-contracts'; -import { useCallback, useEffect, useState } from 'react'; -import { Address } from 'viem'; -import { useEnsName } from 'wagmi'; -import { getEventRPC } from '../../helpers'; -import { useFractal } from '../../providers/App/AppProvider'; -import { useEthersProvider } from '../../providers/Ethers/hooks/useEthersProvider'; -import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; -import { createAccountSubstring } from '../utils/useDisplayName'; - -/** - * Gets the 'display name' for a DAO, in the following order of preference: - * - * 1. Primary ENS Name (reverse record) - * 2. Fractal name registry name - * 3. Truncated Eth address in the form 0xbFC4...7551 - */ -export default function useDAOName({ - address, - registryName, -}: { - address?: string; - registryName?: string | null; -}) { - const { baseContracts } = useFractal(); - const [daoRegistryName, setDAORegistryName] = useState(''); - const { chain } = useNetworkConfig(); - - const { data: ensName } = useEnsName({ - address: address as Address, - chainId: chain.id, - }); - - const getDaoName = useCallback(async () => { - if (!address || !baseContracts) { - setDAORegistryName(''); - return; - } - - if (ensName) { - setDAORegistryName(ensName); - return; - } - - const { fractalRegistryContract } = baseContracts; - if (!fractalRegistryContract) { - setDAORegistryName(createAccountSubstring(address)); - return; - } - if (registryName) { - // Aka supplied from Subgraph - setDAORegistryName(registryName); - } else { - const rpc = getEventRPC(fractalRegistryContract); - const events = await rpc.queryFilter(rpc.filters.FractalNameUpdated(address)); - - const latestEvent = events.pop(); - if (!latestEvent) { - setDAORegistryName(createAccountSubstring(address)); - return; - } - - const { daoName } = latestEvent.args; - setDAORegistryName(daoName); - } - }, [address, ensName, baseContracts, registryName]); - - useEffect(() => { - (async () => { - await getDaoName(); - })(); - }, [getDaoName]); - - return { daoRegistryName }; -} - -/** - * Gets the 'display name' for a DAO, in the following order of preference: - * - * 1. Primary ENS Name (reverse record) - * 2. Fractal name registry name - * 3. Truncated Eth address in the form 0xbFC4...7551 - * - * @dev this is used on initial load of the DAO Node, after subGraph data is loaded - */ -export function useLazyDAOName() { - const provider = useEthersProvider(); - const getDaoName = useCallback( - async (_address: string, _registryName?: string | null): Promise => { - if (provider) { - // check if ens name resolves - const ensName = await provider.lookupAddress(_address).catch(() => null); - if (ensName) { - return ensName; - } - } - - if (_registryName) { - return _registryName; - } - - return createAccountSubstring(_address); - }, - [provider], - ); - - return { getDaoName }; -} diff --git a/src/hooks/DAO/useGetDAOName.ts b/src/hooks/DAO/useGetDAOName.ts new file mode 100644 index 0000000000..8fd165fccc --- /dev/null +++ b/src/hooks/DAO/useGetDAOName.ts @@ -0,0 +1,81 @@ +import { FractalRegistry } from '@fractal-framework/fractal-contracts'; +import { useCallback, useEffect, useState } from 'react'; +import { Address, PublicClient } from 'viem'; +import { usePublicClient } from 'wagmi'; +import { getEventRPC } from '../../helpers'; +import { useFractal } from '../../providers/App/AppProvider'; +import { FractalContracts } from '../../types'; +import { createAccountSubstring } from '../utils/useDisplayName'; + +const getDAOName = async ({ + address, + registryName, + publicClient, + baseContracts, +}: { + address: Address; + registryName?: string | null; + publicClient: PublicClient | undefined; + baseContracts: FractalContracts | undefined; +}) => { + if (!publicClient) { + throw new Error('Public client not available'); + } + + const ensName = await publicClient.getEnsName({ address: address }); + if (ensName) { + return ensName; + } + + if (registryName) { + return registryName; + } + + if (!baseContracts) { + throw new Error('Base contracts not set'); + } + + const rpc = getEventRPC(baseContracts.fractalRegistryContract); + const events = await rpc.queryFilter(rpc.filters.FractalNameUpdated(address)); + const latestEvent = events.pop(); + + if (!latestEvent) { + return createAccountSubstring(address); + } + + return latestEvent.args.daoName; +}; + +const useGetDAOName = ({ + address, + registryName, +}: { + address: Address; + registryName?: string | null; +}) => { + const publicClient = usePublicClient(); + const { baseContracts } = useFractal(); + + const [daoName, setDaoName] = useState(); + useEffect(() => { + getDAOName({ address, registryName, publicClient, baseContracts }).then(name => { + setDaoName(name); + }); + }, [address, baseContracts, publicClient, registryName]); + + return { daoName }; +}; + +const useGetDAONameDeferred = () => { + const publicClient = usePublicClient(); + const { baseContracts } = useFractal(); + return { + getDAOName: useCallback( + ({ address, registryName }: { address: Address; registryName?: string | null }) => + getDAOName({ address, registryName, publicClient, baseContracts }), + [baseContracts, publicClient], + ), + }; +}; + +export { useGetDAOName, useGetDAONameDeferred }; diff --git a/src/pages/home/SafeDisplayRow.tsx b/src/pages/home/SafeDisplayRow.tsx index 716395ae28..28656fcfe0 100644 --- a/src/pages/home/SafeDisplayRow.tsx +++ b/src/pages/home/SafeDisplayRow.tsx @@ -1,16 +1,17 @@ import { Flex, Image, Show, Spacer, Text } from '@chakra-ui/react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; +import { getAddress } from 'viem'; import { SafeMenuItemProps } from '../../components/ui/menus/SafesMenu/SafeMenuItem'; import Avatar from '../../components/ui/page/Header/Avatar'; import { DAO_ROUTES } from '../../constants/routes'; -import useDAOName from '../../hooks/DAO/useDAOName'; +import { useGetDAOName } from '../../hooks/DAO/useGetDAOName'; import useAvatar from '../../hooks/utils/useAvatar'; import useDisplayName, { createAccountSubstring } from '../../hooks/utils/useDisplayName'; import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; export function SafeDisplayRow({ address, network, onClick, showAddress }: SafeMenuItemProps) { - const { daoRegistryName } = useDAOName({ address }); + const { daoName } = useGetDAOName({ address: getAddress(address) }); const navigate = useNavigate(); const { t } = useTranslation('dashboard'); @@ -59,10 +60,10 @@ export function SafeDisplayRow({ address, network, onClick, showAddress }: SafeM alignSelf="center" > - {daoRegistryName ?? t('loadingFavorite')} + {daoName ?? t('loadingFavorite')} {showAddress && {createAccountSubstring(address)}}