From c1f0d5f2a695f45f3178fce217ba5c0acf7bfcb4 Mon Sep 17 00:00:00 2001 From: gregs Date: Wed, 7 Aug 2024 02:46:03 -0300 Subject: [PATCH] fix favorites tokens with no icon (#5982) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix favorites default list * wip * 👍 * Update src/resources/favorites.ts * fix format --------- Co-authored-by: Matthew Wall --- src/App.tsx | 9 +- src/migrations/index.ts | 5 +- src/migrations/migrations/migrateFavorites.ts | 54 +++++++++++ .../migrations/migrateFavoritesToV2.ts | 31 ------ src/migrations/types.ts | 3 +- src/resources/favorites.ts | 94 ++++--------------- 6 files changed, 83 insertions(+), 113 deletions(-) create mode 100644 src/migrations/migrations/migrateFavorites.ts delete mode 100644 src/migrations/migrations/migrateFavoritesToV2.ts diff --git a/src/App.tsx b/src/App.tsx index b32cb85b7ea..7eeb21c04b4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -53,6 +53,7 @@ import { RootStackParamList } from './navigation/types'; import { Address } from 'viem'; import { IS_DEV } from './env'; import { checkIdentifierOnLaunch } from './model/backup'; +import { prefetchDefaultFavorites } from './resources/favorites'; if (IS_DEV) { reactNativeDisableYellowBox && LogBox.ignoreAllLogs(); @@ -288,7 +289,13 @@ function Root() { // @ts-expect-error - Property 'children' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes> & Readonly<...>' - + { + prefetchDefaultFavorites(); + }} + > diff --git a/src/migrations/index.ts b/src/migrations/index.ts index d36aff807f7..8abb7f47509 100644 --- a/src/migrations/index.ts +++ b/src/migrations/index.ts @@ -10,7 +10,7 @@ import { prepareDefaultNotificationGroupSettingsState } from '@/migrations/migra import { Migration, MigrationName, MIGRATIONS_DEBUG_CONTEXT, MIGRATIONS_STORAGE_ID } from '@/migrations/types'; import { changeLanguageKeys } from './migrations/changeLanguageKeys'; import { fixHiddenUSDC } from './migrations/fixHiddenUSDC'; -import { migrateFavoritesToV2 } from './migrations/migrateFavoritesToV2'; +import { migrateFavoritesV2, migrateFavoritesV3 } from './migrations/migrateFavorites'; import { migratePersistedQueriesToMMKV } from './migrations/migratePersistedQueriesToMMKV'; import { migratePinnedAndHiddenTokenUniqueIds } from './migrations/migratePinnedAndHiddenTokenUniqueIds'; import { migrateRemotePromoSheetsToZustand } from './migrations/migrateRemotePromoSheetsToZustand'; @@ -42,7 +42,8 @@ const migrations: Migration[] = [ migrateUnlockableAppIconStorage(), migratePersistedQueriesToMMKV(), migrateRemotePromoSheetsToZustand(), - migrateFavoritesToV2(), + migrateFavoritesV2(), + migrateFavoritesV3(), ]; /** diff --git a/src/migrations/migrations/migrateFavorites.ts b/src/migrations/migrations/migrateFavorites.ts new file mode 100644 index 00000000000..46845e56225 --- /dev/null +++ b/src/migrations/migrations/migrateFavorites.ts @@ -0,0 +1,54 @@ +import { AddressOrEth, UniqueId } from '@/__swaps__/types/assets'; +import { getStandardizedUniqueIdWorklet } from '@/__swaps__/utils/swaps'; +import { EthereumAddress, RainbowToken } from '@/entities'; +import { createQueryKey, persistOptions, queryClient } from '@/react-query'; +import { favoritesQueryKey } from '@/resources/favorites'; +import { ethereumUtils } from '@/utils'; +import { persistQueryClientRestore, persistQueryClientSave } from '@tanstack/react-query-persist-client'; +import { Migration, MigrationName } from '../types'; + +const favoritesV1QueryKey = createQueryKey('favorites', {}, { persisterVersion: 1 }); +const favoritesV2QueryKey = createQueryKey('favorites', {}, { persisterVersion: 2 }); + +export function migrateFavoritesV2(): Migration { + return { + name: MigrationName.migrateFavoritesV2, + async migrate() { + await persistQueryClientRestore({ queryClient, persister: persistOptions.persister }); // first restore persisted data + + // v1 used just the address as key, v2 uses uniqueId as key and builds this uniqueId with ChainId instead of Network + const v1Data = queryClient.getQueryData>(favoritesV1QueryKey); + + if (!v1Data) return; + + const migratedFavorites: Record = {}; + for (const favorite of Object.values(v1Data)) { + const uniqueId = getStandardizedUniqueIdWorklet({ + address: favorite.address as AddressOrEth, + chainId: ethereumUtils.getChainIdFromNetwork(favorite.network), + }); + favorite.uniqueId = uniqueId; // v2 unique uses chainId instead of Network + migratedFavorites[uniqueId] = favorite; + } + queryClient.setQueryData(favoritesQueryKey, migratedFavorites); + + await persistQueryClientSave({ queryClient, persister: persistOptions.persister }); + }, + }; +} + +export function migrateFavoritesV3(): Migration { + return { + name: MigrationName.migrateFavoritesV3, + async migrate() { + await persistQueryClientRestore({ queryClient, persister: persistOptions.persister }); // first restore persisted data + + const v2Data = queryClient.getQueryData>(favoritesV2QueryKey); + if (!v2Data) return; + + queryClient.setQueryData(favoritesQueryKey, v2Data, { updatedAt: 0 }); + + await persistQueryClientSave({ queryClient, persister: persistOptions.persister }); + }, + }; +} diff --git a/src/migrations/migrations/migrateFavoritesToV2.ts b/src/migrations/migrations/migrateFavoritesToV2.ts deleted file mode 100644 index f9a50dfd96b..00000000000 --- a/src/migrations/migrations/migrateFavoritesToV2.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { AddressOrEth, UniqueId } from '@/__swaps__/types/assets'; -import { getStandardizedUniqueIdWorklet } from '@/__swaps__/utils/swaps'; -import { EthereumAddress, RainbowToken } from '@/entities'; -import { createQueryKey, queryClient } from '@/react-query'; -import { favoritesQueryKey } from '@/resources/favorites'; -import { ethereumUtils } from '@/utils'; -import { Migration, MigrationName } from '../types'; - -export function migrateFavoritesToV2(): Migration { - return { - name: MigrationName.migrateFavoritesToV2, - async migrate() { - // v1 used just the address as key, v2 uses uniqueId as key and builds this uniqueId with ChainId instead of Network - const favoritesV1QueryKey = createQueryKey('favorites', {}, { persisterVersion: 1 }); - const v1Data = queryClient.getQueryData>(favoritesV1QueryKey); - if (v1Data) { - const migratedFavorites: Record = {}; - for (const favorite of Object.values(v1Data)) { - const uniqueId = getStandardizedUniqueIdWorklet({ - address: favorite.address as AddressOrEth, - chainId: ethereumUtils.getChainIdFromNetwork(favorite.network), - }); - favorite.uniqueId = uniqueId; // v2 unique uses chainId instead of Network - migratedFavorites[uniqueId] = favorite; - } - queryClient.setQueryData(favoritesQueryKey, migratedFavorites); - queryClient.setQueryData(favoritesV1QueryKey, undefined); // clear v1 store data - } - }, - }; -} diff --git a/src/migrations/types.ts b/src/migrations/types.ts index cdd1a940aeb..6fd5aba3819 100644 --- a/src/migrations/types.ts +++ b/src/migrations/types.ts @@ -18,7 +18,8 @@ export enum MigrationName { migrateUnlockableAppIconStorage = 'migration_migrateUnlockableAppIconStorage', migratePersistedQueriesToMMKV = 'migration_migratePersistedQueriesToMMKV', migrateRemotePromoSheetsToZustand = 'migration_migrateRemotePromoSheetsToZustand', - migrateFavoritesToV2 = 'migration_migrateFavoritesToV2', + migrateFavoritesV2 = 'migration_migrateFavoritesV2', + migrateFavoritesV3 = 'migration_migrateFavoritesV3', removeDuplicateRecentSwaps = 'migration_removeDuplicateRecentSwaps', } diff --git a/src/resources/favorites.ts b/src/resources/favorites.ts index 355b9c6c59b..f220740b06c 100644 --- a/src/resources/favorites.ts +++ b/src/resources/favorites.ts @@ -11,80 +11,11 @@ import { useQuery } from '@tanstack/react-query'; import { omit } from 'lodash'; import { externalTokenQueryKey, fetchExternalToken } from './assets/externalAssetsQuery'; -export const favoritesQueryKey = createQueryKey('favorites', {}, { persisterVersion: 2 }); +export const favoritesQueryKey = createQueryKey('favorites', {}, { persisterVersion: 3 }); -const getUniqueId = (address: AddressOrEth, chainId: ChainId) => getStandardizedUniqueIdWorklet({ address, chainId }); +const DEFAULT_FAVORITES = [DAI_ADDRESS, ETH_ADDRESS, SOCKS_ADDRESS, WBTC_ADDRESS]; -const DAI_uniqueId = getUniqueId(DAI_ADDRESS, ChainId.mainnet); -const ETH_uniqueId = getUniqueId(ETH_ADDRESS, ChainId.mainnet); -const SOCKS_uniqueId = getUniqueId(SOCKS_ADDRESS, ChainId.mainnet); -const WBTC_uniqueId = getUniqueId(WBTC_ADDRESS, ChainId.mainnet); - -const DEFAULT: Record = { - [DAI_uniqueId]: { - address: DAI_ADDRESS, - color: '#F0B340', - decimals: 18, - favorite: true, - highLiquidity: true, - isRainbowCurated: true, - isVerified: true, - name: 'Dai', - symbol: 'DAI', - network: Network.mainnet, - uniqueId: DAI_uniqueId, - networks: { - [ChainId.mainnet]: { address: DAI_ADDRESS }, - }, - }, - [ETH_uniqueId]: { - address: ETH_ADDRESS, - color: '#25292E', - decimals: 18, - favorite: true, - highLiquidity: true, - isVerified: true, - name: 'Ethereum', - symbol: 'ETH', - network: Network.mainnet, - uniqueId: ETH_uniqueId, - networks: { - [ChainId.mainnet]: { address: ETH_ADDRESS }, - }, - }, - [SOCKS_uniqueId]: { - address: SOCKS_ADDRESS, - color: '#E15EE5', - decimals: 18, - favorite: true, - highLiquidity: true, - isRainbowCurated: true, - isVerified: true, - name: 'Unisocks', - symbol: 'SOCKS', - network: Network.mainnet, - uniqueId: SOCKS_uniqueId, - networks: { - [ChainId.mainnet]: { address: SOCKS_ADDRESS }, - }, - }, - [WBTC_uniqueId]: { - address: WBTC_ADDRESS, - color: '#FF9900', - decimals: 8, - favorite: true, - highLiquidity: true, - isRainbowCurated: true, - isVerified: true, - name: 'Wrapped Bitcoin', - symbol: 'WBTC', - network: Network.mainnet, - uniqueId: WBTC_uniqueId, - networks: { - [ChainId.mainnet]: { address: WBTC_ADDRESS }, - }, - }, -}; +const getUniqueId = (address: AddressOrEth, chainId: ChainId) => getStandardizedUniqueIdWorklet({ address, chainId }); /** * Returns a map of the given `addresses` to their corresponding `RainbowToken` metadata. @@ -151,7 +82,8 @@ async function fetchMetadata(addresses: string[], chainId = ChainId.mainnet) { * Refreshes the metadata associated with all favorites. */ export async function refreshFavorites() { - const favorites = queryClient.getQueryData>(favoritesQueryKey) ?? DEFAULT; + const favorites = queryClient.getQueryData>(favoritesQueryKey); + if (!favorites) return; const favoritesByNetwork = Object.values(favorites).reduce( (favoritesByChain, token) => { @@ -192,13 +124,20 @@ export async function toggleFavorite(address: string, chainId = ChainId.mainnet) queryClient.setQueryData(favoritesQueryKey, omit(favorites, uniqueId)); } else { const metadata = await fetchMetadata([lowercasedAddress], chainId); - queryClient.setQueryData(favoritesQueryKey, { - ...favorites, - ...metadata, - }); + queryClient.setQueryData(favoritesQueryKey, { ...favorites, ...metadata }); } } +export async function prefetchDefaultFavorites() { + const favorites = queryClient.getQueryData>(favoritesQueryKey); + if (favorites) return; + + const defaultFavorites = await fetchMetadata(DEFAULT_FAVORITES, ChainId.mainnet); + queryClient.setQueryData(favoritesQueryKey, defaultFavorites); + + return defaultFavorites; +} + /** * Returns `favorites`, an array of favorited addresses, as well as `favoritesMetadata`, a map of these * addresses to their corresponding `RainbowToken`. These values are cached in AsyncStorage and is only @@ -213,7 +152,6 @@ export function useFavorites(): { queryFn: refreshFavorites, staleTime: 24 * 60 * 60 * 1000, // 24hrs cacheTime: Infinity, - initialData: DEFAULT, }); const favoritesMetadata = query.data ?? {};