diff --git a/components/brave_wallet/browser/brave_wallet_constants.cc b/components/brave_wallet/browser/brave_wallet_constants.cc index 803f816ac4a8..f381ca79f4d1 100644 --- a/components/brave_wallet/browser/brave_wallet_constants.cc +++ b/components/brave_wallet/browser/brave_wallet_constants.cc @@ -7,6 +7,6 @@ namespace brave_wallet { -const char kAssetRatioBaseURL[] = "https://ratios.rewards.brave.com/"; +const char kAssetRatioBaseURL[] = "https://ratios.rewards.brave.software/"; } // namespace brave_wallet diff --git a/components/brave_wallet_ui/common/actions/wallet_actions.ts b/components/brave_wallet_ui/common/actions/wallet_actions.ts index a63bf868023e..c74cb98135a9 100644 --- a/components/brave_wallet_ui/common/actions/wallet_actions.ts +++ b/components/brave_wallet_ui/common/actions/wallet_actions.ts @@ -121,5 +121,4 @@ export const setSelectedCoin = createAction('setSelectedCo export const setDefaultNetworks = createAction('setDefaultNetworks') export const getCoinMarkets = createAction('getCoinMarkets') export const setCoinMarkets = createAction('setCoinMarkets') -export const updateIsLoadingCoinMarkets = createAction('updateIsLoadingCoinMarkets') export const setSelectedNetworkFilter = createAction('setSelectedNetworkFilter') diff --git a/components/brave_wallet_ui/common/constants/action_types.ts b/components/brave_wallet_ui/common/constants/action_types.ts index 99ddce56e8cb..a14311a78b12 100644 --- a/components/brave_wallet_ui/common/constants/action_types.ts +++ b/components/brave_wallet_ui/common/constants/action_types.ts @@ -1,3 +1,4 @@ + // 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, @@ -103,7 +104,9 @@ export type SetTransactionProviderErrorType = { } export type GetCoinMarketPayload = { + // Currency of the asset stats vsAsset: string + // Number of items to fetch limit: number } diff --git a/components/brave_wallet_ui/common/hooks/market-data-management.ts b/components/brave_wallet_ui/common/hooks/market-data-management.ts index 81598460769c..b225072b0443 100644 --- a/components/brave_wallet_ui/common/hooks/market-data-management.ts +++ b/components/brave_wallet_ui/common/hooks/market-data-management.ts @@ -2,6 +2,18 @@ import * as React from 'react' import Fuse from 'fuse.js' import { BraveWallet, MarketDataTableColumnTypes, SortOrder } from '../../constants/types' +const searchOptions: Fuse.IFuseOptions = { + shouldSort: true, + threshold: 0.1, + location: 0, + distance: 0, + minMatchCharLength: 1, + keys: [ + { name: 'name', weight: 0.5 }, + { name: 'symbol', weight: 0.5 } + ] +} + export const useMarketDataManagement = (marketData: BraveWallet.CoinMarket[], sortOrder: SortOrder, columnId: MarketDataTableColumnTypes) => { const sortCoinMarketData = React.useCallback(() => { const sortedMarketData = [...marketData] @@ -20,22 +32,9 @@ export const useMarketDataManagement = (marketData: BraveWallet.CoinMarket[], so return searchList } - const options = { - shouldSort: true, - threshold: 0.1, - location: 0, - distance: 0, - minMatchCharLength: 1, - keys: [ - { name: 'name', weight: 0.5 }, - { name: 'symbol', weight: 0.5 } - ] - } - - const fuse = new Fuse(searchList, options) - const results = fuse.search(searchTerm).map((result: Fuse.FuseResult) => result.item) - - return results + const fuse = new Fuse(searchList, searchOptions) + return fuse.search(searchTerm) + .map((result: Fuse.FuseResult) => result.item) }, [marketData]) return { diff --git a/components/brave_wallet_ui/common/hooks/on-screen.ts b/components/brave_wallet_ui/common/hooks/on-screen.ts new file mode 100644 index 000000000000..060aa5d39982 --- /dev/null +++ b/components/brave_wallet_ui/common/hooks/on-screen.ts @@ -0,0 +1,26 @@ +import * as React from 'react' + +export default function useOnScreen ( + ref: React.MutableRefObject, + options: IntersectionObserverInit = { rootMargin: '0px' } +): boolean { + const [isIntersecting, setIsIntersecting] = React.useState(false) + + React.useEffect(() => { + const observer = new IntersectionObserver(([entry]) => { + setIsIntersecting(entry.isIntersecting) + }, options) + + if (ref.current) { + observer.observe(ref.current) + } + + return () => { + if (ref.current) { + observer.unobserve(ref.current) + } + } + }, [ref]) + + return isIntersecting +} diff --git a/components/brave_wallet_ui/components/asset-name-and-icon/index.tsx b/components/brave_wallet_ui/components/asset-name-and-icon/index.tsx index 5014eacca1fc..f9987822cf47 100644 --- a/components/brave_wallet_ui/components/asset-name-and-icon/index.tsx +++ b/components/brave_wallet_ui/components/asset-name-and-icon/index.tsx @@ -8,7 +8,7 @@ export interface Props { assetLogo: string } -const AssetNameAndIcon = (props: Props) => { +export const AssetNameAndIcon = (props: Props) => { const { assetLogo, assetName, symbol } = props return ( @@ -21,5 +21,3 @@ const AssetNameAndIcon = (props: Props) => { ) } - -export default AssetNameAndIcon diff --git a/components/brave_wallet_ui/components/asset-price-change/index.tsx b/components/brave_wallet_ui/components/asset-price-change/index.tsx index a63c5d8d4831..7cc033da670e 100644 --- a/components/brave_wallet_ui/components/asset-price-change/index.tsx +++ b/components/brave_wallet_ui/components/asset-price-change/index.tsx @@ -7,7 +7,7 @@ export interface Props { priceChangePercentage: string } -const AssetPriceChange = (props: Props) => { +export const AssetPriceChange = (props: Props) => { const { isDown, priceChangePercentage } = props return ( @@ -19,5 +19,3 @@ const AssetPriceChange = (props: Props) => { ) } - -export default AssetPriceChange diff --git a/components/brave_wallet_ui/components/asset-wishlist-star/index.tsx b/components/brave_wallet_ui/components/asset-wishlist-star/index.tsx index 4561c9909541..509547c47c24 100644 --- a/components/brave_wallet_ui/components/asset-wishlist-star/index.tsx +++ b/components/brave_wallet_ui/components/asset-wishlist-star/index.tsx @@ -7,7 +7,7 @@ export interface Props { onCheck?: (status: boolean) => void } -const AssetWishlistStar = (props: Props) => { +export const AssetWishlistStar = (props: Props) => { const { active, onCheck } = props const onClick = () => { @@ -18,11 +18,9 @@ const AssetWishlistStar = (props: Props) => { return ( + active={active} + onClick={onClick} + /> ) } - -export default AssetWishlistStar diff --git a/components/brave_wallet_ui/components/desktop/assets-filter-dropdown/index.tsx b/components/brave_wallet_ui/components/desktop/assets-filter-dropdown/index.tsx index cca544f3e3f8..4c2243ab95a1 100644 --- a/components/brave_wallet_ui/components/desktop/assets-filter-dropdown/index.tsx +++ b/components/brave_wallet_ui/components/desktop/assets-filter-dropdown/index.tsx @@ -2,7 +2,7 @@ import { AssetFilter } from '../../../constants/types' import * as React from 'react' import { StyledWrapper, Button, CaratDown, Dropdown } from './style' -import AssetsFilterOption from '../assets-filter-option' +import { AssetsFilterOption } from '../assets-filter-option' export interface Props { options: AssetFilter[] diff --git a/components/brave_wallet_ui/components/desktop/assets-filter-option/index.tsx b/components/brave_wallet_ui/components/desktop/assets-filter-option/index.tsx index f442c1e2216f..0c2c849e4077 100644 --- a/components/brave_wallet_ui/components/desktop/assets-filter-option/index.tsx +++ b/components/brave_wallet_ui/components/desktop/assets-filter-option/index.tsx @@ -9,7 +9,7 @@ export interface Props { onSelect: (value: string) => void } -const AssetsFilterOption = (props: Props) => { +export const AssetsFilterOption = (props: Props) => { const { selected, label, value, onSelect } = props const onClick = () => { @@ -25,5 +25,3 @@ const AssetsFilterOption = (props: Props) => { ) } - -export default AssetsFilterOption 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 98aec2efc281..36b1533868b5 100644 --- a/components/brave_wallet_ui/components/desktop/views/crypto/index.tsx +++ b/components/brave_wallet_ui/components/desktop/views/crypto/index.tsx @@ -20,7 +20,7 @@ import { PortfolioView, AccountsView } from '../' import { HardwareWalletConnectOpts } from '../../popup-modals/add-account-modal/hardware-wallet-connect/types' -import MarketView from '../market' +import { MarketView } from '../market' interface ParamsType { category?: TopTabNavTypes diff --git a/components/brave_wallet_ui/components/desktop/views/index.ts b/components/brave_wallet_ui/components/desktop/views/index.ts index 7c75ee0798a0..346981680999 100644 --- a/components/brave_wallet_ui/components/desktop/views/index.ts +++ b/components/brave_wallet_ui/components/desktop/views/index.ts @@ -1,7 +1,7 @@ import CryptoView from './crypto' import PortfolioView from './portfolio' import AccountsView from './accounts' -import MarketView from './market' +import { MarketView } from './market' export { CryptoView, diff --git a/components/brave_wallet_ui/components/desktop/views/market/index.tsx b/components/brave_wallet_ui/components/desktop/views/market/index.tsx index f3cc781fd6d8..f90090853a28 100644 --- a/components/brave_wallet_ui/components/desktop/views/market/index.tsx +++ b/components/brave_wallet_ui/components/desktop/views/market/index.tsx @@ -13,8 +13,8 @@ import { import { useMarketDataManagement } from '../../../../common/hooks/market-data-management' import { marketDataTableHeaders } from '../../../../options/market-data-headers' import { AssetFilterOption, BraveWallet, MarketDataTableColumnTypes, SortOrder } from '../../../../constants/types' -import MarketDataTable from '../../../../components/market-datatable' -import { debounce } from '../../../../../common/debounce' +import { MarketDataTable } from '../../../market-datatable' +import { debounce } from '$web-common/debounce' export interface Props { isLoadingCoinMarketData: boolean @@ -23,7 +23,11 @@ export interface Props { tradableAssets: BraveWallet.BlockchainToken[] } -const MarketView = (props: Props) => { +const defaultCurrency = 'usd' +const assetsRequestLimit = 250 +const assetsPerPage = 10 + +export const MarketView = (props: Props) => { const { isLoadingCoinMarketData, coinMarkets, tradableAssets, onFetchCoinMarkets } = props const [coinsMarketData, setCoinsMarketData] = React.useState([]) const [tableHeaders, setTableHeaders] = React.useState(marketDataTableHeaders) @@ -34,9 +38,6 @@ const MarketView = (props: Props) => { const [currentPage, setCurrentPage] = React.useState(1) const [searchTerm, setSearchTerm] = React.useState('') const [moreDataAvailable, setMoreDataAvailable] = React.useState(false) - const defaultCurrency = 'usd' - const assetsRequestLimit = 250 - const assetsPerPage = 20 const search = (query: string) => { const filtered = filterCoinMarkets(coinMarkets, currentFilter) @@ -69,8 +70,7 @@ const MarketView = (props: Props) => { if (filter === 'all') { return coins } else if (filter === 'tradable') { - const filtered = coins.filter(asset => tradableAssetsSymbols.includes(asset.symbol.toLowerCase())) - return filtered + return coins.filter(asset => tradableAssetsSymbols.includes(asset.symbol.toLowerCase())) } return [] @@ -85,7 +85,7 @@ const MarketView = (props: Props) => { setCurrentPage(nextPage) } - const onSort = (columnId: MarketDataTableColumnTypes, newSortOrder: SortOrder) => { + const onSort = React.useCallback((columnId: MarketDataTableColumnTypes, newSortOrder: SortOrder) => { const updatedTableHeaders = tableHeaders.map(header => { if (header.id === columnId) { return { @@ -103,7 +103,7 @@ const MarketView = (props: Props) => { setTableHeaders(updatedTableHeaders) setSortByColumnId(columnId) setSortOrder(newSortOrder) - } + }, []) React.useEffect(() => { if (coinMarkets.length === 0) { @@ -161,5 +161,3 @@ const MarketView = (props: Props) => { ) } - -export default MarketView diff --git a/components/brave_wallet_ui/components/market-datatable/index.tsx b/components/brave_wallet_ui/components/market-datatable/index.tsx index f9f50c7670a0..d9428fe2958e 100644 --- a/components/brave_wallet_ui/components/market-datatable/index.tsx +++ b/components/brave_wallet_ui/components/market-datatable/index.tsx @@ -1,5 +1,4 @@ import * as React from 'react' -import InfinitieScroll from 'react-infinite-scroll-component' import { getLocale } from '../../../common/locale' import { BraveWallet, MarketDataTableColumnTypes, SortOrder } from '../../constants/types' import Table, { Cell, Header, Row } from '../shared/datatable' @@ -15,9 +14,9 @@ import { formatPricePercentageChange, formatPriceWithAbbreviation } from '../../utils/format-prices' -import AssetNameAndIcon from '../asset-name-and-icon' -import AssetPriceChange from '../asset-price-change' -import { LoadIcon, LoadIconWrapper } from '../desktop/views/market/style' +import { AssetNameAndIcon } from '../asset-name-and-icon' +import { AssetPriceChange } from '../asset-price-change' +import useOnScreen from '../../common/hooks/on-screen' import { CoinGeckoText } from '../desktop/views/portfolio/style' export interface MarketDataHeader extends Header { @@ -33,8 +32,19 @@ export interface Props { onSort?: (column: MarketDataTableColumnTypes, newSortOrder: SortOrder) => void } -const MarketDataTable = (props: Props) => { +export const MarketDataTable = (props: Props) => { const { headers, coinMarketData, moreDataAvailable, showEmptyState, onFetchMoreMarketData, onSort } = props + const ref: any = React.useRef() + const onScreen = useOnScreen(ref, { + rootMargin: '0px', + threshold: 0 + }) + + React.useEffect(() => { + if (onScreen && moreDataAvailable) { + onFetchMoreMarketData() + } + }, [onScreen, moreDataAvailable]) const renderCells = (coinMarkDataItem: BraveWallet.CoinMarket) => { const { @@ -50,7 +60,7 @@ const MarketDataTable = (props: Props) => { } = coinMarkDataItem const formattedPrice = formatFiatAmountWithCommasAndDecimals(currentPrice.toString(), 'USD') - const formattedPercentageChange = formatPricePercentageChange(priceChangePercentage24h, true) + const formattedPercentageChange = formatPricePercentageChange(priceChangePercentage24h, 2, true) const formattedMarketCap = formatPriceWithAbbreviation(marketCap.toString(), 'USD', 1) const formattedVolume = formatPriceWithAbbreviation(totalVolume.toString(), 'USD', 1) const isDown = priceChange24h < 0 @@ -89,7 +99,7 @@ const MarketDataTable = (props: Props) => { {formattedVolume} // Line Chart Column - // Commented out because priceHisotry data is yet to be + // Commented out because priceHistory data is yet to be // available from the backend // // { return ( - - - - } - hasMore={moreDataAvailable} - endMessage={ - {getLocale('braveWalletPoweredByCoinGecko')} - } - style={{ - overflow: 'inherit' - }} + + - -
- {/* Empty state message */} - {showEmptyState && getLocale('braveWalletMarketDataNoAssetsFound')} -
-
-
+ {/* Empty state message */} + {showEmptyState && getLocale('braveWalletMarketDataNoAssetsFound')} + + + {!moreDataAvailable && {getLocale('braveWalletPoweredByCoinGecko')}} +
) } - -export default MarketDataTable diff --git a/components/brave_wallet_ui/components/shared/datatable/style.ts b/components/brave_wallet_ui/components/shared/datatable/style.ts index 379850fb7328..5a37a82cabc9 100644 --- a/components/brave_wallet_ui/components/shared/datatable/style.ts +++ b/components/brave_wallet_ui/components/shared/datatable/style.ts @@ -79,7 +79,6 @@ export const StyledTH = styled('th')>` ` export const StyledTR = styled('tr')>` - ${p => p.customStyle ? css` ${p.customStyle} diff --git a/components/brave_wallet_ui/utils/format-prices.ts b/components/brave_wallet_ui/utils/format-prices.ts index ca8dfb82dbb2..1465d46fb6e8 100644 --- a/components/brave_wallet_ui/utils/format-prices.ts +++ b/components/brave_wallet_ui/utils/format-prices.ts @@ -1,5 +1,4 @@ import BigNumber from 'bignumber.js' -import * as numeral from 'numeral' import { CurrencySymbols } from './currency-symbols' @@ -68,21 +67,35 @@ export const formatTokenAmountWithCommasAndDecimals = (value: string, symbol: st } export const formatPriceWithAbbreviation = (value: string, defaultCurrency: string, decimals: number = 2): string => { - if (!value) { + if (!value || isNaN(Number(value))) { return '' } - const decimalPlacesFormat = String('').padEnd(decimals, '0') const currencySymbol = CurrencySymbols[defaultCurrency] + const number = Number(value) + + const min = 1e3 // 1000 + + if (number >= min) { + const units = ['k', 'M', 'B', 'T'] + const order = Math.floor(Math.log(number) / Math.log(1000)) + const unitName = units[order - 1] + const fixedPointNumber = Number((number / 1000 ** order).toFixed(decimals)) + const formattedNumber = new Intl.NumberFormat(navigator.language).format(fixedPointNumber) + return currencySymbol + formattedNumber + unitName + } - return currencySymbol + numeral(value).format(`0.${decimalPlacesFormat}a`).toUpperCase() + return number.toFixed(decimals).toString() } -export const formatPricePercentageChange = (value: string | number, absoluteValue: true): string => { +export const formatPricePercentageChange = (value: number, decimals: number, absoluteValue = true): string => { if (!value) { return '' } - const formattedValue = numeral(value).format('0.00') + const formattedValue = new Intl.NumberFormat(navigator.language, { + minimumFractionDigits: decimals, + maximumFractionDigits: decimals + }).format(value) if (absoluteValue && formattedValue.startsWith('-')) { return formattedValue.substring(1) + '%' // remove the '-' sign @@ -90,3 +103,22 @@ export const formatPricePercentageChange = (value: string | number, absoluteValu return formattedValue + '%' } + +export const abbreviateNumber = (number: number, decimals: number = 2): string => { + const min = 1e3 // 1000 + + if (isNaN(number)) { + return number.toString() + } + + if (number >= min) { + const units = ['k', 'M', 'B', 'T'] + const order = Math.floor(Math.log(number) / Math.log(1000)) + const unitName = units[order - 1] + const num = Number((number / 1000 ** order).toFixed(decimals)) + + return num + unitName + } + + return number.toFixed(decimals).toString() +} diff --git a/package.json b/package.json index e130cedf4c04..c5e97007341e 100644 --- a/package.json +++ b/package.json @@ -346,11 +346,11 @@ "@glif/filecoin-wallet-provider": "2.0.0-alpha.14", "@ledgerhq/hw-app-eth": "6.24.1", "@ledgerhq/hw-transport-webhid": "6.24.1", + "@solana/web3.js": "1.36.0", "@types/jszip": "^3.1.6", "@types/parse-torrent": "^5.8.3", "@types/react-router-dom": "^5.1.9", "@types/webtorrent": "^0.109.0", - "@solana/web3.js": "1.36.0", "array-move": "^2.2.1", "bignumber.js": "^9.0.2", "bluebird": "^3.5.1", @@ -361,7 +361,6 @@ "numeral": "^2.0.6", "prettier-bytes": "^1.0.4", "qr-image": "^3.2.0", - "react-infinite-scroll-component": "^6.1.0", "react-router": "^5.2.1", "react-router-dom": "^5.3.0", "react-sortable-hoc": "^1.10.1", diff --git a/third_party/npm_numeral/README.chromium b/third_party/npm_numeral/README.chromium deleted file mode 100644 index f5ca6cb0f35e..000000000000 --- a/third_party/npm_numeral/README.chromium +++ /dev/null @@ -1,4 +0,0 @@ -Name: numeral -URL: https://github.com/adamwdraper/Numeral-js -License: MIT -License File: /brave/node_modules/numeral/LICENSE \ No newline at end of file diff --git a/third_party/npm_react-infinite-scroll-component/README.chromium b/third_party/npm_react-infinite-scroll-component/README.chromium deleted file mode 100644 index 91c459e1a6b1..000000000000 --- a/third_party/npm_react-infinite-scroll-component/README.chromium +++ /dev/null @@ -1,4 +0,0 @@ -Name: react-infinite-scroll-component -URL: https://github.com/ankeetmaini/react-infinite-scroll-component/ -License: MIT -License File: /brave/node_modules/react-infinite-scroll-component/license \ No newline at end of file