From 7def6aaed9787b234c75b0efc189cca7b7e1b167 Mon Sep 17 00:00:00 2001 From: Douglas Daniel Date: Mon, 17 Jan 2022 18:24:46 -0700 Subject: [PATCH] chore(wallet): Created Asset Management Hook --- .../common/actions/wallet_actions.ts | 1 + .../brave_wallet_ui/common/async/handlers.ts | 7 +- .../brave_wallet_ui/common/constants/mocks.ts | 3 +- .../common/hooks/assets-management.test.ts | 113 ++++++++++++++++++ .../common/hooks/assets-management.ts | 88 ++++++++++++++ .../brave_wallet_ui/common/hooks/index.ts | 4 +- .../edit-visible-assets-modal/index.tsx | 113 +++++++++++++----- .../components/desktop/views/crypto/index.tsx | 15 +-- .../desktop/views/portfolio/index.tsx | 15 +-- components/brave_wallet_ui/page/container.tsx | 48 ++++---- .../stories/screens/crypto-story-view.tsx | 15 +-- .../stories/wallet-concept.tsx | 17 +-- 12 files changed, 340 insertions(+), 99 deletions(-) create mode 100644 components/brave_wallet_ui/common/hooks/assets-management.test.ts create mode 100644 components/brave_wallet_ui/common/hooks/assets-management.ts diff --git a/components/brave_wallet_ui/common/actions/wallet_actions.ts b/components/brave_wallet_ui/common/actions/wallet_actions.ts index 2691d47045d7..54472cb7f38e 100644 --- a/components/brave_wallet_ui/common/actions/wallet_actions.ts +++ b/components/brave_wallet_ui/common/actions/wallet_actions.ts @@ -114,3 +114,4 @@ export const cancelTransaction = createAction('canc export const speedupTransaction = createAction('speedupTransaction') export const defaultCurrenciesUpdated = createAction('defaultCurrenciesUpdated') export const expandWalletNetworks = createAction('expandWalletNetworks') +export const refreshBalancesAndPriceHistory = createAction('refreshBalancesAndPriceHistory') diff --git a/components/brave_wallet_ui/common/async/handlers.ts b/components/brave_wallet_ui/common/async/handlers.ts index 876527599649..4f3ee0fef6a5 100644 --- a/components/brave_wallet_ui/common/async/handlers.ts +++ b/components/brave_wallet_ui/common/async/handlers.ts @@ -244,19 +244,20 @@ handler.on(WalletActions.addUserAsset.getType(), async (store: Store, payload: A const braveWalletService = getAPIProxy().braveWalletService const result = await braveWalletService.addUserAsset(payload.token, payload.chainId) store.dispatch(WalletActions.addUserAssetError(!result.success)) - await refreshBalancesPricesAndHistory(store) }) handler.on(WalletActions.removeUserAsset.getType(), async (store: Store, payload: RemoveUserAssetPayloadType) => { const braveWalletService = getAPIProxy().braveWalletService await braveWalletService.removeUserAsset(payload.token, payload.chainId) - await refreshBalancesPricesAndHistory(store) }) handler.on(WalletActions.setUserAssetVisible.getType(), async (store: Store, payload: SetUserAssetVisiblePayloadType) => { const braveWalletService = getAPIProxy().braveWalletService await braveWalletService.setUserAssetVisible(payload.token, payload.chainId, payload.isVisible) - await refreshBalancesPricesAndHistory(store) +}) + +handler.on(WalletActions.refreshBalancesAndPriceHistory.getType(), async (store: Store) => { + refreshBalancesPricesAndHistory(store) }) handler.on(WalletActions.selectPortfolioTimeline.getType(), async (store: Store, payload: BraveWallet.AssetPriceTimeframe) => { diff --git a/components/brave_wallet_ui/common/constants/mocks.ts b/components/brave_wallet_ui/common/constants/mocks.ts index ef6582a89ded..846fb848af73 100644 --- a/components/brave_wallet_ui/common/constants/mocks.ts +++ b/components/brave_wallet_ui/common/constants/mocks.ts @@ -57,7 +57,8 @@ export const mockERC20Token: BraveWallet.BlockchainToken = { isErc721: false, decimals: 18, visible: true, - tokenId: '' + tokenId: '', + coingeckoId: '' } export const mockAccount: WalletAccountType = { diff --git a/components/brave_wallet_ui/common/hooks/assets-management.test.ts b/components/brave_wallet_ui/common/hooks/assets-management.test.ts new file mode 100644 index 000000000000..af1475ec9ac3 --- /dev/null +++ b/components/brave_wallet_ui/common/hooks/assets-management.test.ts @@ -0,0 +1,113 @@ +import { renderHook, act } from '@testing-library/react-hooks' +import * as WalletActions from '../actions/wallet_actions' +import { + mockNetwork +} from '../constants/mocks' +import useAssetManagement from './assets-management' +import { AccountAssetOptions } from '../../options/asset-options' + +const mockUserVisibleTokensInfo = [ + AccountAssetOptions[1], + AccountAssetOptions[2] +] + +const mockCustomToken = { + contractAddress: 'customTokenContractAddress', + name: 'Custom Token', + symbol: 'CT', + logo: '', + isErc20: true, + isErc721: false, + decimals: 18, + visible: true, + tokenId: '', + coingeckoId: '' +} + +describe('useAssetManagement hook', () => { + let addedAssetSpy: jest.SpyInstance + let removedAssetSpy: jest.SpyInstance + let refreshedBalancesPricesSpy: jest.SpyInstance + let setUserAssetVisibleSpy: jest.SpyInstance + + addedAssetSpy = jest.spyOn(WalletActions, 'addUserAsset') + removedAssetSpy = jest.spyOn(WalletActions, 'removeUserAsset') + refreshedBalancesPricesSpy = jest.spyOn(WalletActions, 'refreshBalancesAndPriceHistory') + setUserAssetVisibleSpy = jest.spyOn(WalletActions, 'setUserAssetVisible') + + it('Should add an asset', () => { + const { result } = renderHook(() => useAssetManagement( + WalletActions.addUserAsset, + WalletActions.setUserAssetVisible, + WalletActions.removeUserAsset, + WalletActions.refreshBalancesAndPriceHistory, + mockNetwork, + AccountAssetOptions, + mockUserVisibleTokensInfo + )) + act(() => result.current.onUpdateVisibleAssets([AccountAssetOptions[1], AccountAssetOptions[2], AccountAssetOptions[3]])) + expect(addedAssetSpy).toBeCalledWith({ chainId: mockNetwork.chainId, token: { ...AccountAssetOptions[3], logo: '' } }) + expect(refreshedBalancesPricesSpy).toBeCalledTimes(1) + }) + + it('Should remove an asset', () => { + const { result } = renderHook(() => useAssetManagement( + WalletActions.addUserAsset, + WalletActions.setUserAssetVisible, + WalletActions.removeUserAsset, + WalletActions.refreshBalancesAndPriceHistory, + mockNetwork, + AccountAssetOptions, + mockUserVisibleTokensInfo + )) + act(() => result.current.onUpdateVisibleAssets([AccountAssetOptions[1]])) + expect(removedAssetSpy).toBeCalledWith({ chainId: mockNetwork.chainId, token: AccountAssetOptions[2] }) + expect(refreshedBalancesPricesSpy).toBeCalledTimes(1) + }) + + it('Should remove and add an asset', () => { + const { result } = renderHook(() => useAssetManagement( + WalletActions.addUserAsset, + WalletActions.setUserAssetVisible, + WalletActions.removeUserAsset, + WalletActions.refreshBalancesAndPriceHistory, + mockNetwork, + AccountAssetOptions, + mockUserVisibleTokensInfo + )) + act(() => result.current.onUpdateVisibleAssets([AccountAssetOptions[1], AccountAssetOptions[3]])) + expect(addedAssetSpy).toBeCalledWith({ chainId: mockNetwork.chainId, token: { ...AccountAssetOptions[3], logo: '' } }) + expect(removedAssetSpy).toBeCalledWith({ chainId: mockNetwork.chainId, token: AccountAssetOptions[2] }) + expect(refreshedBalancesPricesSpy).toBeCalledTimes(1) + }) + + it('Should set custom tokens visibility to false', () => { + const { result } = renderHook(() => useAssetManagement( + WalletActions.addUserAsset, + WalletActions.setUserAssetVisible, + WalletActions.removeUserAsset, + WalletActions.refreshBalancesAndPriceHistory, + mockNetwork, + AccountAssetOptions, + [...mockUserVisibleTokensInfo, mockCustomToken] + )) + act(() => result.current.onUpdateVisibleAssets([...mockUserVisibleTokensInfo, { ...mockCustomToken, visible: false }])) + expect(setUserAssetVisibleSpy).toBeCalledWith({ chainId: mockNetwork.chainId, token: { ...mockCustomToken, visible: false }, isVisible: false }) + expect(refreshedBalancesPricesSpy).toBeCalledTimes(1) + }) + + it('Should set custom tokens visibility to true', () => { + const { result } = renderHook(() => useAssetManagement( + WalletActions.addUserAsset, + WalletActions.setUserAssetVisible, + WalletActions.removeUserAsset, + WalletActions.refreshBalancesAndPriceHistory, + mockNetwork, + AccountAssetOptions, + [...mockUserVisibleTokensInfo, { ...mockCustomToken, visible: false }] + )) + act(() => result.current.onUpdateVisibleAssets([...mockUserVisibleTokensInfo, mockCustomToken])) + expect(setUserAssetVisibleSpy).toBeCalledWith({ chainId: mockNetwork.chainId, token: mockCustomToken, isVisible: true }) + expect(refreshedBalancesPricesSpy).toBeCalledTimes(1) + }) +}) diff --git a/components/brave_wallet_ui/common/hooks/assets-management.ts b/components/brave_wallet_ui/common/hooks/assets-management.ts new file mode 100644 index 000000000000..b1865dd810e8 --- /dev/null +++ b/components/brave_wallet_ui/common/hooks/assets-management.ts @@ -0,0 +1,88 @@ +// Copyright (c) 2021 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// you can obtain one at http://mozilla.org/MPL/2.0/. + +import * as React from 'react' +import { SimpleActionCreator, EmptyActionCreator } from 'redux-act' +import { + BraveWallet +} from '../../constants/types' + +import { + AddUserAssetPayloadType, + SetUserAssetVisiblePayloadType, + RemoveUserAssetPayloadType +} from '../constants/action_types' + +import { stripERC20TokenImageURL } from '../../utils/string-utils' + +export default function useAssetManagement ( + addUserAsset: SimpleActionCreator, + setUserAssetVisible: SimpleActionCreator, + removeUserAsset: SimpleActionCreator, + refreshBalancesPricesAndHistory: EmptyActionCreator, + selectedNetwork: BraveWallet.EthereumChain, + fullTokenList: BraveWallet.BlockchainToken[], + userVisibleTokensInfo: BraveWallet.BlockchainToken[] +) { + const onAddUserAsset = (token: BraveWallet.BlockchainToken) => { + addUserAsset({ + token: { + ...token, + logo: stripERC20TokenImageURL(token.logo) || '' + }, + chainId: selectedNetwork.chainId + }) + } + + const onAddCustomAsset = (token: BraveWallet.BlockchainToken) => { + onAddUserAsset(token) + refreshBalancesPricesAndHistory() + } + + const findVisibleTokenInfo = (token: BraveWallet.BlockchainToken) => { + return userVisibleTokensInfo.find((t) => t.contractAddress.toLowerCase() === token.contractAddress.toLowerCase()) + } + + const onUpdateVisibleAssets = React.useCallback((updatedTokensList: BraveWallet.BlockchainToken[]) => { + // Gets a list of all added tokens and adds them to the userVisibleTokensInfo list + updatedTokensList.filter((firstItem) => + !userVisibleTokensInfo.some((secondItem) => + firstItem.contractAddress.toLowerCase() === secondItem.contractAddress.toLowerCase())) + .forEach((token) => onAddUserAsset(token)) + + // Gets a list of all removed tokens and removes them from the userVisibleTokensInfo list + userVisibleTokensInfo.filter((firstItem) => + !updatedTokensList.some((secondItem) => + firstItem.contractAddress.toLowerCase() === secondItem.contractAddress.toLowerCase())) + .forEach((token) => removeUserAsset({ token, chainId: selectedNetwork.chainId })) + + // Gets a list of custom tokens returned from updatedTokensList payload + // then compares customTokens against userVisibleTokensInfo list and updates the custom tokens visibility if it has changed + updatedTokensList.filter((firstItem) => + !fullTokenList.some((secondItem) => + firstItem.contractAddress.toLowerCase() === secondItem.contractAddress.toLowerCase())) + .forEach((token) => { + const foundToken = findVisibleTokenInfo(token) + // Since a networks native token (example 'ETH') can be removed from the the userVisibleTokensInfo list, + // when it is re-added we handle it as a custom token since it is not part of the token registry. + // This check here will add it only if it's value 'visible' is returned true + if (token.contractAddress.toLowerCase() === '' && !foundToken?.visible && token.visible) { + onAddUserAsset(token) + } + // Updates token visibility exluding a networks native token + if (foundToken?.visible !== token.visible && token.contractAddress.toLowerCase() !== '') { + setUserAssetVisible({ token, chainId: selectedNetwork.chainId, isVisible: token.visible }) + } + }) + + // Refreshes Balances, Prices and Price History when done. + refreshBalancesPricesAndHistory() + }, [userVisibleTokensInfo, selectedNetwork]) + + return { + onUpdateVisibleAssets, + onAddCustomAsset + } +} diff --git a/components/brave_wallet_ui/common/hooks/index.ts b/components/brave_wallet_ui/common/hooks/index.ts index 3eeb37b1664f..9b75949f4823 100644 --- a/components/brave_wallet_ui/common/hooks/index.ts +++ b/components/brave_wallet_ui/common/hooks/index.ts @@ -13,6 +13,7 @@ import usePricing from './pricing' import usePreset from './select-preset' import useTokenInfo from './token' import useExplorer from './explorer' +import useAssetManagement from './assets-management' export { useAssets, @@ -25,5 +26,6 @@ export { useSend, usePreset, useTokenInfo, - useExplorer + useExplorer, + useAssetManagement } diff --git a/components/brave_wallet_ui/components/desktop/popup-modals/edit-visible-assets-modal/index.tsx b/components/brave_wallet_ui/components/desktop/popup-modals/edit-visible-assets-modal/index.tsx index f3de1d846d1f..54f5be2f8466 100644 --- a/components/brave_wallet_ui/components/desktop/popup-modals/edit-visible-assets-modal/index.tsx +++ b/components/brave_wallet_ui/components/desktop/popup-modals/edit-visible-assets-modal/index.tsx @@ -29,9 +29,8 @@ import { export interface Props { onClose: () => void - onAddUserAsset: (token: BraveWallet.BlockchainToken) => void - onSetUserAssetVisible: (token: BraveWallet.BlockchainToken, isVisible: boolean) => void - onRemoveUserAsset: (token: BraveWallet.BlockchainToken) => void + onAddCustomAsset: (token: BraveWallet.BlockchainToken) => void + onUpdateVisibleAssets: (updatedTokensList: BraveWallet.BlockchainToken[]) => void addUserAssetError: boolean fullAssetList: BraveWallet.BlockchainToken[] userVisibleTokensInfo: BraveWallet.BlockchainToken[] @@ -47,25 +46,51 @@ const EditVisibleAssetsModal = (props: Props) => { addUserAssetError, selectedNetwork, onClose, - onAddUserAsset, - onRemoveUserAsset, - onSetUserAssetVisible, + onAddCustomAsset, onFindTokenInfoByContractAddress, + onUpdateVisibleAssets, foundTokenInfoByContractAddress } = props + // Token List States const [filteredTokenList, setFilteredTokenList] = React.useState([]) + const [updatedTokensList, setUpdatedTokensList] = React.useState([]) + + // Modal UI States + const [showAddCustomToken, setShowAddCustomToken] = React.useState(false) const [isLoading, setIsLoading] = React.useState(false) + const [hasError, setHasError] = React.useState(false) + const [showTokenIDRequired, setShowTokenIDRequired] = React.useState(false) + + // Form States const [searchValue, setSearchValue] = React.useState('') - const [showAddCustomToken, setShowAddCustomToken] = React.useState(false) const [tokenName, setTokenName] = React.useState('') const [tokenID, setTokenID] = React.useState('') - const [showTokenIDRequired, setShowTokenIDRequired] = React.useState(false) const [tokenSymbol, setTokenSymbol] = React.useState('') const [tokenContractAddress, setTokenContractAddress] = React.useState('') const [tokenDecimals, setTokenDecimals] = React.useState('') - const [hasError, setHasError] = React.useState(false) + // 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 + // native asset (example 'ETH') with the value of visible: false to prevent breaking + // other parts of the wallet. + + // This method here is used to determine if that is the case + // and allows us to handle our true visible lists. + const isUserVisibleTokensInfoEmpty = React.useMemo((): boolean => { + return userVisibleTokensInfo.length === 1 && + userVisibleTokensInfo[0].contractAddress === '' && + !userVisibleTokensInfo[0].visible + }, [userVisibleTokensInfo]) + + React.useEffect(() => { + if (isUserVisibleTokensInfoEmpty) { + return + } + setUpdatedTokensList(userVisibleTokensInfo) + }, [userVisibleTokensInfo]) + + // Handle Form Input Changes const handleTokenNameChanged = (event: React.ChangeEvent) => { if (hasError) { setHasError(false) @@ -117,20 +142,20 @@ const EditVisibleAssetsModal = (props: Props) => { } const tokenList = React.useMemo(() => { - const userVisibleContracts = userVisibleTokensInfo - .map((token) => token.contractAddress.toLowerCase()) - const fullAssetsListPlusNativeToken = fullAssetList.some(token => token.contractAddress === '') + const userVisibleContracts = isUserVisibleTokensInfoEmpty + ? [] + : userVisibleTokensInfo.map((token) => token.contractAddress.toLowerCase()) + + const fullAssetsListPlusNativeToken = userVisibleContracts.includes('') ? fullAssetList : [nativeAsset, ...fullAssetList] - const rest = fullAssetsListPlusNativeToken - .filter((token) => - !userVisibleContracts.includes(token.contractAddress.toLowerCase())) - return [ - ...userVisibleTokensInfo, - ...rest - ] - }, [fullAssetList, userVisibleTokensInfo, selectedNetwork]) + const filteredTokenRegistry = fullAssetsListPlusNativeToken.filter((token) => !userVisibleContracts.includes(token.contractAddress.toLowerCase())) + + return isUserVisibleTokensInfoEmpty + ? filteredTokenRegistry + : [...userVisibleTokensInfo, ...filteredTokenRegistry] + }, [fullAssetList, userVisibleTokensInfo]) React.useEffect(() => { // Added this timeout to throttle setting the list @@ -172,10 +197,10 @@ const EditVisibleAssetsModal = (props: Props) => { let token = foundTokenInfoByContractAddress token.tokenId = tokenID ? toHex(tokenID) : '' setIsLoading(true) - onAddUserAsset(token) + onAddCustomAsset(token) return } - onAddUserAsset(foundTokenInfoByContractAddress) + onAddCustomAsset(foundTokenInfoByContractAddress) } else { const newToken: BraveWallet.BlockchainToken = { contractAddress: tokenContractAddress, @@ -189,18 +214,21 @@ const EditVisibleAssetsModal = (props: Props) => { visible: true, coingeckoId: '' } - onAddUserAsset(newToken) + onAddCustomAsset(newToken) } setIsLoading(true) } + const findUpdatedTokenInfo = (token: BraveWallet.BlockchainToken) => { + return updatedTokensList.find((t) => t.contractAddress.toLowerCase() === token.contractAddress.toLowerCase()) + } + const isUserToken = (token: BraveWallet.BlockchainToken) => { - return userVisibleTokensInfo - .some(e => e.contractAddress.toLowerCase() === token.contractAddress.toLowerCase()) + return updatedTokensList.some(e => e.contractAddress.toLowerCase() === token.contractAddress.toLowerCase()) } const isAssetSelected = (token: BraveWallet.BlockchainToken): boolean => { - return (isUserToken(token) && token.visible) ?? false + return (isUserToken(token) && findUpdatedTokenInfo(token)?.visible) ?? false } const isCustomToken = React.useCallback((token: BraveWallet.BlockchainToken): boolean => { @@ -212,12 +240,25 @@ const EditVisibleAssetsModal = (props: Props) => { .some(each => each.contractAddress.toLowerCase() === token.contractAddress.toLowerCase()) }, [fullAssetList]) + const addOrRemoveTokenFromList = (selected: boolean, token: BraveWallet.BlockchainToken) => { + if (selected) { + const addededToList = [...updatedTokensList, token] + return addededToList + } + const removedFromList = updatedTokensList.filter((t) => t.contractAddress !== token.contractAddress) + return removedFromList + } + const onCheckWatchlistItem = (key: string, selected: boolean, token: BraveWallet.BlockchainToken, isCustom: boolean) => { if (isUserToken(token)) { if (isCustom) { - selected ? onSetUserAssetVisible(token, true) : onSetUserAssetVisible(token, false) + const updatedCustomToken = selected ? { ...token, visible: true } : { ...token, visible: false } + const tokenIndex = updatedTokensList.findIndex((t) => t.contractAddress === token.contractAddress) + let newList = [...updatedTokensList] + newList.splice(tokenIndex, 1, updatedCustomToken) + setUpdatedTokensList(newList) } else { - selected ? onAddUserAsset(token) : onRemoveUserAsset(token) + setUpdatedTokensList(addOrRemoveTokenFromList(selected, token)) } } else { if (token.isErc721) { @@ -226,9 +267,8 @@ const EditVisibleAssetsModal = (props: Props) => { setShowAddCustomToken(true) return } - onAddUserAsset(token) + setUpdatedTokensList(addOrRemoveTokenFromList(selected, token)) } - setIsLoading(true) } const toggleShowAddCustomToken = () => { @@ -246,8 +286,10 @@ const EditVisibleAssetsModal = (props: Props) => { } const onRemoveAsset = (token: BraveWallet.BlockchainToken) => { - setIsLoading(true) - onRemoveUserAsset(token) + const newUserList = updatedTokensList.filter((t) => t.contractAddress.toLowerCase() !== token.contractAddress.toLowerCase()) + setUpdatedTokensList(newUserList) + const newFilteredTokenList = filteredTokenList.filter((t) => t.contractAddress.toLowerCase() !== token.contractAddress.toLowerCase()) + setFilteredTokenList(newFilteredTokenList) } const isDecimalDisabled = React.useMemo((): boolean => { @@ -286,6 +328,11 @@ const EditVisibleAssetsModal = (props: Props) => { !tokenContractAddress.toLowerCase().startsWith('0x') }, [tokenName, tokenSymbol, tokenDecimals, tokenID, tokenContractAddress]) + const onClickDone = () => { + onUpdateVisibleAssets(updatedTokensList) + onClose() + } + return ( {showAddCustomToken && @@ -388,7 +435,7 @@ const EditVisibleAssetsModal = (props: Props) => { diff --git a/components/brave_wallet_ui/components/desktop/views/crypto/index.tsx b/components/brave_wallet_ui/components/desktop/views/crypto/index.tsx index ffcc189b33f8..69d9e2b39b47 100644 --- a/components/brave_wallet_ui/components/desktop/views/crypto/index.tsx +++ b/components/brave_wallet_ui/components/desktop/views/crypto/index.tsx @@ -46,10 +46,9 @@ export interface Props { onDoneViewingPrivateKey: () => void onImportAccountFromJson: (accountName: string, password: string, json: string) => void onSetImportError: (error: boolean) => void - onAddUserAsset: (token: BraveWallet.BlockchainToken) => void - onSetUserAssetVisible: (token: BraveWallet.BlockchainToken, isVisible: boolean) => void - onRemoveUserAsset: (token: BraveWallet.BlockchainToken) => void + onAddCustomAsset: (token: BraveWallet.BlockchainToken) => void onOpenWalletSettings: () => void + onUpdateVisibleAssets: (updatedTokensList: BraveWallet.BlockchainToken[]) => void defaultCurrencies: DefaultCurrencies addUserAssetError: boolean hasImportError: boolean @@ -105,13 +104,12 @@ const CryptoView = (props: Props) => { onDoneViewingPrivateKey, onImportAccountFromJson, onSetImportError, - onAddUserAsset, - onSetUserAssetVisible, - onRemoveUserAsset, + onAddCustomAsset, onOpenWalletSettings, onShowAddModal, onHideAddModal, onShowVisibleAssetsModal, + onUpdateVisibleAssets, showVisibleAssetsModal, defaultCurrencies, defaultWallet, @@ -311,9 +309,7 @@ const CryptoView = (props: Props) => { onSelectAccount={onSelectAccount} onClickAddAccount={onClickAddAccount} onSelectNetwork={onSelectNetwork} - onAddUserAsset={onAddUserAsset} - onSetUserAssetVisible={onSetUserAssetVisible} - onRemoveUserAsset={onRemoveUserAsset} + onAddCustomAsset={onAddCustomAsset} selectedAsset={selectedAsset} portfolioBalance={portfolioBalance} portfolioPriceHistory={portfolioPriceHistory} @@ -335,6 +331,7 @@ const CryptoView = (props: Props) => { onCancelTransaction={onCancelTransaction} onFindTokenInfoByContractAddress={onFindTokenInfoByContractAddress} foundTokenInfoByContractAddress={foundTokenInfoByContractAddress} + onUpdateVisibleAssets={onUpdateVisibleAssets} /> diff --git a/components/brave_wallet_ui/components/desktop/views/portfolio/index.tsx b/components/brave_wallet_ui/components/desktop/views/portfolio/index.tsx index 1c07ad11ea49..a792b3490ce2 100644 --- a/components/brave_wallet_ui/components/desktop/views/portfolio/index.tsx +++ b/components/brave_wallet_ui/components/desktop/views/portfolio/index.tsx @@ -66,10 +66,9 @@ export interface Props { onSelectAccount: (account: WalletAccountType) => void onClickAddAccount: (tabId: AddAccountNavTypes) => () => void onSelectNetwork: (network: BraveWallet.EthereumChain) => void - onAddUserAsset: (token: BraveWallet.BlockchainToken) => void - onSetUserAssetVisible: (token: BraveWallet.BlockchainToken, isVisible: boolean) => void + onAddCustomAsset: (token: BraveWallet.BlockchainToken) => void onShowVisibleAssetsModal: (showModal: boolean) => void - onRemoveUserAsset: (token: BraveWallet.BlockchainToken) => void + onUpdateVisibleAssets: (updatedTokensList: BraveWallet.BlockchainToken[]) => void showVisibleAssetsModal: boolean defaultCurrencies: DefaultCurrencies addUserAssetError: boolean @@ -106,10 +105,9 @@ const Portfolio = (props: Props) => { onSelectAccount, onClickAddAccount, onSelectNetwork, - onAddUserAsset, - onSetUserAssetVisible, - onRemoveUserAsset, + onAddCustomAsset, onShowVisibleAssetsModal, + onUpdateVisibleAssets, showVisibleAssetsModal, defaultCurrencies, addUserAssetError, @@ -357,12 +355,11 @@ const Portfolio = (props: Props) => { userVisibleTokensInfo={userVisibleTokensInfo} addUserAssetError={addUserAssetError} onClose={toggleShowVisibleAssetModal} - onAddUserAsset={onAddUserAsset} - onSetUserAssetVisible={onSetUserAssetVisible} - onRemoveUserAsset={onRemoveUserAsset} + onAddCustomAsset={onAddCustomAsset} selectedNetwork={selectedNetwork} onFindTokenInfoByContractAddress={onFindTokenInfoByContractAddress} foundTokenInfoByContractAddress={foundTokenInfoByContractAddress} + onUpdateVisibleAssets={onUpdateVisibleAssets} /> } diff --git a/components/brave_wallet_ui/page/container.tsx b/components/brave_wallet_ui/page/container.tsx index aeb64dc6684f..f9d45009443c 100644 --- a/components/brave_wallet_ui/page/container.tsx +++ b/components/brave_wallet_ui/page/container.tsx @@ -38,7 +38,6 @@ import BackupWallet from '../stories/screens/backup-wallet' import { formatWithCommasAndDecimals } from '../utils/format-prices' import { GetBuyOrFaucetUrl } from '../utils/buy-asset-url' import { mojoTimeDeltaToJSDate } from '../utils/datetime-utils' -import { stripERC20TokenImageURL } from '../utils/string-utils' import { addNumericValues } from '../utils/bn-utils' import { @@ -54,7 +53,16 @@ import { } from '../common/async/lib' // Hooks -import { useAssets, useBalance, usePreset, usePricing, useSend, useSwap, useTokenInfo } from '../common/hooks' +import { + useAssets, + useBalance, + usePreset, + usePricing, + useSend, + useSwap, + useTokenInfo, + useAssetManagement +} from '../common/hooks' type Props = { wallet: WalletState @@ -193,6 +201,19 @@ function Container (props: Props) { props.wallet.fullTokenList ) + const { + onAddCustomAsset, + onUpdateVisibleAssets + } = useAssetManagement( + props.walletActions.addUserAsset, + props.walletActions.setUserAssetVisible, + props.walletActions.removeUserAsset, + props.walletActions.refreshBalancesAndPriceHistory, + selectedNetwork, + fullTokenList, + userVisibleTokensInfo + ) + const { computeFiatAmount } = usePricing(transactionSpotPrices) const getAccountBalance = useBalance(selectedNetwork) const sendAssetBalance = getAccountBalance(selectedAccount, selectedSendAsset) @@ -424,24 +445,6 @@ function Container (props: Props) { props.walletPageActions.checkWalletsToImport() } - const onSetUserAssetVisible = (token: BraveWallet.BlockchainToken, isVisible: boolean) => { - props.walletActions.setUserAssetVisible({ token, chainId: selectedNetwork.chainId, isVisible }) - } - - const onAddUserAsset = (token: BraveWallet.BlockchainToken) => { - props.walletActions.addUserAsset({ - token: { - ...token, - logo: stripERC20TokenImageURL(token.logo) || '' - }, - chainId: selectedNetwork.chainId - }) - } - - const onRemoveUserAsset = (token: BraveWallet.BlockchainToken) => { - props.walletActions.removeUserAsset({ token, chainId: selectedNetwork.chainId }) - } - const onOpenWalletSettings = () => { props.walletPageActions.openWalletSettings() } @@ -625,9 +628,7 @@ function Container (props: Props) { transactionSpotPrices={transactionSpotPrices} userVisibleTokensInfo={userVisibleTokensInfo} getBalance={getBalance} - onAddUserAsset={onAddUserAsset} - onSetUserAssetVisible={onSetUserAssetVisible} - onRemoveUserAsset={onRemoveUserAsset} + onAddCustomAsset={onAddCustomAsset} addUserAssetError={addUserAssetError} defaultWallet={defaultWallet} onOpenWalletSettings={onOpenWalletSettings} @@ -640,6 +641,7 @@ function Container (props: Props) { showVisibleAssetsModal={showVisibleAssetsModal} onFindTokenInfoByContractAddress={onFindTokenInfoByContractAddress} foundTokenInfoByContractAddress={foundTokenInfoByContractAddress} + onUpdateVisibleAssets={onUpdateVisibleAssets} /> } diff --git a/components/brave_wallet_ui/stories/screens/crypto-story-view.tsx b/components/brave_wallet_ui/stories/screens/crypto-story-view.tsx index ac290fd92253..be63bf5124b5 100644 --- a/components/brave_wallet_ui/stories/screens/crypto-story-view.tsx +++ b/components/brave_wallet_ui/stories/screens/crypto-story-view.tsx @@ -53,9 +53,8 @@ export interface Props { privateKey: string transactionSpotPrices: BraveWallet.AssetPrice[] hasImportError: boolean - onAddUserAsset: (token: BraveWallet.BlockchainToken) => void - onSetUserAssetVisible: (token: BraveWallet.BlockchainToken, isVisible: boolean) => void - onRemoveUserAsset: (token: BraveWallet.BlockchainToken) => void + onUpdateVisibleAssets: (updatedTokensList: BraveWallet.BlockchainToken[]) => void + onAddCustomAsset: (token: BraveWallet.BlockchainToken) => void onLockWallet: () => void onSetImportError: (hasError: boolean) => void onImportAccountFromJson: (accountName: string, password: string, json: string) => void @@ -107,9 +106,6 @@ const CryptoStoryView = (props: Props) => { isFetchingPortfolioPriceHistory, showVisibleAssetsModal, onShowVisibleAssetsModal, - onAddUserAsset, - onSetUserAssetVisible, - onRemoveUserAsset, onLockWallet, onShowBackup, onChangeTimeline, @@ -129,6 +125,8 @@ const CryptoStoryView = (props: Props) => { onImportAccountFromJson, onSetImportError, onFindTokenInfoByContractAddress, + onUpdateVisibleAssets, + onAddCustomAsset, foundTokenInfoByContractAddress } = props const [showBackupWarning, setShowBackupWarning] = React.useState(needsBackup) @@ -297,9 +295,6 @@ const CryptoStoryView = (props: Props) => { onClickAddAccount={onClickAddAccount} onSelectNetwork={onSelectNetwork} addUserAssetError={false} - onAddUserAsset={onAddUserAsset} - onRemoveUserAsset={onRemoveUserAsset} - onSetUserAssetVisible={onSetUserAssetVisible} selectedAsset={selectedAsset} portfolioBalance={portfolioBalance} portfolioPriceHistory={portfolioPriceHistory} @@ -320,6 +315,8 @@ const CryptoStoryView = (props: Props) => { showVisibleAssetsModal={showVisibleAssetsModal} onFindTokenInfoByContractAddress={onFindTokenInfoByContractAddress} foundTokenInfoByContractAddress={foundTokenInfoByContractAddress} + onUpdateVisibleAssets={onUpdateVisibleAssets} + onAddCustomAsset={onAddCustomAsset} /> } {selectedTab === 'accounts' && diff --git a/components/brave_wallet_ui/stories/wallet-concept.tsx b/components/brave_wallet_ui/stories/wallet-concept.tsx index 2d8019f61a82..8c26f30000fb 100644 --- a/components/brave_wallet_ui/stories/wallet-concept.tsx +++ b/components/brave_wallet_ui/stories/wallet-concept.tsx @@ -478,10 +478,6 @@ export const _DesktopWalletConcept = (args: { onboarding: boolean, locked: boole setSelectedNetwork(network) } - const onSetUserAssetVisible = () => { - alert('Will make a custom asset visible') - } - const onSelectTransactAsset = (asset: BraveWallet.BlockchainToken, toOrFrom: ToOrFromType) => { if (toOrFrom === 'from') { setFromAsset(asset) @@ -636,12 +632,12 @@ export const _DesktopWalletConcept = (args: { onboarding: boolean, locked: boole setImportWalletError({ hasError }) } - const onAddUserAsset = () => { - alert('Will Add a Token') + const onAddCustomAsset = () => { + alert('Will Add a Custom Token') } - const onRemoveUserAsset = () => { - alert('Will Remove a Token') + const onUpdateVisibleAssets = () => { + alert('Will Update Visible Assets List') } const onSetCustomTolerance = (value: string) => { @@ -764,9 +760,8 @@ export const _DesktopWalletConcept = (args: { onboarding: boolean, locked: boole onImportAccountFromJson={onImportAccountFromJson} hasImportError={importAccountError} onSetImportError={onSetImportAccountError} - onAddUserAsset={onAddUserAsset} - onSetUserAssetVisible={onSetUserAssetVisible} - onRemoveUserAsset={onRemoveUserAsset} + onAddCustomAsset={onAddCustomAsset} + onUpdateVisibleAssets={onUpdateVisibleAssets} transactionSpotPrices={[]} userVisibleTokensInfo={[]} onShowVisibleAssetsModal={onShowVisibleAssetsModal}