Skip to content
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

Fixes for activity list coin icons and migrating some components to network instead of assetType #5143

Merged
merged 3 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ const MemoizedBalanceCoinRow = React.memo(
<View style={[sx.container]}>
<FastCoinIcon
address={item.address}
assetType={item.type}
network={item.network}
mainnetAddress={item.mainnet_address}
symbol={item.symbol}
theme={theme}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,52 +12,52 @@ import ZoraBadge from '@/assets/badges/zoraBadge.png';
import ZoraBadgeDark from '@/assets/badges/zoraBadgeDark.png';
import BaseBadge from '@/assets/badges/baseBadge.png';
import BaseBadgeDark from '@/assets/badges/baseBadgeDark.png';
import { AssetType } from '@/entities';
import { Network } from '@/networks/types';

interface FastChainBadgeProps {
assetType: AssetType;
network: Network;
theme: any;
}

const AssetIconsByTheme: {
[key in AssetType]?: {
[key in Network]?: {
dark: ImageSourcePropType;
light: ImageSourcePropType;
};
} = {
[AssetType.arbitrum]: {
[Network.arbitrum]: {
dark: ArbitrumBadgeDark,
light: ArbitrumBadge,
},
[AssetType.optimism]: {
[Network.optimism]: {
dark: OptimismBadgeDark,
light: OptimismBadge,
},
[AssetType.polygon]: {
[Network.polygon]: {
dark: PolygonBadgeDark,
light: PolygonBadge,
},
[AssetType.bsc]: {
[Network.bsc]: {
dark: BscBadgeDark,
light: BscBadge,
},
[AssetType.zora]: {
[Network.zora]: {
dark: ZoraBadgeDark,
light: ZoraBadge,
},
[AssetType.base]: {
[Network.base]: {
dark: BaseBadgeDark,
light: BaseBadge,
},
};

export const FastChainBadge = React.memo(function FastChainBadge({
assetType,
network,
theme,
}: FastChainBadgeProps) {
const { isDarkMode } = theme;

const source = AssetIconsByTheme[assetType]?.[isDarkMode ? 'dark' : 'light'];
const source = AssetIconsByTheme[network]?.[isDarkMode ? 'dark' : 'light'];

if (!source) return null;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
import React from 'react';
// @ts-expect-error // no declaration for this yet
import * as CoinIconsImages from 'react-coin-icon/lib/pngs';
import { Image, StyleSheet, View } from 'react-native';
import { FastChainBadge } from './FastCoinBadge';
import { FastFallbackCoinIconImage } from './FastFallbackCoinIconImage';
import ContractInteraction from '@/assets/contractInteraction.png';
import EthIcon from '@/assets/eth-icon.png';
import { AssetType } from '@/entities';
import { useColorForAsset } from '@/hooks';
import { Network } from '@/networks/types';
import { borders, fonts } from '@/styles';
import { ThemeContextProps } from '@/theme';
import {
FallbackIcon as CoinIconTextFallback,
getTokenMetadata,
isETH,
} from '@/utils';
import { FallbackIcon as CoinIconTextFallback, isETH } from '@/utils';

const fallbackTextStyles = {
fontFamily: fonts.family.SFProRounded,
Expand All @@ -29,75 +23,61 @@ const fallbackIconStyle = {
position: 'absolute',
};

function formatSymbol(symbol: string) {
return symbol
? symbol.charAt(0).toUpperCase() + symbol.slice(1).toLowerCase()
: '';
}

/**
* If mainnet asset is available, get the token under /ethereum/ (token) url.
* Otherwise let it use whatever type it has
* @param param0 - optional mainnetAddress, address and optional assetType
* @param param0 - optional mainnetAddress, address and network
* @returns a proper type and address to use for the url
*/
function resolveTypeAndAddress({
mainnetAddress,
function resolveNetworkAndAddress({
address,
assetType,
mainnetAddress,
network,
}: {
mainnetAddress?: string;
address: string;
assetType?: AssetType;
network: Network;
}) {
if (mainnetAddress) {
return {
resolvedAddress: mainnetAddress,
resolvedType: AssetType.token,
resolvedNetwork: Network.mainnet,
};
}

return {
resolvedAddress: address,
resolvedType: assetType,
resolvedNetwork: network,
};
}

export default React.memo(function FastCoinIcon({
address,
mainnetAddress,
network,
symbol,
assetType,
theme,
}: {
address: string;
mainnetAddress?: string;
network: Network;
symbol: string;
assetType?: AssetType;
theme: ThemeContextProps;
}) {
const { colors } = theme;

const { resolvedType, resolvedAddress } = resolveTypeAndAddress({
const { resolvedNetwork, resolvedAddress } = resolveNetworkAndAddress({
address,
assetType,
mainnetAddress,
network,
});

const fallbackIconColor = useColorForAsset({
address: resolvedAddress,
type: resolvedType,
});

const shadowColor = theme.isDarkMode ? colors.shadow : fallbackIconColor;

const eth = isETH(resolvedAddress);

const formattedSymbol = formatSymbol(symbol);

const shouldRenderFallback = !eth;
const shouldRenderLocalCoinIconImage =
!shouldRenderFallback && !!CoinIconsImages[formattedSymbol];
const shouldRenderContract = symbol === 'contract';

return (
Expand All @@ -113,27 +93,12 @@ export default React.memo(function FastCoinIcon({
>
<Image source={EthIcon} style={sx.coinIconFallback} />
</View>
) : shouldRenderLocalCoinIconImage ? (
<View
style={[
sx.coinIconFallback,
sx.reactCoinIconContainer,
sx.withShadow,
{ shadowColor },
]}
>
<Image
resizeMode="contain"
source={CoinIconsImages[formattedSymbol]}
style={sx.reactCoinIconImage}
/>
</View>
) : shouldRenderContract ? (
<Image source={ContractInteraction} style={sx.contract} />
) : (
<FastFallbackCoinIconImage
address={resolvedAddress}
assetType={resolvedType}
network={resolvedNetwork}
shadowColor={shadowColor}
symbol={symbol}
theme={theme}
Expand All @@ -151,7 +116,7 @@ export default React.memo(function FastCoinIcon({
</FastFallbackCoinIconImage>
)}

{assetType && <FastChainBadge assetType={assetType} theme={theme} />}
{network && <FastChainBadge network={network} theme={theme} />}
</View>
);
});
Expand All @@ -177,10 +142,6 @@ const sx = StyleSheet.create({
alignItems: 'center',
justifyContent: 'center',
},
reactCoinIconImage: {
height: '100%',
width: '100%',
},
withShadow: {
elevation: 6,
shadowOffset: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import FastCoinIcon from './FastCoinIcon';
import ContextMenuButton from '@/components/native-context-menu/contextMenu';
import { Text } from '@/design-system';
import { isNativeAsset } from '@/handlers/assets';
import { Network } from '@/helpers';
import { Network } from '@/networks/types';
import { useAccountAsset } from '@/hooks';
import { colors, fonts, fontWithWidth, getFontSize } from '@/styles';
import { deviceUtils, ethereumUtils } from '@/utils';
Expand Down Expand Up @@ -151,7 +151,7 @@ export default React.memo(function FastCurrencySelectionRow({
<View style={sx.rootContainer}>
<FastCoinIcon
address={address || item?.address}
assetType={type ?? item?.type}
network={network}
mainnetAddress={mainnet_address ?? item?.mainnet_address}
symbol={symbol ?? item?.symbol}
theme={theme}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { useCallback } from 'react';
import React, { useCallback, useState } from 'react';
import { StyleSheet, View } from 'react-native';
import { AssetType } from '@/entities';
import { useForceUpdate } from '@/hooks';
import { Network } from '@/networks/types';
import { ImageWithCachedMetadata, ImgixImage } from '@/components/images';
import { ThemeContextProps } from '@/theme';
import { getUrlForTrustIconFallback } from '@/utils';
Expand All @@ -17,56 +16,52 @@ const imagesCache: { [imageUrl: string]: keyof typeof ImageState } = {};
export const FastFallbackCoinIconImage = React.memo(
function FastFallbackCoinIconImage({
address,
assetType,
network,
symbol,
shadowColor,
theme,
children,
}: {
theme: ThemeContextProps;
address: string;
assetType?: AssetType;
network: Network;
symbol: string;
shadowColor: string;
children: () => React.ReactNode;
}) {
const { colors } = theme;
const imageUrl = getUrlForTrustIconFallback(address, assetType)!;
const imageUrl = getUrlForTrustIconFallback(address, network)!;

const key = `${symbol}-${imageUrl}`;

const shouldShowImage = imagesCache[key] !== ImageState.NOT_FOUND;
const isLoaded = imagesCache[key] === ImageState.LOADED;
const [cacheStatus, setCacheStatus] = useState(imagesCache[key]);

// we store data inside the object outside the component
// so we can share it between component instances
// but we still want the component to pick up new changes
const forceUpdate = useForceUpdate();
const shouldShowImage = cacheStatus !== ImageState.NOT_FOUND;
const isLoaded = cacheStatus === ImageState.LOADED;

const onLoad = useCallback(() => {
if (imagesCache[key] === ImageState.LOADED) {
if (isLoaded) {
return;
}

imagesCache[key] = ImageState.LOADED;
forceUpdate();
}, [key, forceUpdate]);
setCacheStatus(ImageState.LOADED);
}, [key, isLoaded]);

const onError = useCallback(
// @ts-expect-error passed to an untyped JS component
err => {
const newError = err?.nativeEvent?.message?.includes('404')
? ImageState.NOT_FOUND
: ImageState.ERROR;

if (imagesCache[key] === newError) {
if (cacheStatus === newError) {
return;
} else {
imagesCache[key] = newError;
}

forceUpdate();
imagesCache[key] = newError;
setCacheStatus(newError);
},
[key, forceUpdate]
[cacheStatus, key]
);

return (
Expand Down
15 changes: 9 additions & 6 deletions src/components/coin-icon/CoinIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { isNil } from 'lodash';
import React, { useMemo } from 'react';
import { View, ViewProps } from 'react-native';
import ContractInteraction from '../../assets/contractInteraction.png';
import { useTheme } from '../../theme/ThemeContext';
import ChainBadge from './ChainBadge';
import { CoinIconFallback } from './CoinIconFallback';
import { AssetTypes } from '@/entities';
import { Network } from '@/networks/types';
import { useColorForAsset } from '@/hooks';
import { ImgixImage } from '@/components/images';
import styled from '@/styled-thing';
import {
getTokenMetadata,
ethereumUtils,
isETH,
magicMemo,
CoinIcon as ReactCoinIcon,
Expand Down Expand Up @@ -59,7 +58,6 @@ const CoinIcon: React.FC<Props> = ({
}) => {
const color = useColorForAsset({
address: mainnet_address || address,
type: mainnet_address ? AssetTypes.token : type,
});
const { colors, isDarkMode } = useTheme();
const forceFallback = !isETH(mainnet_address || address);
Expand All @@ -69,6 +67,12 @@ const CoinIcon: React.FC<Props> = ({

const theme = useTheme();

const network = mainnet_address
? Network.mainnet
: type
? ethereumUtils.getNetworkFromType(type)
: Network.mainnet;

return (
<View>
{isNotContractInteraction ? (
Expand All @@ -85,8 +89,7 @@ const CoinIcon: React.FC<Props> = ({
}
size={size}
symbol={symbol}
type={mainnet_address ? AssetTypes.token : type}
assetType={mainnet_address ? AssetTypes.token : type}
network={network}
theme={theme}
/>
) : (
Expand Down
Loading