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

feat(wallet): Display Custom Chain and Token Icons #12003

Merged
merged 1 commit into from
Jan 27, 2022
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
1 change: 1 addition & 0 deletions components/brave_wallet/browser/brave_wallet_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ constexpr webui::LocalizedString kLocalizedStrings[] = {
{"braveWalletWatchListAdvanced", IDS_BRAVE_WALLET_WATCH_LIST_ADVANCED},
{"braveWalletWatchListCoingeckoId",
IDS_BRAVE_WALLET_WATCH_LIST_COINGECKO_ID},
{"braveWalletIconURL", IDS_BRAVE_WALLET_ICON_URL},
{"braveWalletAccountSettingsDisclaimer",
IDS_BRAVE_WALLET_ACCOUNT_SETTINGS_DISCLAIMER},
{"braveWalletAccountSettingsShowKey",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ const EditVisibleAssetsModal = (props: Props) => {
const [tokenContractAddress, setTokenContractAddress] = React.useState<string>('')
const [tokenDecimals, setTokenDecimals] = React.useState<string>('')
const [coingeckoID, setCoingeckoID] = React.useState<string>('')
const [iconURL, setIconURL] = React.useState<string>('')

// If a user removes all of their assets from the userVisibleTokenInfo list,
// there is a check in the async/lib.ts folder that will still return the networks
Expand Down Expand Up @@ -142,6 +143,13 @@ const EditVisibleAssetsModal = (props: Props) => {
setCoingeckoID(event.target.value)
}

const handleIconURLChanged = (event: React.ChangeEvent<HTMLInputElement>) => {
if (hasError) {
setHasError(false)
}
setIconURL(event.target.value)
}

const nativeAsset = {
contractAddress: '',
decimals: selectedNetwork.decimals,
Expand Down Expand Up @@ -210,12 +218,14 @@ const EditVisibleAssetsModal = (props: Props) => {
if (foundTokenInfoByContractAddress.isErc721) {
let token = foundTokenInfoByContractAddress
token.tokenId = tokenID ? toHex(tokenID) : ''
token.logo = iconURL
setIsLoading(true)
onAddCustomAsset(token)
return
}
let foundToken = foundTokenInfoByContractAddress
foundToken.coingeckoId = coingeckoID !== '' ? coingeckoID : foundTokenInfoByContractAddress.coingeckoId
foundToken.logo = iconURL
onAddCustomAsset(foundToken)
} else {
const newToken: BraveWallet.BlockchainToken = {
Expand All @@ -226,7 +236,7 @@ const EditVisibleAssetsModal = (props: Props) => {
name: tokenName,
symbol: tokenSymbol,
tokenId: tokenID ? toHex(tokenID) : '',
logo: '',
logo: iconURL,
visible: true,
coingeckoId: coingeckoID
}
Expand Down Expand Up @@ -319,6 +329,7 @@ const EditVisibleAssetsModal = (props: Props) => {
setTokenDecimals('')
setTokenID('')
setCoingeckoID('')
setIconURL('')
return
}
onFindTokenInfoByContractAddress(tokenContractAddress)
Expand Down Expand Up @@ -402,6 +413,11 @@ const EditVisibleAssetsModal = (props: Props) => {
<SubDivider />
{showAdvancedFields &&
<>
<InputLabel>{getLocale('braveWalletIconURL')}</InputLabel>
<Input
value={iconURL}
onChange={handleIconURLChanged}
/>
<InputLabel>{getLocale('braveWalletWatchListCoingeckoId')}</InputLabel>
<Input
value={coingeckoID}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react'
import { BraveWallet } from '../../../constants/types'
import { IconWrapper, PlaceholderText } from './style'
import { stripERC20TokenImageURL, isRemoteImageURL } from '../../../utils/string-utils'
import { stripERC20TokenImageURL, isRemoteImageURL, isValidIconExtension } from '../../../utils/string-utils'
import { background } from 'ethereum-blockies'
import { ETH } from '../../../options/asset-options'

Expand Down Expand Up @@ -29,15 +29,38 @@ function withPlaceholderIcon (WrappedComponent: React.ComponentType<any>, config
return null
}
const tokenImageURL = stripERC20TokenImageURL(selectedAsset?.logo)
const checkIconURL = selectedAsset?.symbol !== 'ETH' && (tokenImageURL === '' || isRemoteImageURL(tokenImageURL))
const isRemoteURL = isRemoteImageURL(tokenImageURL)
const isDataURL = selectedAsset?.logo.startsWith('chrome://erc-token-images/')
const isStorybook = selectedAsset?.logo.startsWith('static/media/components/brave_wallet_ui/')

const isValidIcon = React.useMemo(() => {
console.log(selectedAsset?.logo)
if (isRemoteURL || isDataURL) {
const url = new URL(selectedAsset?.logo)
return isValidIconExtension(url.pathname)
}
if (isStorybook) {
return true
}
return false
}, [isRemoteURL, isDataURL, tokenImageURL])

const needsPlaceholder = selectedAsset?.symbol !== 'ETH' && (tokenImageURL === '' || !isValidIcon)

const bg = React.useMemo(() => {
if (checkIconURL) {
if (needsPlaceholder) {
return background({ seed: selectedAsset.contractAddress ? selectedAsset?.contractAddress.toLowerCase() : selectedAsset.name })
}
}, [selectedAsset])

if (checkIconURL) {
const remoteImage = React.useMemo(() => {
if (isRemoteURL) {
return `chrome://image?${tokenImageURL}`
}
return ''
}, [tokenImageURL])

if (needsPlaceholder) {
return (
<IconWrapper
panelBackground={bg}
Expand All @@ -57,7 +80,7 @@ function withPlaceholderIcon (WrappedComponent: React.ComponentType<any>, config
marginLeft={marginLeft ?? 0}
marginRight={marginRight ?? 0}
>
<WrappedComponent icon={selectedAsset?.symbol === 'ETH' ? ETH.logo : selectedAsset?.logo} />
<WrappedComponent icon={selectedAsset?.symbol === 'ETH' ? ETH.logo : isRemoteURL ? remoteImage : selectedAsset?.logo} />
</IconWrapper>
)
}
Expand Down
1 change: 1 addition & 0 deletions components/brave_wallet_ui/stories/locale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ provideStrings({
braveWalletWatchListTokenIdError: 'Token ID is required',
braveWalletWatchListAdvanced: 'Advanced',
braveWalletWatchListCoingeckoId: 'Coingecko ID',
braveWalletIconURL: 'Icon URL',

// AmountPresets
braveWalletPreset25: '25%',
Expand Down
24 changes: 23 additions & 1 deletion components/brave_wallet_ui/utils/string-utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isRemoteImageURL } from './string-utils'
import { isRemoteImageURL, isValidIconExtension } from './string-utils'

describe('Checking URL is remote image or not', () => {
test('HTTP URL should return true', () => {
Expand All @@ -21,3 +21,25 @@ describe('Checking URL is remote image or not', () => {
expect(isRemoteImageURL(undefined)).toEqual(undefined)
})
})

describe('Checking URL ends with a valid icon extension', () => {
test('Ends with .png should return true', () => {
expect(isValidIconExtension('http://test.com/test.png')).toEqual(true)
})

test('Ends with .svg should return true', () => {
expect(isValidIconExtension('https://test.com/test.svg')).toEqual(true)
})

test('Ends with .jpg should return true', () => {
expect(isValidIconExtension('https://test.com/test.jpg')).toEqual(true)
})

test('Ends with .jpeg should return true', () => {
expect(isValidIconExtension('https://test.com/test.jpeg')).toEqual(true)
})

test('Ends with .com should return false', () => {
expect(isValidIconExtension('https://test.com/')).toEqual(false)
})
})
3 changes: 3 additions & 0 deletions components/brave_wallet_ui/utils/string-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ export const toProperCase = (value: string) =>

export const isRemoteImageURL = (url?: string) =>
url?.startsWith('http://') || url?.startsWith('https://') || url?.startsWith('data:image/')

export const isValidIconExtension = (url?: string) =>
url?.endsWith('.jpg') || url?.endsWith('.jpeg') || url?.endsWith('.png') || url?.endsWith('.svg')
1 change: 1 addition & 0 deletions components/resources/wallet_strings.grdp
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@
<message name="IDS_BRAVE_WALLET_WATCH_LIST_TOKEN_ID_ERROR" desc="Visible assets modal tokenId error">Token ID is required</message>
<message name="IDS_BRAVE_WALLET_WATCH_LIST_ADVANCED" desc="Visible assets modal advanced fields">Advanced</message>
<message name="IDS_BRAVE_WALLET_WATCH_LIST_COINGECKO_ID" desc="Visible assets modal Coingecko ID label">Coingecko ID</message>
<message name="IDS_BRAVE_WALLET_ICON_URL" desc="Visible assets modal Icon URL label">Icon URL</message>
<message name="IDS_BRAVE_WALLET_ACCOUNT_SETTINGS_DISCLAIMER" desc="Account settings modal warning">WARNING: Never share your recovery phrase. Anyone with this phrase can take your assets forever.</message>
<message name="IDS_BRAVE_WALLET_ACCOUNT_SETTINGS_SHOW_KEY" desc="Account settings modal show key button">Show key</message>
<message name="IDS_BRAVE_WALLET_ACCOUNT_SETTINGS_HIDE_KEY" desc="Account settings modal hide key button">Hide key</message>
Expand Down