Skip to content

Commit

Permalink
fix favorites tokens with no icon (#5982)
Browse files Browse the repository at this point in the history
* fix favorites default list

* wip

* πŸ‘

* Update src/resources/favorites.ts

* fix format

---------

Co-authored-by: Matthew Wall <[email protected]>
  • Loading branch information
greg-schrammel and walmat authored Aug 7, 2024
1 parent eabc40e commit c1f0d5f
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 113 deletions.
9 changes: 8 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -288,7 +289,13 @@ function Root() {
// @ts-expect-error - Property 'children' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<Provider<AppStateUpdateAction | ChartsUpdateAction | ContactsAction | ... 13 more ... | WalletsAction>> & Readonly<...>'
<ReduxProvider store={store}>
<RecoilRoot>
<PersistQueryClientProvider client={queryClient} persistOptions={persistOptions}>
<PersistQueryClientProvider
client={queryClient}
persistOptions={persistOptions}
onSuccess={() => {
prefetchDefaultFavorites();
}}
>
<SafeAreaProvider>
<MainThemeProvider>
<GestureHandlerRootView style={{ flex: 1 }}>
Expand Down
5 changes: 3 additions & 2 deletions src/migrations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -42,7 +42,8 @@ const migrations: Migration[] = [
migrateUnlockableAppIconStorage(),
migratePersistedQueriesToMMKV(),
migrateRemotePromoSheetsToZustand(),
migrateFavoritesToV2(),
migrateFavoritesV2(),
migrateFavoritesV3(),
];

/**
Expand Down
54 changes: 54 additions & 0 deletions src/migrations/migrations/migrateFavorites.ts
Original file line number Diff line number Diff line change
@@ -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<Record<EthereumAddress, RainbowToken>>(favoritesV1QueryKey);

if (!v1Data) return;

const migratedFavorites: Record<UniqueId, RainbowToken> = {};
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<Record<UniqueId, RainbowToken>>(favoritesV2QueryKey);
if (!v2Data) return;

queryClient.setQueryData(favoritesQueryKey, v2Data, { updatedAt: 0 });

await persistQueryClientSave({ queryClient, persister: persistOptions.persister });
},
};
}
31 changes: 0 additions & 31 deletions src/migrations/migrations/migrateFavoritesToV2.ts

This file was deleted.

3 changes: 2 additions & 1 deletion src/migrations/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
}

Expand Down
94 changes: 16 additions & 78 deletions src/resources/favorites.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<UniqueId, RainbowToken> = {
[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.
Expand Down Expand Up @@ -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<Record<UniqueId, RainbowToken>>(favoritesQueryKey) ?? DEFAULT;
const favorites = queryClient.getQueryData<Record<UniqueId, RainbowToken>>(favoritesQueryKey);
if (!favorites) return;

const favoritesByNetwork = Object.values(favorites).reduce(
(favoritesByChain, token) => {
Expand Down Expand Up @@ -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<Record<UniqueId, RainbowToken>>(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
Expand All @@ -213,7 +152,6 @@ export function useFavorites(): {
queryFn: refreshFavorites,
staleTime: 24 * 60 * 60 * 1000, // 24hrs
cacheTime: Infinity,
initialData: DEFAULT,
});

const favoritesMetadata = query.data ?? {};
Expand Down

0 comments on commit c1f0d5f

Please sign in to comment.