-
Notifications
You must be signed in to change notification settings - Fork 868
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #11875 from brave/asset-management-hook
chore(wallet): Created Asset Management Hook
- Loading branch information
Showing
12 changed files
with
340 additions
and
99 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
113 changes: 113 additions & 0 deletions
113
components/brave_wallet_ui/common/hooks/assets-management.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
}) | ||
}) |
88 changes: 88 additions & 0 deletions
88
components/brave_wallet_ui/common/hooks/assets-management.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<AddUserAssetPayloadType>, | ||
setUserAssetVisible: SimpleActionCreator<SetUserAssetVisiblePayloadType>, | ||
removeUserAsset: SimpleActionCreator<RemoveUserAssetPayloadType>, | ||
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.