diff --git a/app/components/UI/NftGrid/NftGrid.tsx b/app/components/UI/NftGrid/NftGrid.tsx index 2a5a6158bb0b..7902e90d2b8e 100644 --- a/app/components/UI/NftGrid/NftGrid.tsx +++ b/app/components/UI/NftGrid/NftGrid.tsx @@ -97,7 +97,12 @@ const NftGrid = forwardRef( useState(null); const tw = useTailwind(); const { colors } = useTheme(); - const { refreshing, onRefresh } = useNftRefresh(); + const { detectNfts, abortDetection, chainIdsToDetectNftsFor } = + useNftDetection(); + const { refreshing, onRefresh } = useNftRefresh({ + detectNfts, + chainIdsToDetectNftsFor, + }); useImperativeHandle(ref, () => ({ refresh: onRefresh, @@ -134,9 +139,6 @@ const NftGrid = forwardRef( )(state, undefined, addressesOverride), ); - const { detectNfts, abortDetection, chainIdsToDetectNftsFor } = - useNftDetection(); - const isInitialMount = useRef(true); const hasTrackedScreenViewRef = useRef(false); const hasSeenNftFetchingRef = useRef(false); diff --git a/app/components/UI/NftGrid/useNftRefresh.test.ts b/app/components/UI/NftGrid/useNftRefresh.test.ts index c53625de266c..1c82f850f11b 100644 --- a/app/components/UI/NftGrid/useNftRefresh.test.ts +++ b/app/components/UI/NftGrid/useNftRefresh.test.ts @@ -1,10 +1,9 @@ import { renderHook, act } from '@testing-library/react-native'; import { useSelector } from 'react-redux'; +import { Hex } from '@metamask/utils'; import { useNftRefresh } from './useNftRefresh'; import Engine from '../../../core/Engine'; -import { useNftDetection } from '../../hooks/useNftDetection'; import { selectEvmNetworkConfigurationsByChainId } from '../../../selectors/networkController'; -import { selectTokenNetworkFilter } from '../../../selectors/preferencesController'; jest.mock('react-redux', () => ({ useSelector: jest.fn((selector) => selector()), @@ -18,10 +17,6 @@ jest.mock('../../../core/Engine', () => ({ }, })); -jest.mock('../../hooks/useNftDetection', () => ({ - useNftDetection: jest.fn(), -})); - jest.mock('../../../selectors/networkController', () => ({ selectEvmNetworkConfigurationsByChainId: jest.fn(() => ({ '0x1': { @@ -35,23 +30,19 @@ jest.mock('../../../selectors/networkController', () => ({ })), })); -jest.mock('../../../selectors/preferencesController', () => ({ - selectTokenNetworkFilter: jest.fn(() => ({ - '0x1': true, - '0x89': true, - })), -})); - describe('useNftRefresh', () => { const mockDetectNfts = jest.fn(); const mockCheckAndUpdateAllNftsOwnershipStatus = jest.fn(); + const defaultChainIds: Hex[] = ['0x1', '0x89']; const mockUseSelector = useSelector as jest.MockedFunction< typeof useSelector >; - const mockUseNftDetection = useNftDetection as jest.MockedFunction< - typeof useNftDetection - >; + + const defaultProps = { + detectNfts: mockDetectNfts, + chainIdsToDetectNftsFor: defaultChainIds, + }; beforeEach(() => { jest.clearAllMocks(); @@ -59,12 +50,6 @@ describe('useNftRefresh', () => { mockDetectNfts.mockResolvedValue(undefined); mockCheckAndUpdateAllNftsOwnershipStatus.mockResolvedValue(undefined); - mockUseNftDetection.mockReturnValue({ - detectNfts: mockDetectNfts, - chainIdsToDetectNftsFor: ['0x1', '0x89'], - abortDetection: jest.fn(), - }); - ( Engine.context.NftController .checkAndUpdateAllNftsOwnershipStatus as jest.Mock @@ -83,18 +68,12 @@ describe('useNftRefresh', () => { }, }; } - if (selector === selectTokenNetworkFilter) { - return { - '0x1': true, - '0x89': true, - }; - } return undefined; }); }); it('returns refreshing and onRefresh', () => { - const { result } = renderHook(() => useNftRefresh()); + const { result } = renderHook(() => useNftRefresh(defaultProps)); expect(result.current.refreshing).toBe(false); expect(result.current.onRefresh).toBeDefined(); @@ -102,7 +81,7 @@ describe('useNftRefresh', () => { }); it('sets refreshing to true during refresh and false after', async () => { - const { result } = renderHook(() => useNftRefresh()); + const { result } = renderHook(() => useNftRefresh(defaultProps)); expect(result.current.refreshing).toBe(false); @@ -113,8 +92,8 @@ describe('useNftRefresh', () => { expect(result.current.refreshing).toBe(false); }); - it('calls useNftDetection.detectNfts on refresh', async () => { - const { result } = renderHook(() => useNftRefresh()); + it('calls detectNfts on refresh', async () => { + const { result } = renderHook(() => useNftRefresh(defaultProps)); await act(async () => { await result.current.onRefresh(); @@ -124,7 +103,7 @@ describe('useNftRefresh', () => { }); it('calls NftController.checkAndUpdateAllNftsOwnershipStatus for each network', async () => { - const { result } = renderHook(() => useNftRefresh()); + const { result } = renderHook(() => useNftRefresh(defaultProps)); await act(async () => { await result.current.onRefresh(); @@ -143,7 +122,7 @@ describe('useNftRefresh', () => { const mockError = new Error('Detection failed'); mockDetectNfts.mockRejectedValueOnce(mockError); - const { result } = renderHook(() => useNftRefresh()); + const { result } = renderHook(() => useNftRefresh(defaultProps)); await act(async () => { await result.current.onRefresh(); @@ -152,15 +131,13 @@ describe('useNftRefresh', () => { expect(result.current.refreshing).toBe(false); }); - it('does not call checkAndUpdateAllNftsOwnershipStatus when no network client IDs', async () => { - mockUseSelector.mockImplementation((selector: unknown) => { - if (selector === selectTokenNetworkFilter) { - return {}; - } - return undefined; - }); - - const { result } = renderHook(() => useNftRefresh()); + it('does not call checkAndUpdateAllNftsOwnershipStatus when chainIdsToDetectNftsFor is empty', async () => { + const { result } = renderHook(() => + useNftRefresh({ + detectNfts: mockDetectNfts, + chainIdsToDetectNftsFor: [], + }), + ); await act(async () => { await result.current.onRefresh(); @@ -180,13 +157,15 @@ describe('useNftRefresh', () => { }, }; } - if (selector === selectTokenNetworkFilter) { - return { '0x1': true }; - } return undefined; }); - const { result } = renderHook(() => useNftRefresh()); + const { result } = renderHook(() => + useNftRefresh({ + detectNfts: mockDetectNfts, + chainIdsToDetectNftsFor: ['0x1'], + }), + ); await act(async () => { await result.current.onRefresh(); @@ -210,7 +189,7 @@ describe('useNftRefresh', () => { callOrder.push('ownership-end'); }); - const { result } = renderHook(() => useNftRefresh()); + const { result } = renderHook(() => useNftRefresh(defaultProps)); await act(async () => { await result.current.onRefresh(); diff --git a/app/components/UI/NftGrid/useNftRefresh.ts b/app/components/UI/NftGrid/useNftRefresh.ts index 076ad459ecb1..4312abc06144 100644 --- a/app/components/UI/NftGrid/useNftRefresh.ts +++ b/app/components/UI/NftGrid/useNftRefresh.ts @@ -1,26 +1,31 @@ import { useCallback, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; +import { Hex } from '@metamask/utils'; import Engine from '../../../core/Engine'; -import { useNftDetection } from '../../hooks/useNftDetection'; -import { selectTokenNetworkFilter } from '../../../selectors/preferencesController'; import { selectEvmNetworkConfigurationsByChainId } from '../../../selectors/networkController'; +interface UseNftRefreshOptions { + detectNfts: (firstPageOnly?: boolean) => Promise; + chainIdsToDetectNftsFor: Hex[]; +} + interface UseNftRefreshReturn { refreshing: boolean; onRefresh: () => Promise; } -export const useNftRefresh = (): UseNftRefreshReturn => { +export const useNftRefresh = ({ + detectNfts, + chainIdsToDetectNftsFor, +}: UseNftRefreshOptions): UseNftRefreshReturn => { const allEVMNetworks = useSelector(selectEvmNetworkConfigurationsByChainId); - const tokenNetworkFilter = useSelector(selectTokenNetworkFilter); - const { detectNfts } = useNftDetection(); const [refreshing, setRefreshing] = useState(false); const allNetworkClientIds = useMemo( () => - Object.keys(tokenNetworkFilter).flatMap((chainId) => { + chainIdsToDetectNftsFor.flatMap((chainId) => { const entry = allEVMNetworks[chainId as `0x${string}`]; if (!entry) { return []; @@ -29,7 +34,7 @@ export const useNftRefresh = (): UseNftRefreshReturn => { const endpoint = entry.rpcEndpoints[index]; return endpoint?.networkClientId ? [endpoint.networkClientId] : []; }), - [tokenNetworkFilter, allEVMNetworks], + [chainIdsToDetectNftsFor, allEVMNetworks], ); const onRefresh = useCallback(async () => { @@ -38,13 +43,11 @@ export const useNftRefresh = (): UseNftRefreshReturn => { setRefreshing(true); try { - // Use useNftDetection.detectNfts which: - // - Checks if NFT detection is enabled in user preferences - // - Dispatches loading indicators - // - Handles analytics tracking + // detectNfts: checks if NFT detection is enabled, dispatches loading + // indicators, and handles analytics tracking const detectNftsPromise = detectNfts(); - // Also update ownership status for all NFTs + // Also update ownership status for all NFTs across all currently enabled networks const ownershipPromises = allNetworkClientIds.map((networkClientId) => NftController.checkAndUpdateAllNftsOwnershipStatus(networkClientId), ); diff --git a/app/components/Views/Homepage/Sections/NFTs/NFTsSection.tsx b/app/components/Views/Homepage/Sections/NFTs/NFTsSection.tsx index 617d80b38ffe..1f0de5571853 100644 --- a/app/components/Views/Homepage/Sections/NFTs/NFTsSection.tsx +++ b/app/components/Views/Homepage/Sections/NFTs/NFTsSection.tsx @@ -70,8 +70,12 @@ const NFTsSection = forwardRef( const ownedNfts = useOwnedNfts(); const hasNfts = ownedNfts.length > 0; const isNftFetchingProgress = useSelector(isNftFetchingProgressSelector); - const { onRefresh } = useNftRefresh(); - const { detectNfts, abortDetection } = useNftDetection(); + const { detectNfts, abortDetection, chainIdsToDetectNftsFor } = + useNftDetection(); + const { onRefresh } = useNftRefresh({ + detectNfts, + chainIdsToDetectNftsFor, + }); const hasLoadedOnceRef = useRef(false); const isSilentDetectionRef = useRef(false); diff --git a/app/core/Engine/messengers/nft-controller-messenger.ts b/app/core/Engine/messengers/nft-controller-messenger.ts index 0c2777f863cc..0ae4717433ac 100644 --- a/app/core/Engine/messengers/nft-controller-messenger.ts +++ b/app/core/Engine/messengers/nft-controller-messenger.ts @@ -33,8 +33,6 @@ export function getNftControllerMessenger( 'AssetsContractController:getERC721AssetName', 'AssetsContractController:getERC721AssetSymbol', 'AssetsContractController:getERC721TokenURI', - 'AssetsContractController:getERC721OwnerOf', - 'AssetsContractController:getERC1155BalanceOf', 'AssetsContractController:getERC1155TokenURI', 'NetworkController:getNetworkClientById', 'NetworkController:findNetworkClientIdByChainId',