From 2fe72ca6451bfefde25a9a8f050187223c555ecb Mon Sep 17 00:00:00 2001 From: William Muli Date: Tue, 6 Sep 2022 23:26:44 +0300 Subject: [PATCH 1/2] Add nft tab --- .../browser/brave_wallet_constants.h | 4 +- .../components/desktop/views/crypto/index.tsx | 6 + .../views/nfts/components/nfts.styles.tsx | 39 +++++++ .../desktop/views/nfts/components/nfts.tsx | 109 ++++++++++++++++++ .../desktop/views/nfts/nft-view.tsx | 39 +++++++ .../views/portfolio/portfolio-asset.tsx | 6 +- components/brave_wallet_ui/constants/types.ts | 4 + .../options/top-nav-options.ts | 4 + components/brave_wallet_ui/page/container.tsx | 7 +- components/brave_wallet_ui/stories/locale.ts | 7 +- components/resources/wallet_strings.grdp | 4 +- 11 files changed, 219 insertions(+), 10 deletions(-) create mode 100644 components/brave_wallet_ui/components/desktop/views/nfts/components/nfts.styles.tsx create mode 100644 components/brave_wallet_ui/components/desktop/views/nfts/components/nfts.tsx create mode 100644 components/brave_wallet_ui/components/desktop/views/nfts/nft-view.tsx diff --git a/components/brave_wallet/browser/brave_wallet_constants.h b/components/brave_wallet/browser/brave_wallet_constants.h index 069e016291f8..5b252b3ae7d6 100644 --- a/components/brave_wallet/browser/brave_wallet_constants.h +++ b/components/brave_wallet/browser/brave_wallet_constants.h @@ -878,7 +878,9 @@ constexpr webui::LocalizedString kLocalizedStrings[] = { {"braveWalletConfirmHidingToken", IDS_BRAVE_WALLET_CONFIRM_HIDING_TOKEN}, {"braveWalletCancelHidingToken", IDS_BRAVE_WALLET_CANCEL_HIDING_TOKEN}, {"braveWalletRequestFeatureButtonText", - IDS_BRAVE_WALLET_REQUEST_FEATURE_BUTTON_TEXT}}; + IDS_BRAVE_WALLET_REQUEST_FEATURE_BUTTON_TEXT}, + {"braveWalletNftsEmptyState", IDS_BRAVE_WALLET_NFTS_EMPTY_STATE}, + {"braveWalletNftsEmptyStateSearch", IDS_BRAVE_WALLET_NFTS_EMPTY_STATE_SEARCH_FILTER}}; // 0x swap constants constexpr char kRopstenSwapBaseAPIURL[] = "https://ropsten.api.0x.org/"; 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 fd77dc23efd3..985ad12fe9bb 100644 --- a/components/brave_wallet_ui/components/desktop/views/crypto/index.tsx +++ b/components/brave_wallet_ui/components/desktop/views/crypto/index.tsx @@ -29,6 +29,7 @@ import { MarketView } from '../market' import { Accounts } from '../accounts/accounts' import { Account } from '../accounts/account' import { AddAccountModal } from '../../popup-modals/add-account-modal/add-account-modal' +import { NftView } from '../nfts/nft-view' interface ParamsType { category?: TopTabNavTypes @@ -231,6 +232,11 @@ const CryptoView = (props: Props) => { /> + + {nav} + + + diff --git a/components/brave_wallet_ui/components/desktop/views/nfts/components/nfts.styles.tsx b/components/brave_wallet_ui/components/desktop/views/nfts/components/nfts.styles.tsx new file mode 100644 index 000000000000..60cad5b8fbbe --- /dev/null +++ b/components/brave_wallet_ui/components/desktop/views/nfts/components/nfts.styles.tsx @@ -0,0 +1,39 @@ +// Copyright (c) 2022 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 styled from 'styled-components' + +export const FilterTokenRow = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + flex-direction: row; + width: 100%; +` + +export const NftGrid = styled.div` + display: grid; + grid-template-columns: repeat(5, 1fr); + grid-gap: 25px; + box-sizing: border-box; + width: 100%; + padding-top: 10px; + @media screen and (max-width: 1350px) { + grid-template-columns: repeat(4, 1fr); + } + @media screen and (max-width: 1150px) { + grid-template-columns: repeat(3, 1fr); + } + @media screen and (max-width: 950px) { + grid-template-columns: repeat(2, 1fr); + } +` + +export const EmptyStateText = styled.div` + text-align: center; + padding: 30px 0; + color: ${p => p.theme.color.text03}; + font-size: 14px; + font-family: Poppins; +` diff --git a/components/brave_wallet_ui/components/desktop/views/nfts/components/nfts.tsx b/components/brave_wallet_ui/components/desktop/views/nfts/components/nfts.tsx new file mode 100644 index 000000000000..7ff8b04d1efe --- /dev/null +++ b/components/brave_wallet_ui/components/desktop/views/nfts/components/nfts.tsx @@ -0,0 +1,109 @@ +// Copyright (c) 2022 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 { useHistory } from 'react-router-dom' +import { useDispatch } from 'react-redux' + +// types +import { + BraveWallet, + WalletRoutes +} from '../../../../../constants/types' + +// utils +import { getLocale } from '$web-common/locale' + +// components +import SearchBar from '../../../../shared/search-bar' +import NetworkFilterSelector from '../../../network-filter-selector' + +// styles +import { + EmptyStateText, + FilterTokenRow, + NftGrid +} from './nfts.styles' +import { NFTGridViewItem } from '../../portfolio/components/nft-grid-view/nft-grid-view-item' +import { WalletPageActions } from '../../../../../page/actions' +import Amount from '../../../../../utils/amount' + +interface Props { + networks: BraveWallet.NetworkInfo[] + nftList: BraveWallet.BlockchainToken[] +} + +export const Nfts = (props: Props) => { + const { + networks, + nftList + } = props + + // state + const [searchValue, setSearchValue] = React.useState('') + + // hooks + const history = useHistory() + const dispatch = useDispatch() + + // methods + const onSearchValueChange = React.useCallback((event: React.ChangeEvent) => { + setSearchValue(event.target.value) + }, []) + + const onSelectAsset = React.useCallback((asset: BraveWallet.BlockchainToken) => { + history.push(`${WalletRoutes.Portfolio}/${asset.contractAddress}/${asset.tokenId}`) + // reset nft metadata + dispatch(WalletPageActions.updateNFTMetadata(undefined)) + }, [dispatch]) + + // memos + const filteredNfts = React.useMemo(() => { + if (searchValue === '') { + return nftList + } + + return nftList.filter((item) => { + const tokenId = new Amount(item.tokenId).toNumber().toString() + + return ( + item.name.toLowerCase() === searchValue.toLowerCase() || + item.name.toLowerCase().includes(searchValue.toLowerCase()) || + item.symbol.toLocaleLowerCase() === searchValue.toLowerCase() || + item.symbol.toLowerCase().includes(searchValue.toLowerCase()) || + tokenId === searchValue.toLowerCase() || + tokenId.includes(searchValue.toLowerCase()) + ) + }) + }, [searchValue, nftList]) + + const emptyStateMessage = React.useMemo(() => { + return getLocale(searchValue === '' ? 'braveWalletNftsEmptyState' : 'braveWalletNftsEmptyStateSearch') + }, [searchValue]) + + return ( + <> + + + + + {filteredNfts.length === 0 + ? {emptyStateMessage} + : + {filteredNfts.map(nft => ( + onSelectAsset(nft)} + /> + ))} + + } + + ) +} diff --git a/components/brave_wallet_ui/components/desktop/views/nfts/nft-view.tsx b/components/brave_wallet_ui/components/desktop/views/nfts/nft-view.tsx new file mode 100644 index 000000000000..4b8b3f1eaae2 --- /dev/null +++ b/components/brave_wallet_ui/components/desktop/views/nfts/nft-view.tsx @@ -0,0 +1,39 @@ +// Copyright (c) 2022 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 { useSelector } from 'react-redux' + +// types +import { + SupportedTestNetworks, + WalletState +} from '../../../../constants/types' + +// components +import { Nfts } from './components/nfts' +import { AllNetworksOption } from '../../../../options/network-filter-options' + +export const NftView = () => { + // redux + const networkList = useSelector(({ wallet }: { wallet: WalletState }) => wallet.networkList) + const userVisibleTokensInfo = useSelector(({ wallet }: { wallet: WalletState}) => wallet.userVisibleTokensInfo) + const selectedNetworkFilter = useSelector(({ wallet }: { wallet: WalletState }) => wallet.selectedNetworkFilter) + + const fungibleTokens = React.useMemo(() => { + if (selectedNetworkFilter.chainId === AllNetworksOption.chainId) { + return userVisibleTokensInfo.filter((token) => !SupportedTestNetworks.includes(token.chainId) && token.isErc721) + } + + return userVisibleTokensInfo.filter(token => token.chainId === selectedNetworkFilter.chainId && token.isErc721) + }, [userVisibleTokensInfo, selectedNetworkFilter]) + + return ( + + ) +} diff --git a/components/brave_wallet_ui/components/desktop/views/portfolio/portfolio-asset.tsx b/components/brave_wallet_ui/components/desktop/views/portfolio/portfolio-asset.tsx index f2d80aac0cf2..379b96051258 100644 --- a/components/brave_wallet_ui/components/desktop/views/portfolio/portfolio-asset.tsx +++ b/components/brave_wallet_ui/components/desktop/views/portfolio/portfolio-asset.tsx @@ -356,11 +356,7 @@ export const PortfolioAsset = (props: Props) => { dispatch(WalletPageActions.selectAsset({ asset: undefined, timeFrame: selectedTimeline })) dispatch(WalletPageActions.selectCoinMarket(undefined)) setfilteredAssetList(userAssetList) - if (isShowingMarketData) { - history.push(WalletRoutes.Market) - } else { - history.push(WalletRoutes.Portfolio) - } + history.goBack() }, [ userAssetList, selectedTimeline diff --git a/components/brave_wallet_ui/constants/types.ts b/components/brave_wallet_ui/constants/types.ts index 6f3a928b9efb..9d380dda0d4a 100644 --- a/components/brave_wallet_ui/constants/types.ts +++ b/components/brave_wallet_ui/constants/types.ts @@ -125,6 +125,7 @@ export type NavTypes = export type TopTabNavTypes = | 'portfolio' | 'apps' + | 'nfts' | 'accounts' | 'market' @@ -630,6 +631,9 @@ export enum WalletRoutes { FundWalletPage = '/crypto/fund-wallet', DepositFundsPage = '/crypto/deposit-funds', + // NFTs + Nfts = '/crypto/nfts', + // market Market = '/crypto/market', MarketSub = '/crypto/market/:id?', diff --git a/components/brave_wallet_ui/options/top-nav-options.ts b/components/brave_wallet_ui/options/top-nav-options.ts index 21d008f86675..e6c485368a6c 100644 --- a/components/brave_wallet_ui/options/top-nav-options.ts +++ b/components/brave_wallet_ui/options/top-nav-options.ts @@ -15,6 +15,10 @@ export const TopNavOptions = (): TopTabNavObjectType[] => [ id: 'market', name: getLocale('braveWalletTopNavMarket') }, + { + id: 'nfts', + name: getLocale('braveWalletTopNavNFTS') + }, { id: 'accounts', name: getLocale('braveWalletTopNavAccounts') diff --git a/components/brave_wallet_ui/page/container.tsx b/components/brave_wallet_ui/page/container.tsx index 661fe9a6274b..83d524ee8a2e 100644 --- a/components/brave_wallet_ui/page/container.tsx +++ b/components/brave_wallet_ui/page/container.tsx @@ -140,7 +140,8 @@ export const Container = () => { ( walletLocation.includes(WalletRoutes.Portfolio) || walletLocation.includes(WalletRoutes.Accounts) || - walletLocation.includes(WalletRoutes.Market) + walletLocation.includes(WalletRoutes.Market) || + walletLocation.includes(WalletRoutes.Nfts) ) // effects @@ -159,7 +160,9 @@ export const Container = () => { walletLocation.includes(WalletRoutes.Backup) || walletLocation.includes(WalletRoutes.DepositFundsPage) || walletLocation.includes(WalletRoutes.FundWalletPage) || - walletLocation.includes(WalletRoutes.Portfolio) + walletLocation.includes(WalletRoutes.Portfolio) || + walletLocation.includes(WalletRoutes.Market) || + walletLocation.includes(WalletRoutes.Nfts) ) { setSessionRoute(walletLocation) } diff --git a/components/brave_wallet_ui/stories/locale.ts b/components/brave_wallet_ui/stories/locale.ts index 041fef05afd4..a095d2d7641c 100644 --- a/components/brave_wallet_ui/stories/locale.ts +++ b/components/brave_wallet_ui/stories/locale.ts @@ -740,5 +740,10 @@ provideStrings({ // ASCII toggles braveWalletViewEncodedMessage: 'View original message', - braveWalletViewDecodedMessage: 'View message in ASCII encoding' + braveWalletViewDecodedMessage: 'View message in ASCII encoding', + + // NFTs Tab + braveWalletNftsEmptyState: 'No NFTs found in your wallet. You can add NFTs by clicking the "+ Visible assets" button at the bottom of' + + ' the "Portfolio" tab', + braveWalletNftsEmptyStateSearch: 'No NFTs matching search or filter found' }) diff --git a/components/resources/wallet_strings.grdp b/components/resources/wallet_strings.grdp index bb9e5bc0b9a8..c56735fd63cf 100644 --- a/components/resources/wallet_strings.grdp +++ b/components/resources/wallet_strings.grdp @@ -61,7 +61,7 @@ Portfolio Prices Apps - NFTs + NFTs Accounts Market 1H @@ -635,4 +635,6 @@ Are you sure you want to remove "$1Eth Account 1"? Request feature Recovery phrase must be 12, 15, 18, 21, or 24 words long + No NFTs found in your wallet. You can add NFTs by clicking the "+ Visible assets" button at the bottom of the "Portfolio" tab + No NFTs matching search or filter found From 1aac196d4dfbda91112194a1e734d994c3e27417 Mon Sep 17 00:00:00 2001 From: William Muli Date: Wed, 7 Sep 2022 09:15:10 +0300 Subject: [PATCH 2/2] chore: format --- components/brave_wallet/browser/brave_wallet_constants.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/brave_wallet/browser/brave_wallet_constants.h b/components/brave_wallet/browser/brave_wallet_constants.h index 5b252b3ae7d6..e33cd8bd5f87 100644 --- a/components/brave_wallet/browser/brave_wallet_constants.h +++ b/components/brave_wallet/browser/brave_wallet_constants.h @@ -880,7 +880,8 @@ constexpr webui::LocalizedString kLocalizedStrings[] = { {"braveWalletRequestFeatureButtonText", IDS_BRAVE_WALLET_REQUEST_FEATURE_BUTTON_TEXT}, {"braveWalletNftsEmptyState", IDS_BRAVE_WALLET_NFTS_EMPTY_STATE}, - {"braveWalletNftsEmptyStateSearch", IDS_BRAVE_WALLET_NFTS_EMPTY_STATE_SEARCH_FILTER}}; + {"braveWalletNftsEmptyStateSearch", + IDS_BRAVE_WALLET_NFTS_EMPTY_STATE_SEARCH_FILTER}}; // 0x swap constants constexpr char kRopstenSwapBaseAPIURL[] = "https://ropsten.api.0x.org/";