diff --git a/src/components/asset-list/RecyclerAssetList2/WrappedCollectiblesHeader.tsx b/src/components/asset-list/RecyclerAssetList2/WrappedCollectiblesHeader.tsx index 1954febef5c..bb233041e82 100644 --- a/src/components/asset-list/RecyclerAssetList2/WrappedCollectiblesHeader.tsx +++ b/src/components/asset-list/RecyclerAssetList2/WrappedCollectiblesHeader.tsx @@ -2,9 +2,8 @@ import React from 'react'; import { Box, Inline, Text } from '@/design-system'; import * as i18n from '@/languages'; import { ListHeaderMenu } from '@/components/list/ListHeaderMenu'; -import { NftCollectionSortCriterion, SortDirection } from '@/graphql/__generated__/arc'; -import { NftSort, useNftSort } from '@/hooks/useNFTsSortBy'; -import { colors } from '@/styles'; +import { NftCollectionSortCriterion } from '@/graphql/__generated__/arc'; +import useNftSort from '@/hooks/useNFTsSortBy'; const TokenFamilyHeaderHeight = 48; @@ -31,7 +30,7 @@ const getMenuItemIcon = (value: NftCollectionSortCriterion) => { }; const CollectiblesHeader = () => { - const { nftSort, nftSortDirection, updateNFTSort } = useNftSort(); + const { nftSort, updateNFTSort } = useNftSort(); return ( { { - return { - icon: { iconType: 'SYSTEM', iconValue: getMenuItemIcon(sortCriterion) }, - ...(nftSort === sortCriterion - ? { - menuTitle: i18n.t(i18n.l.nfts.sort[sortCriterion]), - menuPreferredElementSize: 'small', - menuState: 'on', - menuItems: [ - { - actionKey: `${sortCriterion}|${SortDirection.Asc}`, - actionTitle: i18n.t(i18n.l.nfts.sort.order.asc), - icon: { - iconType: 'SYSTEM', - iconValue: 'arrow.up.circle', - iconTint: nftSortDirection === SortDirection.Asc ? undefined : colors.grey, - }, - }, - { - actionKey: `${sortCriterion}|${SortDirection.Desc}`, - actionTitle: i18n.t(i18n.l.nfts.sort.order.desc), - icon: { - iconType: 'SYSTEM', - iconValue: 'arrow.down.circle', - iconTint: nftSortDirection === SortDirection.Desc ? undefined : colors.grey, - }, - }, - ], - } - : { - actionKey: `${sortCriterion}|${SortDirection.Desc}`, - actionTitle: i18n.t(i18n.l.nfts.sort[sortCriterion]), - menuState: 'off', - }), - }; - })} - selectItem={string => updateNFTSort(string as NftSort)} + selected={nftSort} + menuItems={Object.entries(NftCollectionSortCriterion).map(([key, value]) => ({ + actionKey: value, + actionTitle: i18n.t(i18n.l.nfts.sort[value]), + icon: { iconType: 'SYSTEM', iconValue: getMenuItemIcon(value) }, + menuState: nftSort === key ? 'on' : 'off', + }))} + selectItem={string => updateNFTSort(string as NftCollectionSortCriterion)} icon={getIconForSortType(nftSort)} text={i18n.t(i18n.l.nfts.sort[nftSort])} /> diff --git a/src/components/list/ListHeaderMenu.tsx b/src/components/list/ListHeaderMenu.tsx index 48ad813b2b6..f39df68a365 100644 --- a/src/components/list/ListHeaderMenu.tsx +++ b/src/components/list/ListHeaderMenu.tsx @@ -2,13 +2,18 @@ import React from 'react'; import ContextMenuButton from '@/components/native-context-menu/contextMenu'; import { ButtonPressAnimation } from '@/components/animations'; import { Bleed, Box, Inline, Text, useForegroundColor } from '@/design-system'; -import { NftSort } from '@/hooks/useNFTsSortBy'; import { haptics } from '@/utils'; -import { MenuConfig } from 'react-native-ios-context-menu'; +import { NftCollectionSortCriterion } from '@/graphql/__generated__/arc'; + +type MenuItem = { + actionKey: string; + actionTitle: string; + menuState?: 'on' | 'off'; +}; type ListHeaderMenuProps = { - selected: NftSort; - menuItems: MenuConfig['menuItems']; + selected: NftCollectionSortCriterion; + menuItems: MenuItem[]; selectItem: (item: string) => void; icon: string; text: string; diff --git a/src/components/native-context-menu/contextMenu.js b/src/components/native-context-menu/contextMenu.js new file mode 100644 index 00000000000..fbe320a1a44 --- /dev/null +++ b/src/components/native-context-menu/contextMenu.js @@ -0,0 +1,6 @@ +import React from 'react'; +import { ContextMenuButton } from 'react-native-ios-context-menu'; + +export default function ContextMenu(props) { + return ; +} diff --git a/src/components/native-context-menu/contextMenu.tsx b/src/components/native-context-menu/contextMenu.tsx deleted file mode 100644 index 95a134cb757..00000000000 --- a/src/components/native-context-menu/contextMenu.tsx +++ /dev/null @@ -1,7 +0,0 @@ -/* eslint-disable react/jsx-props-no-spreading */ -import React from 'react'; -import { ContextMenuButton, ContextMenuButtonProps } from 'react-native-ios-context-menu'; - -export default function ContextMenu(props: ContextMenuButtonProps) { - return ; -} diff --git a/src/hooks/useNFTsSortBy.ts b/src/hooks/useNFTsSortBy.ts index a37f69b6a16..345f20132a7 100644 --- a/src/hooks/useNFTsSortBy.ts +++ b/src/hooks/useNFTsSortBy.ts @@ -1,32 +1,31 @@ -import { NftCollectionSortCriterion, SortDirection } from '@/graphql/__generated__/arc'; +import { useCallback } from 'react'; import { MMKV, useMMKVString } from 'react-native-mmkv'; import useAccountSettings from './useAccountSettings'; +import { NftCollectionSortCriterion } from '@/graphql/__generated__/arc'; const mmkv = new MMKV(); const getStorageKey = (accountAddress: string) => `nfts-sort-${accountAddress}`; -const parseNftSort = (s: string | undefined) => { - const [sortBy = NftCollectionSortCriterion.MostRecent, sortDirection = SortDirection.Desc] = (s?.split('|') || []) as [ - sortBy?: NftCollectionSortCriterion, - sortDirection?: SortDirection, - ]; - return { sortBy, sortDirection } as const; -}; - export const getNftSortForAddress = (accountAddress: string) => { - return parseNftSort(mmkv.getString(getStorageKey(accountAddress))); + return mmkv.getString(getStorageKey(accountAddress)) as NftCollectionSortCriterion; }; -export type NftSort = `${NftCollectionSortCriterion}|${SortDirection}`; - -export function useNftSort() { +export default function useNftSort(): { + nftSort: NftCollectionSortCriterion; + updateNFTSort: (sortBy: NftCollectionSortCriterion) => void; +} { const { accountAddress } = useAccountSettings(); - const [nftSortData, setNftSortData] = useMMKVString(getStorageKey(accountAddress)); - const { sortBy, sortDirection } = parseNftSort(nftSortData); + const [nftSort, setNftSort] = useMMKVString(getStorageKey(accountAddress)); + + const updateNFTSort = useCallback( + (sortBy: NftCollectionSortCriterion) => { + setNftSort(sortBy); + }, + [setNftSort] + ); return { - updateNFTSort: (nftSort: NftSort) => setNftSortData(nftSort), - nftSort: sortBy, - nftSortDirection: sortDirection, + updateNFTSort, + nftSort: (nftSort as NftCollectionSortCriterion) || NftCollectionSortCriterion.MostRecent, }; } diff --git a/src/hooks/useRefreshAccountData.ts b/src/hooks/useRefreshAccountData.ts index 67897f81319..f9cb7ab786c 100644 --- a/src/hooks/useRefreshAccountData.ts +++ b/src/hooks/useRefreshAccountData.ts @@ -9,8 +9,9 @@ import { logger, RainbowError } from '@/logger'; import { queryClient } from '@/react-query'; import { userAssetsQueryKey } from '@/resources/assets/UserAssetsQuery'; import { userAssetsQueryKey as swapsUserAssetsQueryKey } from '@/__swaps__/screens/Swap/resources/assets/userAssets'; -import { invalidateAddressNftsQueries } from '@/resources/nfts'; +import { nftsQueryKey } from '@/resources/nfts'; import { positionsQueryKey } from '@/resources/defi/PositionsQuery'; +import useNftSort from './useNFTsSortBy'; import { Address } from 'viem'; import { addysSummaryQueryKey } from '@/resources/summary/summary'; import useWallets from './useWallets'; @@ -21,6 +22,7 @@ export default function useRefreshAccountData() { const { accountAddress, nativeCurrency } = useAccountSettings(); const [isRefreshing, setIsRefreshing] = useState(false); const profilesEnabled = useExperimentalFlag(PROFILES); + const { nftSort } = useNftSort(); const { connectedToHardhat } = useConnectedToHardhatStore(); const { wallets } = useWallets(); @@ -31,7 +33,7 @@ export default function useRefreshAccountData() { ); const fetchAccountData = useCallback(async () => { - invalidateAddressNftsQueries(accountAddress); + queryClient.invalidateQueries(nftsQueryKey({ address: accountAddress, sortBy: nftSort })); queryClient.invalidateQueries(positionsQueryKey({ address: accountAddress as Address, currency: nativeCurrency })); queryClient.invalidateQueries(addysSummaryQueryKey({ addresses: allAddresses, currency: nativeCurrency })); queryClient.invalidateQueries(userAssetsQueryKey({ address: accountAddress, currency: nativeCurrency, connectedToHardhat })); @@ -53,7 +55,7 @@ export default function useRefreshAccountData() { logger.error(new RainbowError(`[useRefreshAccountData]: Error refreshing data: ${error}`)); throw error; } - }, [accountAddress, allAddresses, connectedToHardhat, dispatch, nativeCurrency, profilesEnabled]); + }, [accountAddress, allAddresses, connectedToHardhat, dispatch, nativeCurrency, nftSort, profilesEnabled]); const refresh = useCallback(async () => { if (isRefreshing) return; diff --git a/src/hooks/useWalletSectionsData.ts b/src/hooks/useWalletSectionsData.ts index 88bd12c8be4..72e89e3de55 100644 --- a/src/hooks/useWalletSectionsData.ts +++ b/src/hooks/useWalletSectionsData.ts @@ -4,13 +4,13 @@ import useCoinListEditOptions from './useCoinListEditOptions'; import useCoinListEdited from './useCoinListEdited'; import useHiddenTokens from './useHiddenTokens'; import useIsWalletEthZero from './useIsWalletEthZero'; -import { useNftSort } from './useNFTsSortBy'; import useSendableUniqueTokens from './useSendableUniqueTokens'; import useShowcaseTokens from './useShowcaseTokens'; import useWallets from './useWallets'; import { buildBriefWalletSectionsSelector } from '@/helpers/buildWalletSections'; import { useSortedUserAssets } from '@/resources/assets/useSortedUserAssets'; import { useLegacyNFTs } from '@/resources/nfts'; +import useNftSort from './useNFTsSortBy'; import useWalletsWithBalancesAndNames from './useWalletsWithBalancesAndNames'; export default function useWalletSectionsData({ @@ -22,7 +22,7 @@ export default function useWalletSectionsData({ const { isLoading: isLoadingUserAssets, data: sortedAssets = [] } = useSortedUserAssets(); const isWalletEthZero = useIsWalletEthZero(); - const { nftSort, nftSortDirection } = useNftSort(); + const { nftSort } = useNftSort(); const { accountAddress, language, network, nativeCurrency } = useAccountSettings(); const { sendableUniqueTokens } = useSendableUniqueTokens(); @@ -32,7 +32,6 @@ export default function useWalletSectionsData({ } = useLegacyNFTs({ address: accountAddress, sortBy: nftSort, - sortDirection: nftSortDirection, }); const walletsWithBalancesAndNames = useWalletsWithBalancesAndNames(); diff --git a/src/hooks/useWatchPendingTxs.ts b/src/hooks/useWatchPendingTxs.ts index 26da4995160..20ddd2169fe 100644 --- a/src/hooks/useWatchPendingTxs.ts +++ b/src/hooks/useWatchPendingTxs.ts @@ -1,5 +1,8 @@ +import { useCallback, useMemo } from 'react'; +import useAccountSettings from './useAccountSettings'; +import { RainbowTransaction, MinedTransaction } from '@/entities/transactions/transaction'; +import { userAssetsQueryKey } from '@/resources/assets/UserAssetsQuery'; import { userAssetsQueryKey as swapsUserAssetsQueryKey } from '@/__swaps__/screens/Swap/resources/assets/userAssets'; -import { MinedTransaction, RainbowTransaction } from '@/entities/transactions/transaction'; import { transactionFetchQuery } from '@/resources/transactions/transaction'; import { RainbowError, logger } from '@/logger'; import { getProvider } from '@/handlers/web3'; @@ -7,14 +10,12 @@ import { consolidatedTransactionsQueryKey } from '@/resources/transactions/conso import { RainbowNetworkObjects } from '@/networks'; import { queryClient } from '@/react-query/queryClient'; import { getTransactionFlashbotStatus } from '@/handlers/transactions'; -import { ChainId } from '@/networks/types'; -import { userAssetsQueryKey } from '@/resources/assets/UserAssetsQuery'; -import { invalidateAddressNftsQueries } from '@/resources/nfts'; -import { useNonceStore } from '@/state/nonces'; import { usePendingTransactionsStore } from '@/state/pendingTransactions'; -import { useCallback, useMemo } from 'react'; +import { useNonceStore } from '@/state/nonces'; import { Address } from 'viem'; -import useAccountSettings from './useAccountSettings'; +import { nftsQueryKey } from '@/resources/nfts'; +import { getNftSortForAddress } from './useNFTsSortBy'; +import { ChainId } from '@/networks/types'; import { staleBalancesStore } from '@/state/staleBalances'; import { useConnectedToHardhatStore } from '@/state/connectedToHardhat'; @@ -48,7 +49,7 @@ export const useWatchPendingTransactions = ({ address }: { address: string }) => testnetMode: !!connectedToHardhat, }) ); - invalidateAddressNftsQueries(address); + queryClient.invalidateQueries(nftsQueryKey({ address, sortBy: getNftSortForAddress(address) })); }, [address, connectedToHardhat, nativeCurrency] ); diff --git a/src/languages/en_US.json b/src/languages/en_US.json index 7b76a8919ef..31697624327 100644 --- a/src/languages/en_US.json +++ b/src/languages/en_US.json @@ -1321,11 +1321,7 @@ "MOST_RECENT": "Recent", "most_recent": "Recent", "FLOOR_PRICE": "Floor Price", - "floor_price": "Floor Price", - "order": { - "asc": "Ascending order", - "desc": "Descending order" - } + "floor_price": "Floor Price" }, "empty": "Collectibles", "collect_now": "Collect Now", diff --git a/src/resources/nfts/index.ts b/src/resources/nfts/index.ts index c7c92601830..1730cb71592 100644 --- a/src/resources/nfts/index.ts +++ b/src/resources/nfts/index.ts @@ -1,32 +1,21 @@ import { QueryFunction, useQuery } from '@tanstack/react-query'; -import { QueryConfigWithSelect, createQueryKey, queryClient } from '@/react-query'; +import { QueryConfigWithSelect, createQueryKey } from '@/react-query'; import { fetchSimpleHashNFTListing } from '@/resources/nfts/simplehash'; import { simpleHashNFTToUniqueAsset } from '@/resources/nfts/simplehash/utils'; import { useSelector } from 'react-redux'; import { AppState } from '@/redux/store'; import { UniqueAsset } from '@/entities'; import { arcClient } from '@/graphql'; -import { NftCollectionSortCriterion, SortDirection } from '@/graphql/__generated__/arc'; import { createSelector } from 'reselect'; +import { NftCollectionSortCriterion } from '@/graphql/__generated__/arc'; import { ChainId } from '@/networks/types'; const NFTS_STALE_TIME = 600000; // 10 minutes const NFTS_CACHE_TIME_EXTERNAL = 3600000; // 1 hour const NFTS_CACHE_TIME_INTERNAL = 604800000; // 1 week -export const nftsQueryKey = ({ - address, - sortBy, - sortDirection, -}: { - address: string; - sortBy: NftCollectionSortCriterion; - sortDirection: SortDirection; -}) => createQueryKey('nfts', { address, sortBy, sortDirection }, { persisterVersion: 1 }); - -export const invalidateAddressNftsQueries = (address: string) => { - queryClient.invalidateQueries(createQueryKey('nfts', { address })); -}; +export const nftsQueryKey = ({ address, sortBy }: { address: string; sortBy: NftCollectionSortCriterion }) => + createQueryKey('nfts', { address, sortBy }, { persisterVersion: 4 }); export const nftListingQueryKey = ({ contractAddress, @@ -64,8 +53,8 @@ interface NFTData { type NFTQueryKey = ReturnType; const fetchNFTData: QueryFunction = async ({ queryKey }) => { - const [{ address, sortBy, sortDirection }] = queryKey; - const queryResponse = await arcClient.getNFTs({ walletAddress: address, sortBy, sortDirection }); + const [{ address, sortBy }] = queryKey; + const queryResponse = await arcClient.getNFTs({ walletAddress: address, sortBy }); const nfts = queryResponse?.nftsV2?.map(nft => simpleHashNFTToUniqueAsset(nft, address)); @@ -88,17 +77,15 @@ const FALLBACK_DATA: NFTData = { nfts: [], nftsMap: {} }; export function useLegacyNFTs({ address, sortBy = NftCollectionSortCriterion.MostRecent, - sortDirection = SortDirection.Desc, config, }: { address: string; sortBy?: NftCollectionSortCriterion; - sortDirection?: SortDirection; config?: QueryConfigWithSelect; }) { const isImportedWallet = useSelector((state: AppState) => isImportedWalletSelector(state, address)); - const { data, error, isLoading, isInitialLoading } = useQuery(nftsQueryKey({ address, sortBy, sortDirection }), fetchNFTData, { + const { data, error, isLoading, isInitialLoading } = useQuery(nftsQueryKey({ address, sortBy }), fetchNFTData, { cacheTime: isImportedWallet ? NFTS_CACHE_TIME_INTERNAL : NFTS_CACHE_TIME_EXTERNAL, enabled: !!address, retry: 3,