-
Notifications
You must be signed in to change notification settings - Fork 38
Feature/move icons to own hook #2308
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
d1df3a8
7e8e393
f9226f4
243346e
5158449
2ca75c5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,9 @@ import { | |
| import { NetworkDetails } from "@shared/constants/stellar"; | ||
| import { getCanonicalFromAsset } from "@shared/helpers/stellar"; | ||
|
|
||
| import { sendMessageToBackground } from "./extensionMessaging"; | ||
| import { SERVICE_TYPES } from "../../constants/services"; | ||
|
|
||
| export const getIconFromTokenLists = async ({ | ||
| issuerId, | ||
| contractId, | ||
|
|
@@ -36,6 +39,7 @@ export const getIconFromTokenLists = async ({ | |
| issuerId && | ||
| record.issuer && | ||
| record.issuer === issuerId && | ||
| record.code === code && | ||
| record.icon | ||
| ) { | ||
| verifiedToken = record; | ||
|
|
@@ -46,6 +50,15 @@ export const getIconFromTokenLists = async ({ | |
| } | ||
| } | ||
|
|
||
| if (verifiedToken?.icon) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if we find an icon, let's cache it in the background so in the future, we don't need to consult the token list at all; we'll already have the url in our background |
||
| await sendMessageToBackground({ | ||
| activePublicKey: null, | ||
| assetCanonical: `${code}:${contractId || issuerId}`, | ||
| iconUrl: verifiedToken?.icon, | ||
| type: SERVICE_TYPES.CACHE_ASSET_ICON, | ||
| }); | ||
| } | ||
|
|
||
| return { | ||
| icon: verifiedToken?.icon, | ||
| canonicalAsset, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -84,7 +84,8 @@ export const getIconUrlFromIssuer = async ({ | |
|
|
||
| if (toml.CURRENCIES) { | ||
| /* If we find some currencies listed, check to see if they have the currency we're looking for listed */ | ||
| toml.CURRENCIES.every(async ({ code: currencyCode, issuer, image }) => { | ||
| for (const currency of toml.CURRENCIES) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. another small bug I found while testing - Array.every doesn't properly await promises. Changing this to a for loop
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should this break out of the loop once the icon has been found? |
||
| const { code: currencyCode, issuer, image } = currency; | ||
| if (currencyCode === code && issuer === key && image) { | ||
| /* We found the currency listing in the toml. 3. Get the image url from it */ | ||
| iconUrl = image; | ||
|
|
@@ -95,11 +96,10 @@ export const getIconUrlFromIssuer = async ({ | |
| iconUrl, | ||
| type: SERVICE_TYPES.CACHE_ASSET_ICON, | ||
| }); | ||
| return false; | ||
| } | ||
| return true; | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| /* Return the icon url to the UI, if we found it */ | ||
| return iconUrl; | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -948,7 +948,10 @@ export const getAccountBalances = async ( | |
| isMainnet, | ||
| }); | ||
| } | ||
| return await getAccountIndexerBalances({ publicKey, networkDetails }); | ||
| return await getAccountIndexerBalances({ | ||
| publicKey, | ||
| networkDetails, | ||
| }); | ||
| }; | ||
|
|
||
| export const getTokenDetails = async ({ | ||
|
|
@@ -1034,24 +1037,54 @@ export const getTokenDetails = async ({ | |
| } | ||
| }; | ||
|
|
||
| export const getAssetIconCache = async ({ | ||
| activePublicKey, | ||
| }: { | ||
| activePublicKey: string | null; | ||
| }) => { | ||
| let icons = {}; | ||
| try { | ||
| ({ icons } = await sendMessageToBackground({ | ||
| activePublicKey, | ||
| type: SERVICE_TYPES.GET_CACHED_ASSET_ICON_LIST, | ||
| })); | ||
| } catch (e) { | ||
| return { icons }; | ||
| } | ||
| return { icons }; | ||
| }; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm adding a method to grab the entire cached icon list. Previously, we were consulting this list one asset at a time |
||
|
|
||
| /* | ||
| getAssetIcons is used to get the icons for the assets in the balances. | ||
| It can be used with or without lookup. Lookup is keyed off of passing assetListsData and networkDetails. | ||
|
|
||
| Using this method without lookup is fastest as it just consults the cache and then returns the icons. | ||
| This is useful for quickly getting the icons without extra network calls. | ||
|
|
||
| Using this method with lookup is slower as it needs to fetch the token lists and the issuer. | ||
| This is useful for getting the icons for the first time. Once we've retrieved an icon, | ||
| we'll store it in the background's storage and then use this cache for future lookups. | ||
| */ | ||
|
|
||
| export const getAssetIcons = async ({ | ||
| balances, | ||
| networkDetails, | ||
| assetsListsData, | ||
| cachedIcons, | ||
| }: { | ||
| balances: Balances; | ||
| networkDetails: NetworkDetails; | ||
| assetsListsData: AssetListResponse[]; | ||
| cachedIcons: Record<string, string>; | ||
| networkDetails?: NetworkDetails; | ||
| assetsListsData?: AssetListResponse[]; | ||
| cachedIcons: Record<string, string | null>; | ||
| }) => { | ||
| const assetIcons = {} as { [code: string]: string }; | ||
| const assetIcons = {} as { [code: string]: string | null }; | ||
| const skipLookup = !assetsListsData || !networkDetails; | ||
|
|
||
| if (balances) { | ||
| let icon = ""; | ||
| const balanceValues = Object.values(balances); | ||
|
|
||
| for (let i = 0; i < balanceValues.length; i++) { | ||
| let icon = ""; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. another small bugfix. This just happened to work, but this |
||
| const { token, contractId } = balanceValues[i]; | ||
| if (token && "issuer" in token) { | ||
| const { | ||
|
|
@@ -1066,8 +1099,18 @@ export const getAssetIcons = async ({ | |
| continue; | ||
| } | ||
|
|
||
| icon = await getIconUrlFromIssuer({ key, code, networkDetails }); | ||
| if (cachedIcon === null) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So in the case that an image URL didn't resolve temporarily for whatever reasons(network or something), then this cache will prevent the icon from ever being used in the future? Maybe this cache should have a ttl?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, so something important here (that I can document better) is this |
||
| // if we've tried to fetch this icon before and it wasn't found, we marked it as null | ||
| // don't bother trying to fetch it again | ||
| continue; | ||
| } | ||
|
|
||
| if (skipLookup) { | ||
| continue; | ||
| } | ||
|
|
||
| if (!icon) { | ||
| // if we don't have the icon, we try to get it from the token lists | ||
| const tokenListIcon = await getIconFromTokenLists({ | ||
| networkDetails, | ||
| issuerId: key, | ||
|
|
@@ -1078,9 +1121,14 @@ export const getAssetIcons = async ({ | |
| if (tokenListIcon.icon && tokenListIcon.canonicalAsset) { | ||
| icon = tokenListIcon.icon; | ||
| canonical = tokenListIcon.canonicalAsset; | ||
| } else { | ||
| // if we still don't have the icon, we try to get it from the issuer | ||
| icon = await getIconUrlFromIssuer({ key, code, networkDetails }); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. small adjustment here - look up the icon issuer last because fetching from the fetched token list should be faster. Let's save the most expensive call for last and only if needed |
||
| } | ||
| } | ||
| assetIcons[canonical] = icon; | ||
|
|
||
| // we assign null here if we checked all sources and still don't have the icon | ||
| assetIcons[canonical] = icon || null; | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -1096,7 +1144,7 @@ export const retryAssetIcon = async ({ | |
| }: { | ||
| key: string; | ||
| code: string; | ||
| assetIcons: { [code: string]: string }; | ||
| assetIcons: { [code: string]: string | null }; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there's a lot of these type updates in this PR - this just allows us to skip an unnecessary asset issuer lookup if previously in the user's session we weren't able to find the icon |
||
| networkDetails: NetworkDetails; | ||
| activePublicKey: string | null; | ||
| }) => { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import { DataStorageAccess } from "background/helpers/dataStorageAccess"; | ||
| import { CACHED_ASSET_ICONS_ID } from "constants/localStorageTypes"; | ||
|
|
||
| export const getCachedAssetIconList = async ({ | ||
| localStore, | ||
| }: { | ||
| localStore: DataStorageAccess; | ||
| }) => { | ||
| const assetIconCache = | ||
| (await localStore.getItem(CACHED_ASSET_ICONS_ID)) || {}; | ||
|
|
||
| return { | ||
| icons: assetIconCache, | ||
| }; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ import { | |
| getAccountBalances, | ||
| getAssetIcons, | ||
| getHiddenAssets, | ||
| getAssetIconCache, | ||
| } from "@shared/api/internal"; | ||
| import { NetworkDetails } from "@shared/constants/stellar"; | ||
| import { AssetIcons } from "@shared/api/types"; | ||
|
|
@@ -99,6 +100,14 @@ function useGetBalances(options: { | |
| } as AccountBalances; | ||
|
|
||
| if (options.includeIcons) { | ||
| let cachedIconsFromCache = cachedIcons; | ||
| if (!Object.keys(cachedIcons).length) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if we haven't hydrated redux yet (first load), use background storage's asset cache as the cache |
||
| const backgroundCachedIcons = await getAssetIconCache({ | ||
| activePublicKey: publicKey, | ||
| }); | ||
|
|
||
| cachedIconsFromCache = { ...backgroundCachedIcons.icons }; | ||
| } | ||
| const assetsListsData = | ||
| useCache && cachedTokenLists.length | ||
| ? cachedTokenLists | ||
|
|
@@ -111,7 +120,7 @@ function useGetBalances(options: { | |
| balances: accountBalances.balances, | ||
| networkDetails, | ||
| assetsListsData, | ||
| cachedIcons, | ||
| cachedIcons: cachedIconsFromCache, | ||
| }); | ||
| payload.icons = icons; | ||
| reduxDispatch(saveTokenLists(assetsListsData)); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a small bug I found while testing - we need to check the asset issuer AND asset code because one G address could be the issuer for multiple assets
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wdyt about adding a test that asserts that two assets from the same issuer get the correct icon?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's actually how I found this bug :) The test is right here: https://github.com/stellar/freighter/pull/2308/files#diff-9943e9b6f3ea7a02af1d671f5ea2a1b9381361230431f521219ea5f36702961cR324
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah nice, ty