diff --git a/packages/connect/src/index.ts b/packages/connect/src/index.ts index 5c6e667a4..fbab08d8f 100644 --- a/packages/connect/src/index.ts +++ b/packages/connect/src/index.ts @@ -44,7 +44,15 @@ export { // Utils export { getConnectWallets } from './utils/getConnectWallets.js' -export { capitalize, compareAddress, formatAddress, formatDisplay, isEmailValid, truncateAtMiddle } from './utils/helpers.js' +export { + capitalize, + compareAddress, + formatAddress, + formatDisplay, + isEmailValid, + truncateAtIndex, + truncateAtMiddle +} from './utils/helpers.js' export { createNativeTokenBalance, getNativeTokenInfoByChainId } from './utils/tokens.js' export { getModalPositionCss } from './utils/styling.js' export { getNetwork, getNetworkBackgroundColor, getNetworkColor } from './utils/networks.js' diff --git a/packages/connect/src/styles.ts b/packages/connect/src/styles.ts index dcb258afa..0a444e95d 100644 --- a/packages/connect/src/styles.ts +++ b/packages/connect/src/styles.ts @@ -7,7 +7,6 @@ export const styles = String.raw` --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; --color-violet-600: oklch(54.1% 0.281 293.009); - --color-gray-500: oklch(55.1% 0.027 264.364); --color-black: #000; --color-white: #fff; --spacing: 0.25rem; @@ -427,6 +426,9 @@ export const styles = String.raw` .h-\[1px\] { height: 1px; } + .h-\[2px\] { + height: 2px; + } .h-\[52px\] { height: 52px; } @@ -686,6 +688,11 @@ export const styles = String.raw` .justify-self-center { justify-self: center; } + .truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } .overflow-hidden { overflow: hidden; } @@ -1156,9 +1163,6 @@ export const styles = String.raw` .text-black { color: var(--color-black); } - .text-gray-500 { - color: var(--color-gray-500); - } .text-info { color: var(--seq-color-info); } diff --git a/packages/connect/src/utils/helpers.ts b/packages/connect/src/utils/helpers.ts index 15bd9d734..c260fd964 100644 --- a/packages/connect/src/utils/helpers.ts +++ b/packages/connect/src/utils/helpers.ts @@ -107,6 +107,16 @@ export const truncateAtMiddle = (text: string, truncateAt: number) => { return finalText } +export const truncateAtIndex = (text: string, truncateIndex: number) => { + let finalText = text + + if (text.length >= truncateIndex) { + finalText = text.slice(0, truncateIndex) + '...' + text.slice(text.length - 4, text.length) + } + + return finalText +} + export const formatAddress = (text: string) => { return `0x${truncateAtMiddle(text?.substring(2) || '', 8)}` } diff --git a/packages/wallet-widget/src/components/CollectibleTileImage.tsx b/packages/wallet-widget/src/components/CollectibleTileImage.tsx deleted file mode 100644 index 34540e0b7..000000000 --- a/packages/wallet-widget/src/components/CollectibleTileImage.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { Card, Image } from '@0xsequence/design-system' -import React from 'react' - -interface CollectibleTileImageProps { - imageUrl?: string -} - -export const CollectibleTileImage = ({ imageUrl }: CollectibleTileImageProps) => { - return ( - - - - ) -} diff --git a/packages/wallet-widget/src/components/CopyButton.tsx b/packages/wallet-widget/src/components/CopyButton.tsx index 3775ffecd..954e849ab 100644 --- a/packages/wallet-widget/src/components/CopyButton.tsx +++ b/packages/wallet-widget/src/components/CopyButton.tsx @@ -1,4 +1,4 @@ -import { Button, CheckmarkIcon, CopyIcon } from '@0xsequence/design-system' +import { Button, CheckmarkIcon, CopyIcon, Text } from '@0xsequence/design-system' import { useClipboard } from '@0xsequence/hooks' import type { ComponentProps } from 'react' @@ -6,23 +6,32 @@ type ButtonProps = ComponentProps interface CopyButtonProps extends ButtonProps { text: string - buttonVariant: 'icon' | 'with-label' + includeLabel?: boolean + size?: 'xs' | 'sm' | 'md' | 'lg' } export const CopyButton = (props: CopyButtonProps) => { - const { buttonVariant = 'icon', text, size = 'xs', ...rest } = props + const { includeLabel = false, text, size = 'xs' } = props const [isCopied, setCopied] = useClipboard({ timeout: 4000 }) const label = isCopied ? 'Copied!' : 'Copy' return ( - + )} + + + ) +} diff --git a/packages/wallet-widget/src/components/SearchLists/CollectiblesList.tsx b/packages/wallet-widget/src/components/SearchLists/CollectiblesList.tsx deleted file mode 100644 index 2274d8947..000000000 --- a/packages/wallet-widget/src/components/SearchLists/CollectiblesList.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { SearchIcon, TextInput, TokenImage } from '@0xsequence/design-system' // Import TokenImage from design-system -import type { TokenBalance } from '@0xsequence/indexer' -import Fuse from 'fuse.js' -import { useMemo, useState } from 'react' - -import { useGetMoreBalances } from '../../hooks/index.js' -import type { TokenBalanceWithPrice } from '../../utils/index.js' -import { FilterButton } from '../Filter/FilterButton.js' -import { NetworkBadge } from '../NetworkBadge.js' - -import { CollectiblesTab } from './CollectiblesList/CollectiblesTab.js' - -export const CollectiblesList = ({ - tokenBalancesData, - isLoadingFirstPage, - onTokenClick, - enableFilters = true, - gridColumns = 2, - collectionHeaderInfo -}: { - tokenBalancesData: TokenBalance[] - isLoadingFirstPage: boolean - onTokenClick: (token: TokenBalanceWithPrice) => void - enableFilters?: boolean - gridColumns?: number - // Used only if a single collection is selected - collectionHeaderInfo?: { - logoURI?: string - name?: string - chainId: number - } -}) => { - const pageSize = 8 - - const [search, setSearch] = useState('') - - const collectibleBalancesUnordered = - tokenBalancesData?.filter(b => b.contractType === 'ERC721' || b.contractType === 'ERC1155') || [] - - const collectibleBalances = collectibleBalancesUnordered.sort((a, b) => { - return Number(b.balance) - Number(a.balance) - }) - - const collectibleBalancesWithPrice = collectibleBalances.map(balance => ({ - ...balance, - price: { - value: 0, - currency: 'USD' - } - })) - - const fuseOptions = { - threshold: 0.1, - ignoreLocation: true, - keys: [ - { - name: 'name', - getFn: (token: TokenBalance) => { - return token.tokenMetadata?.name || '' - } - }, - { - name: 'collectionName', - getFn: (token: TokenBalance) => { - return token.contractInfo?.name || '' - } - } - ] - } - - const fuse = useMemo(() => { - return new Fuse(collectibleBalancesWithPrice, fuseOptions) - }, [collectibleBalancesWithPrice]) - - const searchResults = useMemo(() => { - if (!search.trimStart()) { - return [] - } - return fuse.search(search).map(result => result.item) - }, [search, fuse]) - - const { - data: infiniteBalances, - fetchNextPage: fetchMoreBalances, - hasNextPage: hasMoreBalances, - isFetching: isFetchingMoreBalances - } = useGetMoreBalances(collectibleBalancesWithPrice, pageSize, { enabled: search.trim() === '' }) - - const { - data: infiniteSearchBalances, - fetchNextPage: fetchMoreSearchBalances, - hasNextPage: hasMoreSearchBalances, - isFetching: isFetchingMoreSearchBalances - } = useGetMoreBalances(searchResults, pageSize, { enabled: search.trim() !== '' }) - - return ( -
-
-
- setSearch(ev.target.value)} - placeholder="Search your wallet" - data-1p-ignore - /> -
- {enableFilters && } -
- - {collectionHeaderInfo && ( -
-
- -
- {collectionHeaderInfo.name && ( -

{collectionHeaderInfo.name}

- )} - - -

- {collectibleBalances.length} Unique Collectible - {collectibleBalances.length === 0 || collectibleBalances.length > 1 ? 's' : ''} -

-
- )} - -
- -
-
- ) -} diff --git a/packages/wallet-widget/src/components/SearchLists/CollectiblesList/CollectibleTile.tsx b/packages/wallet-widget/src/components/SearchLists/CollectiblesList/CollectibleTile.tsx index 794502a20..c4671ec7f 100644 --- a/packages/wallet-widget/src/components/SearchLists/CollectiblesList/CollectibleTile.tsx +++ b/packages/wallet-widget/src/components/SearchLists/CollectiblesList/CollectibleTile.tsx @@ -1,15 +1,15 @@ import { NetworkImage } from '@0xsequence/design-system' import { useGetTokenMetadata } from '@0xsequence/hooks' -import type { TokenBalanceWithPrice } from '../../../utils/index.js' -import { CollectibleTileImage } from '../../CollectibleTileImage.js' +import type { TokenBalanceWithDetails } from '../../../utils/index.js' +import { TokenTileImage } from '../../TokenTileImage.js' const NETWORK_IMAGE_SIZE = '15%' const NETWORK_IMAGE_OFFSET = '2%' interface CollectibleTileProps { - balance: TokenBalanceWithPrice - onTokenClick: (token: TokenBalanceWithPrice) => void + balance: TokenBalanceWithDetails + onTokenClick: (token: TokenBalanceWithDetails) => void } export const CollectibleTile = ({ balance, onTokenClick }: CollectibleTileProps) => { @@ -24,10 +24,11 @@ export const CollectibleTile = ({ balance, onTokenClick }: CollectibleTileProps) }) const imageUrl = tokenMetadata?.[0]?.image + const symbol = tokenMetadata?.[0]?.name return (
- + Promise hasMoreCollectibleBalances: boolean isFetchingMoreCollectibleBalances: boolean isFetchingInitialBalances: boolean - onTokenClick: (token: TokenBalanceWithPrice) => void - gridColumns?: number + onTokenClick: (token: TokenBalanceWithDetails) => void } export const CollectiblesTab: FC = ({ @@ -22,12 +22,11 @@ export const CollectiblesTab: FC = ({ hasMoreCollectibleBalances, isFetchingMoreCollectibleBalances, isFetchingInitialBalances, - onTokenClick, - gridColumns + onTokenClick }) => { return ( -
-
+
+
{isFetchingInitialBalances ? ( <> {Array(6) @@ -49,7 +48,7 @@ export const CollectiblesTab: FC = ({ )}
{(!displayedCollectibleBalances || displayedCollectibleBalances.length === 0) && !isFetchingMoreCollectibleBalances && ( - No Collectibles Found + )} {isFetchingMoreCollectibleBalances && }
diff --git a/packages/wallet-widget/src/components/SearchLists/CollectionsList/CollectionTile.tsx b/packages/wallet-widget/src/components/SearchLists/CollectionsList/CollectionTile.tsx new file mode 100644 index 000000000..81f11e06d --- /dev/null +++ b/packages/wallet-widget/src/components/SearchLists/CollectionsList/CollectionTile.tsx @@ -0,0 +1,37 @@ +import { NetworkImage } from '@0xsequence/design-system' +import type { ContractInfo } from '@0xsequence/indexer' + +import { TokenTileImage } from '../../TokenTileImage.js' + +const NETWORK_IMAGE_SIZE = '15%' +const NETWORK_IMAGE_OFFSET = '2%' + +interface CollectionTileProps { + balance: ContractInfo + onTokenClick: (token: ContractInfo) => void +} + +export const CollectionTile = ({ balance, onTokenClick }: CollectionTileProps) => { + const onClick = () => { + onTokenClick(balance) + } + + const imageUrl = balance.logoURI + const symbol = balance.name + + return ( +
+ + +
+ ) +} diff --git a/packages/wallet-widget/src/components/SearchLists/CollectionsList/CollectionsTab.tsx b/packages/wallet-widget/src/components/SearchLists/CollectionsList/CollectionsTab.tsx new file mode 100644 index 000000000..530da553e --- /dev/null +++ b/packages/wallet-widget/src/components/SearchLists/CollectionsList/CollectionsTab.tsx @@ -0,0 +1,56 @@ +import { Skeleton, Spinner } from '@0xsequence/design-system' +import type { ContractInfo } from '@0xsequence/indexer' +import type { FC } from 'react' + +import { InfiniteScroll } from '../../InfiniteScroll.js' +import { NoResults } from '../../NoResults.js' + +import { CollectionTile } from './CollectionTile.js' + +interface CollectionsTabProps { + displayedCollectibleBalances: ContractInfo[] | undefined + fetchMoreCollectibleBalances: () => Promise + hasMoreCollectibleBalances: boolean + isFetchingMoreCollectibleBalances: boolean + isFetchingInitialBalances: boolean + onTokenClick: (token: ContractInfo) => void +} + +export const CollectionsTab: FC = ({ + displayedCollectibleBalances, + fetchMoreCollectibleBalances, + hasMoreCollectibleBalances, + isFetchingMoreCollectibleBalances, + isFetchingInitialBalances, + onTokenClick +}) => { + return ( +
+
+ {isFetchingInitialBalances ? ( + <> + {Array(6) + .fill(null) + .map((_, i) => ( + + ))} + + ) : ( + <> + {displayedCollectibleBalances && displayedCollectibleBalances.length > 0 && ( + fetchMoreCollectibleBalances()} hasMore={hasMoreCollectibleBalances}> + {displayedCollectibleBalances?.map((balance, index) => { + return + })} + + )} + + )} +
+ {(!displayedCollectibleBalances || displayedCollectibleBalances.length === 0) && !isFetchingMoreCollectibleBalances && ( + + )} + {isFetchingMoreCollectibleBalances && } +
+ ) +} diff --git a/packages/wallet-widget/src/components/SearchLists/GeneralList.tsx b/packages/wallet-widget/src/components/SearchLists/GeneralList.tsx new file mode 100644 index 000000000..a4369dab5 --- /dev/null +++ b/packages/wallet-widget/src/components/SearchLists/GeneralList.tsx @@ -0,0 +1,510 @@ +import { compareAddress, getNativeTokenInfoByChainId, useWallets } from '@0xsequence/connect' +import { Divider, SearchIcon, TabsContent, TabsHeader, TabsPrimitive, Text, TextInput } from '@0xsequence/design-system' +import { useGetCoinPrices, useGetExchangeRate, useGetTransactionHistorySummary } from '@0xsequence/hooks' +import type { ContractInfo, Transaction, TxnTransfer } from '@0xsequence/indexer' +import { ethers } from 'ethers' +import Fuse from 'fuse.js' +import { useObservable } from 'micro-observables' +import { useEffect, useMemo } from 'react' +import { useConfig } from 'wagmi' + +import { useGetAllTokensDetails, useGetMoreBalances, useNavigation, useSettings } from '../../hooks/index.js' +import { useGetAllCollections } from '../../hooks/useGetAllCollections.js' +import { useNavigationHeader } from '../../hooks/useNavigationHeader.js' +import { computeBalanceFiat, type TokenBalanceWithDetails } from '../../utils/index.js' +import { FilterMenu } from '../Filter/FilterMenu.js' +import { TransactionHistoryList } from '../TransactionHistoryList/index.js' + +import { CollectiblesTab } from './CollectiblesList/CollectiblesTab.js' +import { CollectionsTab } from './CollectionsList/CollectionsTab.js' +import { CoinsTab } from './TokenList/CoinsTab.js' + +export const GeneralList = ({ variant = 'default' }: { variant?: 'default' | 'send' }) => { + const { setNavigation } = useNavigation() + const { chains } = useConfig() + const { + fiatCurrency, + allNetworks, + hideUnlistedTokens, + selectedNetworksObservable, + selectedWalletsObservable, + showCollectionsObservable + } = useSettings() + const { wallets } = useWallets() + const { search, selectedTab, setSearch, setSelectedTab } = useNavigationHeader() + + const selectedNetworks = useObservable(selectedNetworksObservable) + const selectedWallets = useObservable(selectedWalletsObservable) + const showCollections = useObservable(showCollectionsObservable) + + useEffect(() => { + return () => { + setSearch('') + } + }, [variant]) + + const activeWallet = wallets.find(wallet => wallet.isActive) + + const TOKEN_PAGE_SIZE = 10 + const COLLECTIBLE_PAGE_SIZE = 9 + const COLLECTION_PAGE_SIZE = 9 + + const { data: tokenBalancesData = [], isLoading: isLoadingTokenBalances } = useGetAllTokensDetails({ + accountAddresses: variant === 'default' ? selectedWallets.map(wallet => wallet.address) : [activeWallet?.address || ''], + chainIds: variant === 'default' ? selectedNetworks : allNetworks, + hideUnlistedTokens + }) + + const { data: collectionsData = [] } = useGetAllCollections({ + accountAddresses: variant === 'default' ? selectedWallets.map(wallet => wallet.address) : [activeWallet?.address || ''], + chainIds: variant === 'default' ? selectedNetworks : allNetworks, + hideUnlistedTokens + }) + + const collectionsDataWithDetails = collectionsData.map(collection => ({ + ...collection, + _type: 'collection' as const + })) + + const coinBalancesUnordered = + tokenBalancesData?.filter(b => b.contractType === 'ERC20' || compareAddress(b.contractAddress, ethers.ZeroAddress)) || [] + + const { data: coinPrices = [], isLoading: isLoadingCoinPrices } = useGetCoinPrices( + coinBalancesUnordered.map(token => ({ + chainId: token.chainId, + contractAddress: token.contractAddress + })) + ) + + const { data: conversionRate = 1, isLoading: isLoadingConversionRate } = useGetExchangeRate(fiatCurrency.symbol) + + const coinBalances = coinBalancesUnordered.sort((a, b) => { + const fiatA = computeBalanceFiat({ + balance: a, + prices: coinPrices, + conversionRate, + decimals: a.contractInfo?.decimals || 18 + }) + const fiatB = computeBalanceFiat({ + balance: b, + prices: coinPrices, + conversionRate, + decimals: b.contractInfo?.decimals || 18 + }) + return Number(fiatB) - Number(fiatA) + }) + + const coinBalancesWithPrice = coinBalances.map(balance => { + const matchingPrice = coinPrices.find(price => { + const isSameChainAndAddress = + price.token.chainId === balance.chainId && price.token.contractAddress === balance.contractAddress + + const isTokenIdMatch = + price.token.tokenId === balance.tokenID || !(balance.contractType === 'ERC721' || balance.contractType === 'ERC1155') + + return isSameChainAndAddress && isTokenIdMatch + }) + + const priceValue = (matchingPrice?.price?.value || 0) * conversionRate + const priceCurrency = fiatCurrency.symbol + + return { + ...balance, + price: { value: priceValue, currency: priceCurrency }, + _type: 'coin' as const + } + }) + + const isLoading = isLoadingTokenBalances || isLoadingCoinPrices || isLoadingConversionRate + + const collectibleBalancesUnordered = + tokenBalancesData?.filter(b => b.contractType === 'ERC721' || b.contractType === 'ERC1155') || [] + + const collectibleBalances = collectibleBalancesUnordered.sort((a, b) => { + return Number(b.balance) - Number(a.balance) + }) + + const collectibleBalancesWithPrice = collectibleBalances.map(balance => ({ + ...balance, + price: { + value: 0, + currency: 'USD' + }, + _type: 'collectible' as const + })) + + const fuseOptions = { + threshold: 0.1, + ignoreLocation: true, + keys: [ + // Coin: Name + { + name: 'coinName', + getFn: (item: any) => { + if (item._type === 'coin') { + if (compareAddress(item.contractAddress, ethers.ZeroAddress)) { + const nativeTokenInfo = getNativeTokenInfoByChainId(item.chainId, chains) + return nativeTokenInfo.name + } + return item.contractInfo?.name || 'Unknown' + } + } + }, + // Collectible: Name + { + name: 'collectibleName', + getFn: (item: any) => { + if (item._type === 'collectible') { + return item.tokenMetadata?.name || '' + } + } + }, + // Collectible: Collection Name + { + name: 'collectionName', + getFn: (item: any) => { + if (item._type === 'collectible') { + return item.contractInfo?.name || '' + } + return '' + } + }, + // Collection: Name + { + name: 'collectionName', + getFn: (item: any) => { + if (item._type === 'collection') { + return item.contractInfo?.name || '' + } + return '' + } + }, + // Transaction: Contract Name + { + name: 'contractName', + getFn: (item: any) => { + if (item._type === 'transaction') { + return item.transfers?.map((transfer: TxnTransfer) => transfer.contractInfo?.name).join(', ') || '' + } + return '' + } + }, + // Transaction: Token Symbol + { + name: 'tokenSymbol', + getFn: (item: any) => { + if (item._type === 'transaction') { + const hasNativeToken = item.transfers?.some((transfer: TxnTransfer) => + compareAddress(transfer.contractInfo?.address || '', ethers.ZeroAddress) + ) + if (hasNativeToken) { + const nativeTokenInfo = getNativeTokenInfoByChainId(item.chainId, chains) + return nativeTokenInfo.symbol + } + return item.transfers?.map((transfer: TxnTransfer) => transfer.contractInfo?.symbol).join(', ') || '' + } + return '' + } + }, + // Transaction: Collectible Name + { + name: 'collectibleName', + getFn: (item: any) => { + if (item._type === 'transaction') { + return ( + item.transfers + ?.map((transfer: TxnTransfer) => { + return Object.values(transfer.tokenMetadata || {}) + .map(tokenMetadata => tokenMetadata?.name) + .join(', ') + }) + .join(', ') || '' + ) + } + return '' + } + }, + // Transaction: Date + { + name: 'date', + getFn: (item: any) => { + if (item._type === 'transaction') { + const date = new Date(item.timestamp) + const day = date.getDate() + const month = date.toLocaleString('en-US', { month: 'long' }) + const year = date.getFullYear() + return ` + ${day} ${month} ${year} + ${day} ${year} ${month} + ${month} ${day} ${year} + ${month} ${year} ${day} + ${year} ${day} ${month} + ${year} ${month} ${day} + ` + } + return '' + } + } + ] + } + + const { data: transactionHistoryData = [], isLoading: isLoadingTransactionHistory } = useGetTransactionHistorySummary({ + accountAddresses: selectedWallets.map(wallet => wallet.address), + chainIds: selectedNetworks + }) + + const transactionHistory = transactionHistoryData.map(transaction => ({ + ...transaction, + _type: 'transaction' as const + })) + + const combinedBalances = [ + ...coinBalancesWithPrice, + ...collectibleBalancesWithPrice, + ...collectionsDataWithDetails, + ...transactionHistory + ] + + const fuse = useMemo(() => { + return new Fuse(combinedBalances, fuseOptions) + }, [combinedBalances]) + + const searchResults = useMemo(() => { + if (!search.trimStart()) { + return [] + } + return fuse.search(search).map(result => result.item) + }, [search, fuse]) + + // infinite scroll for tokens + const { + data: infiniteBalancesTokens, + fetchNextPage: fetchMoreBalancesTokens, + hasNextPage: hasMoreBalancesTokens, + isFetching: isFetchingMoreBalancesTokens + } = useGetMoreBalances(coinBalancesWithPrice, TOKEN_PAGE_SIZE, { enabled: search.trim() === '' }) + + const { + data: infiniteSearchBalancesTokens, + fetchNextPage: fetchMoreSearchBalancesTokens, + hasNextPage: hasMoreSearchBalancesTokens, + isFetching: isFetchingMoreSearchBalancesTokens + } = useGetMoreBalances(searchResults.filter(item => item._type === 'coin') as TokenBalanceWithDetails[], TOKEN_PAGE_SIZE, { + enabled: search.trim() !== '' + }) + + // infinite scroll for collectibles + + const { + data: infiniteBalancesCollectibles, + fetchNextPage: fetchMoreBalancesCollectibles, + hasNextPage: hasMoreBalancesCollectibles, + isFetching: isFetchingMoreBalancesCollectibles + } = useGetMoreBalances(collectibleBalancesWithPrice, COLLECTIBLE_PAGE_SIZE, { enabled: search.trim() === '' }) + + const { + data: infiniteSearchBalancesCollectibles, + fetchNextPage: fetchMoreSearchBalancesCollectibles, + hasNextPage: hasMoreSearchBalancesCollectibles, + isFetching: isFetchingMoreSearchBalancesCollectibles + } = useGetMoreBalances( + searchResults.filter(item => item._type === 'collectible') as TokenBalanceWithDetails[], + COLLECTIBLE_PAGE_SIZE, + { + enabled: search.trim() !== '' + } + ) + + // infinite scroll for collections + + const { + data: infiniteBalancesCollections, + fetchNextPage: fetchMoreBalancesCollections, + hasNextPage: hasMoreBalancesCollections, + isFetching: isFetchingMoreBalancesCollections + } = useGetMoreBalances(collectionsDataWithDetails, COLLECTION_PAGE_SIZE, { enabled: search.trim() === '' }) + + const { + data: infiniteSearchBalancesCollections, + fetchNextPage: fetchMoreSearchBalancesCollections, + hasNextPage: hasMoreSearchBalancesCollections, + isFetching: isFetchingMoreSearchBalancesCollections + } = useGetMoreBalances( + searchResults.filter(item => item._type === 'collection'), + COLLECTION_PAGE_SIZE, + { + enabled: search.trim() !== '' + } + ) + + const handleTokenClickDefault = (balance: TokenBalanceWithDetails) => { + setNavigation({ + location: 'coin-details', + params: { + contractAddress: balance.contractAddress, + chainId: balance.chainId, + accountAddress: balance.accountAddress + } + }) + } + + const handleTokenClickSend = (token: TokenBalanceWithDetails) => { + setNavigation({ + location: 'send-coin', + params: { + chainId: token.chainId, + contractAddress: token.contractAddress + } + }) + } + + const handleCollectibleClickDefault = (balance: TokenBalanceWithDetails) => { + setNavigation({ + location: 'collectible-details', + params: { + contractAddress: balance.contractAddress, + chainId: balance.chainId, + tokenId: balance.tokenID || '', + accountAddress: balance.accountAddress + } + }) + } + + const handleCollectibleClickSend = (token: TokenBalanceWithDetails) => { + setNavigation({ + location: 'send-collectible', + params: { + chainId: token.chainId, + contractAddress: token.contractAddress, + tokenId: token.tokenID || '' + } + }) + } + + const handleCollectionClick = (collection: ContractInfo) => { + // setNavigation({ + // location: 'collection-details', + // params: { + // contractAddress: collection.address, + // chainId: collection.chainId + // } + // }) + } + + return ( +
+ setSelectedTab(value as 'tokens' | 'collectibles' | 'history')} + > + {variant === 'default' ? ( +
+ + + + Coins + + {selectedTab === 'tokens' &&
} + + + + Collectibles + + {selectedTab === 'collectibles' &&
} + + + + Transactions + + {selectedTab === 'history' &&
} + + + +
+ ) : ( +
+ +
+ setSearch(ev.target.value)} + placeholder="Search your wallet" + /> +
+
+ )} + +
+ + + +
+ +
+
+ +
+ {showCollections ? ( + + ) : ( + + )} +
+
+ +
+ item._type === 'transaction') as unknown as Transaction[]) + : transactionHistory + } + isLoading={isLoadingTransactionHistory} + isFetchingNextPage={false} + /> +
+
+
+ +
+ ) +} diff --git a/packages/wallet-widget/src/components/SearchLists/TokenList.tsx b/packages/wallet-widget/src/components/SearchLists/TokenList.tsx deleted file mode 100644 index b5f19c064..000000000 --- a/packages/wallet-widget/src/components/SearchLists/TokenList.tsx +++ /dev/null @@ -1,157 +0,0 @@ -import { compareAddress, getNativeTokenInfoByChainId } from '@0xsequence/connect' -import { SearchIcon, TextInput } from '@0xsequence/design-system' -import { useGetCoinPrices, useGetExchangeRate } from '@0xsequence/hooks' -import type { TokenBalance } from '@0xsequence/indexer' -import { ethers } from 'ethers' -import Fuse from 'fuse.js' -import { useMemo, useState } from 'react' -import { useConfig } from 'wagmi' - -import { useGetMoreBalances, useSettings } from '../../hooks/index.js' -import { computeBalanceFiat, type TokenBalanceWithPrice } from '../../utils/index.js' -import { FilterButton } from '../Filter/FilterButton.js' - -import { CoinsTab } from './TokenList/CoinsTab.js' - -export const TokenList = ({ - tokenBalancesData, - isLoadingFirstPage, - onTokenClick, - includeUserAddress = false, - enableFilters = true -}: { - tokenBalancesData: TokenBalance[] - isLoadingFirstPage: boolean - onTokenClick: (token: TokenBalanceWithPrice) => void - enableFilters?: boolean - includeUserAddress?: boolean -}) => { - const pageSize = 10 - - const { chains } = useConfig() - const { fiatCurrency } = useSettings() - - const [search, setSearch] = useState('') - - const coinBalancesUnordered = - tokenBalancesData?.filter(b => b.contractType === 'ERC20' || compareAddress(b.contractAddress, ethers.ZeroAddress)) || [] - - const { data: coinPrices = [], isLoading: isLoadingCoinPrices } = useGetCoinPrices( - coinBalancesUnordered.map(token => ({ - chainId: token.chainId, - contractAddress: token.contractAddress - })) - ) - - const { data: conversionRate = 1, isLoading: isLoadingConversionRate } = useGetExchangeRate(fiatCurrency.symbol) - - const coinBalances = coinBalancesUnordered.sort((a, b) => { - const fiatA = computeBalanceFiat({ - balance: a, - prices: coinPrices, - conversionRate, - decimals: a.contractInfo?.decimals || 18 - }) - const fiatB = computeBalanceFiat({ - balance: b, - prices: coinPrices, - conversionRate, - decimals: b.contractInfo?.decimals || 18 - }) - return Number(fiatB) - Number(fiatA) - }) - - const coinBalancesWithPrices = coinBalances.map(balance => { - const matchingPrice = coinPrices.find(price => { - const isSameChainAndAddress = - price.token.chainId === balance.chainId && price.token.contractAddress === balance.contractAddress - - const isTokenIdMatch = - price.token.tokenId === balance.tokenID || !(balance.contractType === 'ERC721' || balance.contractType === 'ERC1155') - - return isSameChainAndAddress && isTokenIdMatch - }) - - const priceValue = (matchingPrice?.price?.value || 0) * conversionRate - const priceCurrency = fiatCurrency.symbol - - return { - ...balance, - price: { value: priceValue, currency: priceCurrency } - } - }) - - const isLoading = isLoadingFirstPage || isLoadingCoinPrices || isLoadingConversionRate - - const fuseOptions = { - threshold: 0.1, - ignoreLocation: true, - keys: [ - { - name: 'name', - getFn: (token: TokenBalance) => { - if (compareAddress(token.contractAddress, ethers.ZeroAddress)) { - const nativeTokenInfo = getNativeTokenInfoByChainId(token.chainId, chains) - return nativeTokenInfo.name - } - return token.contractInfo?.name || 'Unknown' - } - } - ] - } - - const fuse = useMemo(() => { - return new Fuse(coinBalancesWithPrices, fuseOptions) - }, [coinBalancesWithPrices]) - - const searchResults = useMemo(() => { - if (!search.trimStart()) { - return [] - } - return fuse.search(search).map(result => result.item) - }, [search, fuse]) - - const { - data: infiniteBalances, - fetchNextPage: fetchMoreBalances, - hasNextPage: hasMoreBalances, - isFetching: isFetchingMoreBalances - } = useGetMoreBalances(coinBalancesWithPrices, pageSize, { enabled: search.trim() === '' }) - - const { - data: infiniteSearchBalances, - fetchNextPage: fetchMoreSearchBalances, - hasNextPage: hasMoreSearchBalances, - isFetching: isFetchingMoreSearchBalances - } = useGetMoreBalances(searchResults, pageSize, { enabled: search.trim() !== '' }) - - return ( -
-
-
- setSearch(ev.target.value)} - placeholder="Search your wallet" - data-1p-ignore - /> -
- {enableFilters && } -
-
- -
-
- ) -} diff --git a/packages/wallet-widget/src/components/SearchLists/TokenList/CoinRow.tsx b/packages/wallet-widget/src/components/SearchLists/TokenList/CoinRow.tsx index a08064928..64900aae3 100644 --- a/packages/wallet-widget/src/components/SearchLists/TokenList/CoinRow.tsx +++ b/packages/wallet-widget/src/components/SearchLists/TokenList/CoinRow.tsx @@ -5,14 +5,14 @@ import { useChains } from 'wagmi' import { useSettings } from '../../../hooks/index.js' import { formatTokenInfo } from '../../../utils/formatBalance.js' -import type { TokenBalanceWithPrice } from '../../../utils/tokens.js' +import type { TokenBalanceWithDetails } from '../../../utils/tokens.js' import { TokenImageCustom } from '../../Filter/TokenImageCustom.js' import { ListCardNav } from '../../ListCard/ListCardNav.js' interface BalanceItemProps { - balance: TokenBalanceWithPrice + balance: TokenBalanceWithDetails includeUserAddress?: boolean - onTokenClick: (token: TokenBalanceWithPrice) => void + onTokenClick: (token: TokenBalanceWithDetails) => void } export const CoinRow = ({ balance, onTokenClick, includeUserAddress = false }: BalanceItemProps) => { diff --git a/packages/wallet-widget/src/components/SearchLists/TokenList/CoinsTab.tsx b/packages/wallet-widget/src/components/SearchLists/TokenList/CoinsTab.tsx index 904b9603d..db0264945 100644 --- a/packages/wallet-widget/src/components/SearchLists/TokenList/CoinsTab.tsx +++ b/packages/wallet-widget/src/components/SearchLists/TokenList/CoinsTab.tsx @@ -1,18 +1,19 @@ -import { Skeleton, Spinner, Text } from '@0xsequence/design-system' +import { Skeleton, Spinner } from '@0xsequence/design-system' import type { FC } from 'react' -import type { TokenBalanceWithPrice } from '../../../utils/tokens.js' +import type { TokenBalanceWithDetails } from '../../../utils/tokens.js' import { InfiniteScroll } from '../../InfiniteScroll.js' +import { NoResults } from '../../NoResults.js' import { CoinRow } from './CoinRow.js' interface CoinsTabProps { - displayedCoinBalances: TokenBalanceWithPrice[] | undefined + displayedCoinBalances: TokenBalanceWithDetails[] | undefined fetchMoreCoinBalances: () => Promise hasMoreCoinBalances: boolean isFetchingMoreCoinBalances: boolean isFetchingInitialBalances: boolean - onTokenClick: (token: TokenBalanceWithPrice) => void + onTokenClick: (token: TokenBalanceWithDetails) => void includeUserAddress?: boolean } @@ -38,9 +39,7 @@ export const CoinsTab: FC = ({ ) : ( <> - {(!displayedCoinBalances || displayedCoinBalances.length === 0) && !isFetchingMoreCoinBalances ? ( - No Coins Found - ) : ( + {displayedCoinBalances && displayedCoinBalances.length > 0 && ( fetchMoreCoinBalances()} hasMore={hasMoreCoinBalances}> {displayedCoinBalances?.map((balance, index) => { return ( @@ -51,8 +50,9 @@ export const CoinsTab: FC = ({ )} )} - {isFetchingMoreCoinBalances && }
+ {(!displayedCoinBalances || displayedCoinBalances.length === 0) && !isFetchingMoreCoinBalances && } + {isFetchingMoreCoinBalances && }
) } diff --git a/packages/wallet-widget/src/components/SearchLists/index.ts b/packages/wallet-widget/src/components/SearchLists/index.ts index db2e0c46e..34a439419 100644 --- a/packages/wallet-widget/src/components/SearchLists/index.ts +++ b/packages/wallet-widget/src/components/SearchLists/index.ts @@ -1,2 +1 @@ -export * from './TokenList.js' -export * from './CollectiblesList.js' +export * from './GeneralList.js' diff --git a/packages/wallet-widget/src/components/Select/ConnectedWalletSelect.tsx b/packages/wallet-widget/src/components/Select/ConnectedWalletSelect.tsx new file mode 100644 index 000000000..04e12ba86 --- /dev/null +++ b/packages/wallet-widget/src/components/Select/ConnectedWalletSelect.tsx @@ -0,0 +1,59 @@ +import { useWallets } from '@0xsequence/connect' +import { ChevronUpDownIcon, Text } from '@0xsequence/design-system' +import { useState } from 'react' + +import { SelectWalletRow } from './SelectWalletRow.js' +import { SlideupDrawer } from './SlideupDrawer.js' + +const WALLET_SELECT_HEIGHT = '60px' + +export const ConnectedWalletSelect = ({ + selectedWallet, + onClick +}: { + selectedWallet: string + onClick: (address: string) => void +}) => { + const { wallets } = useWallets() + const [isOpen, setIsOpen] = useState(false) + + const activeWallet = wallets.find(wallet => wallet.isActive) + + const allButActiveWallet = wallets.filter(wallet => wallet.address !== activeWallet?.address) + + const handleClick = (address: string) => { + onClick(address) + setIsOpen(false) + } + + return ( +
setIsOpen(true)} + > +
+ + Select Connected Wallet + +
+ + + {isOpen && ( + setIsOpen(false)}> +
+ {allButActiveWallet.map(wallet => ( + setIsOpen(false)} + /> + ))} +
+
+ )} +
+ ) +} diff --git a/packages/wallet-widget/src/components/Select/NetworkSelect.tsx b/packages/wallet-widget/src/components/Select/NetworkSelect.tsx index 69140d0a2..f2e6d664a 100644 --- a/packages/wallet-widget/src/components/Select/NetworkSelect.tsx +++ b/packages/wallet-widget/src/components/Select/NetworkSelect.tsx @@ -2,7 +2,7 @@ import { cardVariants, ChevronUpDownIcon, cn, NetworkImage, Text } from '@0xsequ import { useState } from 'react' import { useChainId, useChains, useSwitchChain } from 'wagmi' -import { NetworkRow } from '../Filter/NetworkRow.js' +import { ListCardNetwork } from '../ListCard/ListCardNetwork.js' import { SlideupDrawer } from './SlideupDrawer.js' @@ -37,10 +37,10 @@ export const NetworkSelect = () => { {isOpen && ( - setIsOpen(false)}> + setIsOpen(false)}>
{chains.map(chain => ( - - +
- {formatAddress(wallet.address)} - e.stopPropagation()} /> +
{formatAddress(wallet.address)}
+ e.stopPropagation()} />
diff --git a/packages/wallet-widget/src/components/Select/SlideupDrawer.tsx b/packages/wallet-widget/src/components/Select/SlideupDrawer.tsx index b98b998c9..7ae21e71b 100644 --- a/packages/wallet-widget/src/components/Select/SlideupDrawer.tsx +++ b/packages/wallet-widget/src/components/Select/SlideupDrawer.tsx @@ -1,4 +1,4 @@ -import { Button, cardVariants, ChevronLeftIcon, cn, Divider, Text } from '@0xsequence/design-system' +import { Divider } from '@0xsequence/design-system' import { motion } from 'motion/react' import { useContext, useEffect, useState } from 'react' import ReactDOM from 'react-dom' @@ -7,21 +7,15 @@ import { WALLET_WIDTH } from '../../constants/index.js' import { WalletContentRefContext } from '../../contexts/WalletContentRef.js' export const SlideupDrawer = ({ - label, + header, children, - buttonLabel, - dragHandleWidth = 64, - onClose, - handleButtonPress, - onBackPress + footer, + onClose }: { - label: string + header: React.ReactNode children: React.ReactNode - buttonLabel?: string - dragHandleWidth?: number + footer?: React.ReactNode onClose: () => void - handleButtonPress?: () => void - onBackPress?: () => void }) => { const [walletContentHeight, setWalletContentHeight] = useState(0) const walletContentRef = useContext(WalletContentRefContext) @@ -85,49 +79,17 @@ export const SlideupDrawer = ({ onClick={e => e.stopPropagation()} >
-
- {onBackPress && ( - - )} -
-
-
-
-
- - {label} - -
-
- {onBackPress &&
} -
+
{header}
{children}
- {buttonLabel && ( + {footer && ( <> -
-
- - {buttonLabel} - -
-
+
{footer}
)}
diff --git a/packages/wallet-widget/src/components/Select/WalletSelect.tsx b/packages/wallet-widget/src/components/Select/WalletSelect.tsx index 6064dad56..933e39265 100644 --- a/packages/wallet-widget/src/components/Select/WalletSelect.tsx +++ b/packages/wallet-widget/src/components/Select/WalletSelect.tsx @@ -1,5 +1,5 @@ -import { useWallets } from '@0xsequence/connect' -import { ChevronUpDownIcon, Text } from '@0xsequence/design-system' +import { truncateAtIndex, useWallets } from '@0xsequence/connect' +import { ChevronUpDownIcon, GradientAvatar, Text } from '@0xsequence/design-system' import { useState } from 'react' import { SelectWalletRow } from './SelectWalletRow.js' @@ -13,7 +13,7 @@ export const WalletSelect = ({ selectedWallet, onClick }: { selectedWallet: stri const activeWallet = wallets.find(wallet => wallet.isActive) - const allButActiveWallet = wallets.filter(wallet => wallet.address !== activeWallet?.address) + const walletOptions = wallets const handleClick = (address: string) => { onClick(address) @@ -22,21 +22,27 @@ export const WalletSelect = ({ selectedWallet, onClick }: { selectedWallet: stri return (
setIsOpen(true)} >
- Select Connected Wallet + Wallet +
+ + + {truncateAtIndex(activeWallet?.address || '', 21)} + +
{isOpen && ( - setIsOpen(false)}> + setIsOpen(false)}>
- {allButActiveWallet.map(wallet => ( + {walletOptions.map(wallet => ( {showSquareImage ? (
- +
) : ( diff --git a/packages/wallet-widget/src/components/SequenceWalletProvider/ProviderComponents/FiatWalletsMapProvider.tsx b/packages/wallet-widget/src/components/SequenceWalletProvider/ProviderComponents/FiatWalletsMapProvider.tsx index a541949ab..e93e758e2 100644 --- a/packages/wallet-widget/src/components/SequenceWalletProvider/ProviderComponents/FiatWalletsMapProvider.tsx +++ b/packages/wallet-widget/src/components/SequenceWalletProvider/ProviderComponents/FiatWalletsMapProvider.tsx @@ -10,13 +10,14 @@ import { computeBalanceFiat } from '../../../utils/index.js' // Define the provider component export const FiatWalletsMapProvider = ({ children }: { children: ReactNode }) => { const { wallets } = useWallets() - const { selectedNetworks, hideUnlistedTokens, fiatCurrency } = useSettings() + const { allNetworks, hideUnlistedTokens, fiatCurrency } = useSettings() const [fiatWalletsMap, setFiatWalletsMap] = useState([]) + const [totalFiatValue, setTotalFiatValue] = useState('0') const { data: tokenBalancesData, isLoading: isTokenBalancesLoading } = useGetAllTokensDetails({ accountAddresses: wallets.map(wallet => wallet.address), - chainIds: selectedNetworks, + chainIds: allNetworks, hideUnlistedTokens }) @@ -64,9 +65,11 @@ export const FiatWalletsMapProvider = ({ children }: { children: ReactNode }) => if (JSON.stringify(newFiatWalletsMap) !== JSON.stringify(fiatWalletsMap)) { setFiatWalletsMap(newFiatWalletsMap) + const totalFiatValue = newFiatWalletsMap.reduce((acc, wallet) => acc + Number(wallet.fiatValue), 0).toFixed(2) + setTotalFiatValue(totalFiatValue) } } }, [coinBalancesUnordered, coinPrices, conversionRate]) - return {children} + return {children} } diff --git a/packages/wallet-widget/src/components/SequenceWalletProvider/ProviderComponents/SwapProvider.tsx b/packages/wallet-widget/src/components/SequenceWalletProvider/ProviderComponents/SwapProvider.tsx index 49b891771..80a74fc88 100644 --- a/packages/wallet-widget/src/components/SequenceWalletProvider/ProviderComponents/SwapProvider.tsx +++ b/packages/wallet-widget/src/components/SequenceWalletProvider/ProviderComponents/SwapProvider.tsx @@ -8,7 +8,7 @@ import { useAccount, useChainId, useChains, usePublicClient, useWalletClient } f import { SwapContextProvider } from '../../../contexts/Swap.js' import { useNavigation } from '../../../hooks/index.js' -import type { TokenBalanceWithPrice } from '../../../utils/index.js' +import type { TokenBalanceWithDetails } from '../../../utils/index.js' export const SwapProvider = ({ children }: { children: ReactNode }) => { const toast = useToast() @@ -17,8 +17,8 @@ export const SwapProvider = ({ children }: { children: ReactNode }) => { const apiClient = useAPIClient() const connectedChainId = useChainId() const chains = useChains() - const [fromCoin, _setFromCoin] = useState() - const [toCoin, _setToCoin] = useState() + const [fromCoin, _setFromCoin] = useState() + const [toCoin, _setToCoin] = useState() const [amount, _setAmount] = useState(0) const [nonRecentAmount, setNonRecentAmount] = useState(0) const [recentInput, setRecentInput] = useState<'from' | 'to'>('from') @@ -112,7 +112,7 @@ export const SwapProvider = ({ children }: { children: ReactNode }) => { fetchSwapQuote() }, [fromCoin, toCoin, amount]) - const setFromCoin = (coin: TokenBalanceWithPrice | undefined) => { + const setFromCoin = (coin: TokenBalanceWithDetails | undefined) => { if (coin?.chainId === toCoin?.chainId && coin?.contractAddress === toCoin?.contractAddress) { switchCoinOrder() } else { @@ -120,7 +120,7 @@ export const SwapProvider = ({ children }: { children: ReactNode }) => { } } - const setToCoin = (coin: TokenBalanceWithPrice | undefined) => { + const setToCoin = (coin: TokenBalanceWithDetails | undefined) => { if (coin?.chainId === fromCoin?.chainId && coin?.contractAddress === fromCoin?.contractAddress) { switchCoinOrder() } else { diff --git a/packages/wallet-widget/src/components/SequenceWalletProvider/SequenceWalletProvider.tsx b/packages/wallet-widget/src/components/SequenceWalletProvider/SequenceWalletProvider.tsx index 64d5fe668..907321335 100644 --- a/packages/wallet-widget/src/components/SequenceWalletProvider/SequenceWalletProvider.tsx +++ b/packages/wallet-widget/src/components/SequenceWalletProvider/SequenceWalletProvider.tsx @@ -4,10 +4,9 @@ import { SequenceCheckoutProvider, useAddFundsModal } from '@0xsequence/checkout import { getModalPositionCss, ShadowRoot, useConnectConfigContext, useOpenConnectModal, useTheme } from '@0xsequence/connect' import { Modal, Scroll, ToastProvider } from '@0xsequence/design-system' import { AnimatePresence } from 'motion/react' -import React, { useContext, useEffect, useState, type ReactNode } from 'react' +import { useContext, useEffect, useState, type ReactNode } from 'react' import { useAccount } from 'wagmi' -import { HEADER_HEIGHT, HEADER_HEIGHT_WITH_LABEL } from '../../constants/index.js' import { WALLET_HEIGHT, WALLET_WIDTH } from '../../constants/index.js' import { NavigationContextProvider, @@ -16,6 +15,7 @@ import { type Navigation, type WalletOptions } from '../../contexts/index.js' +import { NavigationHeaderContextProvider } from '../../contexts/NavigationHeader.js' import { WalletContentRefContext, WalletContentRefProvider } from '../../contexts/WalletContentRef.js' import { FiatWalletsMapProvider } from './ProviderComponents/FiatWalletsMapProvider.js' @@ -68,30 +68,21 @@ export const WalletContent = ({ children }: SequenceWalletProviderProps) => { const [isBackButtonEnabled, setIsBackButtonEnabled] = useState(true) const navigation = history.length > 0 ? history[history.length - 1] : DEFAULT_LOCATION + // Navigation Header Context + const [search, setSearch] = useState('') + const [selectedTab, setSelectedTab] = useState<'tokens' | 'collectibles' | 'history'>('tokens') + const displayScrollbar = + navigation.location === 'home' || navigation.location === 'send-general' || navigation.location === 'collectible-details' || navigation.location === 'coin-details' || navigation.location === 'history' || navigation.location === 'search' || - navigation.location === 'search-view-all' || navigation.location === 'settings-wallets' || - navigation.location === 'settings-networks' || navigation.location === 'settings-currency' || navigation.location === 'settings-profiles' || - navigation.location === 'settings-apps' || - navigation.location === 'legacy-settings-currency' || - navigation.location === 'search-tokens' || - navigation.location === 'search-collectibles' - - let paddingTop = '0px' - switch (navigation.location) { - case 'send-general': - paddingTop = HEADER_HEIGHT_WITH_LABEL - break - default: - paddingTop = HEADER_HEIGHT - } + navigation.location === 'settings-apps' const walletContentRef = useContext(WalletContentRefContext) @@ -99,40 +90,51 @@ export const WalletContent = ({ children }: SequenceWalletProviderProps) => { - - - - - {openWalletModal && !isAddFundsModalOpen && !isConnectModalOpen && ( - setOpenWalletModal(false)} - > -
- {getHeader(navigation)} - - {displayScrollbar ? ( - {getContent(navigation)} - ) : ( - getContent(navigation) - )} -
-
- )} -
-
- {children} -
-
+ + + + + + {openWalletModal && !isAddFundsModalOpen && !isConnectModalOpen && ( + setOpenWalletModal(false)} + > +
+
{getHeader(navigation)}
+ +
+ {displayScrollbar ? ( + {getContent(navigation)} + ) : ( + getContent(navigation) + )} +
+
+
+ )} +
+
+ {children} +
+
+
diff --git a/packages/wallet-widget/src/components/SequenceWalletProvider/utils/index.tsx b/packages/wallet-widget/src/components/SequenceWalletProvider/utils/index.tsx index 709639995..5bc3abd5b 100644 --- a/packages/wallet-widget/src/components/SequenceWalletProvider/utils/index.tsx +++ b/packages/wallet-widget/src/components/SequenceWalletProvider/utils/index.tsx @@ -1,19 +1,18 @@ import type { Navigation } from '../../../contexts/index.js' import { + Buy, CoinDetails, CollectibleDetails, History, Home, Receive, - SearchCollectibles, - SearchTokens, + Search, SendCoin, SendCollectible, SendGeneral, SettingsApps, SettingsCurrency, SettingsMenu, - SettingsNetworks, SettingsPreferences, SettingsProfiles, SettingsWallets, @@ -24,7 +23,6 @@ import { TransactionDetails } from '../../../views/index.js' import { NavigationHeader } from '../../NavigationHeader/index.js' -import { WalletHeader } from '../../WalletHeader/index.js' export const getContent = (navigation: Navigation) => { const { location } = navigation @@ -46,23 +44,16 @@ export const getContent = (navigation: Navigation) => { return case 'receive': return + case 'buy': + return + case 'search': + return case 'history': return - case 'search-tokens': - return - case 'search-collectibles': - return ( - - ) case 'settings': return case 'settings-wallets': return - case 'settings-networks': - return case 'settings-currency': return case 'settings-profiles': @@ -112,46 +103,46 @@ export const getContent = (navigation: Navigation) => { export const getHeader = (navigation: Navigation) => { const { location } = navigation switch (location) { - case 'search-tokens': - return - case 'search-collectibles': - return + case 'home': + return case 'settings': - return + return case 'settings-wallets': - return - case 'settings-networks': - return + return case 'settings-currency': - return + return case 'settings-profiles': - return + return case 'settings-preferences': - return + return case 'settings-apps': - return + return case 'connect-dapp': - return + return case 'history': - return + return case 'coin-details': - return + return case 'collectible-details': - return + return case 'transaction-details': - return + return case 'send-general': - return + return case 'send-coin': - return + return case 'send-collectible': - return + return case 'swap': - return + return case 'swap-coin': case 'swap-coin-list': - return + return case 'receive': - return + return + case 'buy': + return + case 'search': + return } } diff --git a/packages/wallet-widget/src/components/TokenTileImage.tsx b/packages/wallet-widget/src/components/TokenTileImage.tsx new file mode 100644 index 000000000..44730f0c3 --- /dev/null +++ b/packages/wallet-widget/src/components/TokenTileImage.tsx @@ -0,0 +1,34 @@ +import { Card, Image, Text } from '@0xsequence/design-system' + +interface TokenTileImageProps { + src?: string + symbol?: string +} + +export const TokenTileImage = ({ src, symbol }: TokenTileImageProps) => { + let symbolLabel + + const abrevSymbol = symbol + ?.split(' ') + .map(word => word[0]) + .join('') + const shortSymbol = symbol?.replace(/\s/, '').slice(0, 6) + + if (abrevSymbol && abrevSymbol.length > 2) { + symbolLabel = abrevSymbol + } else { + symbolLabel = shortSymbol + } + + return ( + + {src ? ( + + ) : ( + + {symbolLabel} + + )} + + ) +} diff --git a/packages/wallet-widget/src/components/WalletAccountGradient.tsx b/packages/wallet-widget/src/components/WalletAccountGradient.tsx index 087bd66b0..35e788944 100644 --- a/packages/wallet-widget/src/components/WalletAccountGradient.tsx +++ b/packages/wallet-widget/src/components/WalletAccountGradient.tsx @@ -1,39 +1,46 @@ import { useWallets } from '@0xsequence/connect' -import { GradientAvatar } from '@0xsequence/design-system' +import { GradientAvatar, useMediaQuery } from '@0xsequence/design-system' import { getConnectorLogo } from './ConnectorLogos/getConnectorLogos.js' -export const WalletAccountGradient = ({ - accountAddress, - size = 'large' -}: { - accountAddress: string - size?: 'small' | 'large' -}) => { +export const WalletAccountGradient = ({ accountAddresses }: { accountAddresses: string[] }) => { + const isMobile = useMediaQuery('isMobile') + const limit = isMobile ? 4 : 6 + // limit the number of wallets shown to 6 on desktop and 4 on mobile + + return ( +
+ {accountAddresses.slice(0, limit).map((address, index) => ( + + ))} +
+ ) +} + +const WalletAccountGradientItem = ({ address, index }: { address: string; index: number }) => { const { wallets } = useWallets() - const remSize = size === 'small' ? 8 : 16 - const LoginIcon = getConnectorLogo(wallets.find(wallet => wallet.address === accountAddress)?.signInMethod || '') + const LoginIcon = getConnectorLogo(wallets.find(wallet => address.includes(wallet.address))?.signInMethod || '') return ( -
-
- -
-
{LoginIcon}
+
+ +
+
+ {LoginIcon}
diff --git a/packages/wallet-widget/src/components/WalletHeader/components/AccountInformation.tsx b/packages/wallet-widget/src/components/WalletHeader/components/AccountInformation.tsx deleted file mode 100644 index 83dca2043..000000000 --- a/packages/wallet-widget/src/components/WalletHeader/components/AccountInformation.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { formatAddress } from '@0xsequence/connect' -import { Card, ChevronUpDownIcon, GradientAvatar, Text } from '@0xsequence/design-system' -import { useAccount } from 'wagmi' - -interface AccountInformationProps { - onClickAccount?: () => void -} - -export const AccountInformation = ({ onClickAccount }: AccountInformationProps) => { - const { address } = useAccount() - - return ( - - - - {formatAddress(address || '')} - - {onClickAccount && } - - ) -} diff --git a/packages/wallet-widget/src/components/WalletHeader/index.tsx b/packages/wallet-widget/src/components/WalletHeader/index.tsx deleted file mode 100644 index 0ec0d7266..000000000 --- a/packages/wallet-widget/src/components/WalletHeader/index.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { useOpenConnectModal, useWallets } from '@0xsequence/connect' -import { ChevronLeftIcon, IconButton, Text } from '@0xsequence/design-system' -import { AnimatePresence } from 'motion/react' -import { useState } from 'react' - -import { HEADER_HEIGHT, HEADER_HEIGHT_WITH_LABEL } from '../../constants/index.js' -import { useNavigation } from '../../hooks/index.js' -import { SelectWalletRow } from '../Select/SelectWalletRow.js' -import { SlideupDrawer } from '../Select/SlideupDrawer.js' - -import { AccountInformation } from './components/AccountInformation.js' - -export const WalletHeader = ({ - primaryText, - enableAccountSelector = false -}: { - primaryText?: string - enableAccountSelector?: boolean -}) => { - const { wallets, setActiveWallet } = useWallets() - const { setOpenConnectModal } = useOpenConnectModal() - const { goBack } = useNavigation() - - const [accountSelectorModalOpen, setAccountSelectorModalOpen] = useState(false) - - const onClickSelector = () => { - setAccountSelectorModalOpen(true) - } - - const handleAddNewWallet = () => { - setAccountSelectorModalOpen(false) - setOpenConnectModal(true) - } - - const onClickBack = () => { - goBack() - } - - return ( -
-
- - -
-
- {primaryText && ( - - {primaryText} - - )} - - {accountSelectorModalOpen && ( - setAccountSelectorModalOpen(false)} - label="Select main wallet" - buttonLabel="+ Add new wallet" - handleButtonPress={handleAddNewWallet} - dragHandleWidth={28} - > -
- {wallets.map((wallet, index) => ( - setAccountSelectorModalOpen(false)} - onClick={setActiveWallet} - /> - ))} -
-
- )} -
-
- ) -} diff --git a/packages/wallet-widget/src/constants/sizing.ts b/packages/wallet-widget/src/constants/sizing.ts index 1eea31b50..fa0adfb77 100644 --- a/packages/wallet-widget/src/constants/sizing.ts +++ b/packages/wallet-widget/src/constants/sizing.ts @@ -1,4 +1,3 @@ -export const HEADER_HEIGHT = '60px' -export const HEADER_HEIGHT_WITH_LABEL = '100px' +export const HEADER_HEIGHT = '68px' export const WALLET_WIDTH = '460px' -export const WALLET_HEIGHT = 'min(800px, 90vh)' +export const WALLET_HEIGHT = '644px' diff --git a/packages/wallet-widget/src/contexts/FiatWalletsMap.ts b/packages/wallet-widget/src/contexts/FiatWalletsMap.ts index 0e012a9a1..d76623d7c 100644 --- a/packages/wallet-widget/src/contexts/FiatWalletsMap.ts +++ b/packages/wallet-widget/src/contexts/FiatWalletsMap.ts @@ -7,7 +7,7 @@ export interface FiatWalletPair { export interface FiatWalletsMapContext { fiatWalletsMap: FiatWalletPair[] - setFiatWalletsMap: (fiatWalletsMap: FiatWalletPair[]) => void + totalFiatValue: string } const [useFiatWalletsMapContext, FiatWalletsMapContextProvider] = createGenericContext() diff --git a/packages/wallet-widget/src/contexts/Navigation.ts b/packages/wallet-widget/src/contexts/Navigation.ts index ab0a5b7af..e3e8d8775 100644 --- a/packages/wallet-widget/src/contexts/Navigation.ts +++ b/packages/wallet-widget/src/contexts/Navigation.ts @@ -36,15 +36,6 @@ export interface TransactionDetailsNavigation { params: TransactionDetailsParams } -export interface SearchViewAllParams { - defaultTab: 'coins' | 'collections' -} - -export interface SearchViewAllNavigation { - location: 'search-view-all' - params: SearchViewAllParams -} - export interface SendCoinParams { chainId: number contractAddress: string @@ -87,38 +78,22 @@ export interface SendCollectibleNavigation { params: SendCollectibleParams } -export interface SearchCollectiblesParams { - selectedCollection: { - chainId: number - contractAddress: string - } -} - -export interface SearchCollectiblesNavigation { - location: 'search-collectibles' - params?: SearchCollectiblesParams -} export interface BasicNavigation { location: | 'home' | 'send-general' | 'swap' | 'receive' + | 'buy' | 'history' - | 'legacy-settings' - | 'legacy-settings-general' - | 'legacy-settings-currency' - | 'legacy-settings-networks' | 'settings' | 'settings-wallets' - | 'settings-networks' | 'settings-currency' | 'settings-profiles' | 'settings-apps' | 'settings-preferences' | 'connect-dapp' | 'search' - | 'search-tokens' } export type Navigation = @@ -126,8 +101,6 @@ export type Navigation = | CoinDetailsNavigation | CollectibleDetailsNavigation | TransactionDetailsNavigation - | SearchCollectiblesNavigation - | SearchViewAllNavigation | SendCoinNavigation | SendCollectibleNavigation | SwapCoinNavigation diff --git a/packages/wallet-widget/src/contexts/NavigationHeader.ts b/packages/wallet-widget/src/contexts/NavigationHeader.ts new file mode 100644 index 000000000..d9c36287a --- /dev/null +++ b/packages/wallet-widget/src/contexts/NavigationHeader.ts @@ -0,0 +1,12 @@ +import { createGenericContext } from './genericContext.js' + +export interface NavigationHeaderContext { + search: string + selectedTab: 'tokens' | 'collectibles' | 'history' + setSearch: (search: string) => void + setSelectedTab: (tab: 'tokens' | 'collectibles' | 'history') => void +} + +const [useNavigationHeaderContext, NavigationHeaderContextProvider] = createGenericContext() + +export { NavigationHeaderContextProvider, useNavigationHeaderContext } diff --git a/packages/wallet-widget/src/contexts/Swap.ts b/packages/wallet-widget/src/contexts/Swap.ts index 11bd43ecd..f1eca12d0 100644 --- a/packages/wallet-widget/src/contexts/Swap.ts +++ b/packages/wallet-widget/src/contexts/Swap.ts @@ -1,10 +1,10 @@ -import type { TokenBalanceWithPrice } from '../utils/index.js' +import type { TokenBalanceWithDetails } from '../utils/index.js' import { createGenericContext } from './genericContext.js' export interface SwapContext { - fromCoin: TokenBalanceWithPrice | undefined - toCoin: TokenBalanceWithPrice | undefined + fromCoin: TokenBalanceWithDetails | undefined + toCoin: TokenBalanceWithDetails | undefined amount: number nonRecentAmount: number recentInput: 'from' | 'to' @@ -14,8 +14,8 @@ export interface SwapContext { isErrorSwapQuote: boolean isTxnPending: boolean isErrorTxn: boolean - setFromCoin: (coin: TokenBalanceWithPrice | undefined) => void - setToCoin: (coin: TokenBalanceWithPrice | undefined) => void + setFromCoin: (coin: TokenBalanceWithDetails | undefined) => void + setToCoin: (coin: TokenBalanceWithDetails | undefined) => void setAmount: (amount: number, type: 'from' | 'to') => void switchCoinOrder: () => void onSubmitSwap: () => void diff --git a/packages/wallet-widget/src/hooks/useFiatWalletsMap.ts b/packages/wallet-widget/src/hooks/useFiatWalletsMap.ts index e8cdd7d0a..4468ee25e 100644 --- a/packages/wallet-widget/src/hooks/useFiatWalletsMap.ts +++ b/packages/wallet-widget/src/hooks/useFiatWalletsMap.ts @@ -1,7 +1,7 @@ import { useFiatWalletsMapContext } from '../contexts/FiatWalletsMap.js' export const useFiatWalletsMap = () => { - const { fiatWalletsMap, setFiatWalletsMap } = useFiatWalletsMapContext() + const { fiatWalletsMap, totalFiatValue } = useFiatWalletsMapContext() - return { fiatWalletsMap, setFiatWalletsMap } + return { fiatWalletsMap, totalFiatValue } } diff --git a/packages/wallet-widget/src/hooks/useGetAllCollections.tsx b/packages/wallet-widget/src/hooks/useGetAllCollections.tsx new file mode 100644 index 000000000..dabafd274 --- /dev/null +++ b/packages/wallet-widget/src/hooks/useGetAllCollections.tsx @@ -0,0 +1,51 @@ +import { useGetMultipleContractsInfo, useGetTokenBalancesSummary } from '@0xsequence/hooks' +import { ContractVerificationStatus } from '@0xsequence/indexer' +import { useEffect } from 'react' + +export const useGetAllCollections = ({ + accountAddresses, + chainIds, + hideUnlistedTokens +}: { + accountAddresses: string[] + chainIds: number[] + hideUnlistedTokens: boolean +}) => { + const { + data: tokenBalancesData, + isLoading, + fetchNextPage, + hasNextPage, + isFetchingNextPage + } = useGetTokenBalancesSummary({ + chainIds, + filter: { + accountAddresses, + contractStatus: hideUnlistedTokens ? ContractVerificationStatus.VERIFIED : ContractVerificationStatus.ALL, + omitNativeBalances: false + }, + page: { pageSize: 40 } + }) + + useEffect(() => { + if (hasNextPage && !isFetchingNextPage) { + fetchNextPage() + } + }, [hasNextPage, isFetchingNextPage]) + + const collections = tokenBalancesData?.pages.flatMap(page => + page.balances.filter(balance => balance.contractType === 'ERC721' || balance.contractType === 'ERC1155') + ) + + const { data: collectionsWithMetadata } = useGetMultipleContractsInfo( + collections?.map(collection => ({ + chainID: collection.chainId.toString(), + contractAddress: collection.contractAddress + })) || [] + ) + + return { + data: collectionsWithMetadata || [], + isLoading: isLoading + } +} diff --git a/packages/wallet-widget/src/hooks/useGetAllTokensDetails.ts b/packages/wallet-widget/src/hooks/useGetAllTokensDetails.ts index dc8e450b0..4c2d94bba 100644 --- a/packages/wallet-widget/src/hooks/useGetAllTokensDetails.ts +++ b/packages/wallet-widget/src/hooks/useGetAllTokensDetails.ts @@ -5,12 +5,10 @@ import { useEffect } from 'react' export const useGetAllTokensDetails = ({ accountAddresses, chainIds, - contractWhitelist, hideUnlistedTokens }: { accountAddresses: string[] chainIds: number[] - contractWhitelist?: string[] hideUnlistedTokens: boolean }) => { const { @@ -24,7 +22,6 @@ export const useGetAllTokensDetails = ({ filter: { accountAddresses, contractStatus: hideUnlistedTokens ? ContractVerificationStatus.VERIFIED : ContractVerificationStatus.ALL, - contractWhitelist: contractWhitelist ?? [], omitNativeBalances: false }, page: { pageSize: 40 } diff --git a/packages/wallet-widget/src/hooks/useGetMoreBalances.ts b/packages/wallet-widget/src/hooks/useGetMoreBalances.ts index cd429b933..dcd614a3e 100644 --- a/packages/wallet-widget/src/hooks/useGetMoreBalances.ts +++ b/packages/wallet-widget/src/hooks/useGetMoreBalances.ts @@ -1,13 +1,11 @@ import { useInfiniteQuery } from '@tanstack/react-query' import type { InfiniteData, UseInfiniteQueryResult } from '@tanstack/react-query' -import type { TokenBalanceWithPrice } from '../utils/index.js' - export const useGetMoreBalances = ( - balances: TokenBalanceWithPrice[], + balances: any[], pageSize: number, options?: { enabled: boolean } -): UseInfiniteQueryResult, Error> => { +): UseInfiniteQueryResult, Error> => { return useInfiniteQuery({ queryKey: ['infiniteBalances', balances], queryFn: ({ pageParam }) => { diff --git a/packages/wallet-widget/src/hooks/useNavigationHeader.ts b/packages/wallet-widget/src/hooks/useNavigationHeader.ts new file mode 100644 index 000000000..b830233ec --- /dev/null +++ b/packages/wallet-widget/src/hooks/useNavigationHeader.ts @@ -0,0 +1,6 @@ +import { useNavigationHeaderContext } from '../contexts/NavigationHeader.js' + +export const useNavigationHeader = () => { + const { search, selectedTab, setSearch, setSelectedTab } = useNavigationHeaderContext() + return { search, selectedTab, setSearch, setSelectedTab } +} diff --git a/packages/wallet-widget/src/hooks/useSettings.ts b/packages/wallet-widget/src/hooks/useSettings.ts index 1cecac080..2458e5633 100644 --- a/packages/wallet-widget/src/hooks/useSettings.ts +++ b/packages/wallet-widget/src/hooks/useSettings.ts @@ -21,17 +21,17 @@ interface Settings { selectedNetworks: number[] allNetworks: number[] selectedWallets: ConnectedWallet[] - selectedCollections: SettingsCollection[] + showCollections: boolean hideUnlistedTokensObservable: Observable fiatCurrencyObservable: Observable selectedNetworksObservable: Observable selectedWalletsObservable: Observable - selectedCollectionsObservable: Observable + showCollectionsObservable: Observable setFiatCurrency: (newFiatCurrency: FiatCurrency) => void setHideUnlistedTokens: (newState: boolean) => void setSelectedWallets: (newWallets: ConnectedWallet[]) => void setSelectedNetworks: (newNetworks: number[]) => void - setSelectedCollections: (newCollections: SettingsCollection[]) => void + setShowCollections: (newState: boolean) => void } type SettingsItems = { @@ -39,7 +39,7 @@ type SettingsItems = { fiatCurrencyObservable: MutableObservable selectedWalletsObservable: MutableObservable selectedNetworksObservable: MutableObservable - selectedCollectionsObservable: MutableObservable + showCollectionsObservable: MutableObservable } let settingsObservables: SettingsItems | null = null @@ -89,7 +89,7 @@ export const useSettings = (): Settings => { let fiatCurrency = defaultFiatCurrency let selectedWallets: ConnectedWallet[] = allWallets let selectedNetworks: number[] = allNetworks - let selectedCollections: SettingsCollection[] = [] + let showCollections = false try { const settingsStorage = localStorage.getItem(LocalStorageKey.Settings) @@ -125,15 +125,13 @@ export const useSettings = (): Settings => { } }) - const hasInvalidNetworksSelection = selectedNetworks.length > 1 && selectedNetworks.length !== allNetworks.length - - if (hasInvalidNetworks || hasInvalidNetworksSelection) { + if (hasInvalidNetworks) { selectedNetworks = allNetworks localStorage.setItem(LocalStorageKey.Settings, JSON.stringify({ ...settings, selectedNetworks: allNetworks })) } } - if (settings?.selectedCollections !== undefined) { - selectedCollections = settings?.selectedCollections + if (settings?.showCollections !== undefined) { + showCollections = settings?.showCollections } } catch (e) { console.error(e, 'Failed to fetch settings') @@ -144,23 +142,20 @@ export const useSettings = (): Settings => { fiatCurrencyObservable: observable(fiatCurrency), selectedWalletsObservable: observable(selectedWallets), selectedNetworksObservable: observable(selectedNetworks), - selectedCollectionsObservable: observable(selectedCollections) + showCollectionsObservable: observable(showCollections) } } const resetSettings = () => { if (settingsObservables) { const selectedWallets = settingsObservables.selectedWalletsObservable.get() - const selectedNetworks = settingsObservables.selectedNetworksObservable.get() const isPartialSelection = selectedWallets.length > 1 && selectedWallets.length !== allWallets.length const hasInvalidWallets = selectedWallets.some(wallet => !allWallets.some((w: ConnectedWallet) => w.address === wallet.address)) || isPartialSelection - const hasInvalidNetworksSelection = selectedNetworks.length > 1 && selectedNetworks.length !== allNetworks.length - - if (hasInvalidWallets || hasInvalidNetworksSelection || !selectedWallets.length) { + if (hasInvalidWallets || !selectedWallets.length) { return true } } @@ -176,7 +171,7 @@ export const useSettings = (): Settings => { fiatCurrencyObservable, selectedWalletsObservable, selectedNetworksObservable, - selectedCollectionsObservable + showCollectionsObservable } = settingsObservables const setHideUnlistedTokens = (newState: boolean) => { @@ -203,17 +198,12 @@ export const useSettings = (): Settings => { selectedNetworksObservable.set(allNetworks) } else { selectedNetworksObservable.set(newSelectedNetworks) - selectedCollectionsObservable.set([]) } updateLocalStorage() } - const setSelectedCollections = (newSelectedCollections: SettingsCollection[]) => { - if (newSelectedCollections.length === 0) { - selectedCollectionsObservable.set([]) - } else { - selectedCollectionsObservable.set(newSelectedCollections) - } + const setShowCollections = (newState: boolean) => { + showCollectionsObservable.set(newState) updateLocalStorage() } @@ -223,7 +213,7 @@ export const useSettings = (): Settings => { fiatCurrency: fiatCurrencyObservable.get(), selectedWallets: selectedWalletsObservable.get(), selectedNetworks: selectedNetworksObservable.get(), - selectedCollections: selectedCollectionsObservable.get() + showCollections: showCollectionsObservable.get() } console.log('settings updated', newSettings) localStorage.setItem(LocalStorageKey.Settings, JSON.stringify(newSettings)) @@ -235,16 +225,16 @@ export const useSettings = (): Settings => { selectedWallets: selectedWalletsObservable.get(), selectedNetworks: selectedNetworksObservable.get(), allNetworks: allNetworks, - selectedCollections: selectedCollectionsObservable.get(), + showCollections: showCollectionsObservable.get(), hideUnlistedTokensObservable, fiatCurrencyObservable, selectedWalletsObservable, selectedNetworksObservable, - selectedCollectionsObservable, + showCollectionsObservable, setFiatCurrency, setHideUnlistedTokens, setSelectedWallets, setSelectedNetworks, - setSelectedCollections + setShowCollections } } diff --git a/packages/wallet-widget/src/utils/formatBalance.ts b/packages/wallet-widget/src/utils/formatBalance.ts index b3f66cf50..aa9923e8a 100644 --- a/packages/wallet-widget/src/utils/formatBalance.ts +++ b/packages/wallet-widget/src/utils/formatBalance.ts @@ -5,7 +5,7 @@ import type { TokenBalance } from '@0xsequence/indexer' import { formatUnits, type Chain } from 'viem' import { zeroAddress } from 'viem' -import type { TokenBalanceWithPrice } from './tokens.js' +import type { TokenBalanceWithDetails } from './tokens.js' //TODO: rename these and maybe do a refactor @@ -30,7 +30,7 @@ interface TokenInfo { } export const formatTokenInfo = ( - balance: TokenBalanceWithPrice | undefined, + balance: TokenBalanceWithDetails | undefined, fiatSign: string, chains: readonly [Chain, ...Chain[]] ): TokenInfo => { @@ -49,6 +49,7 @@ export const formatTokenInfo = ( const bal = formatUnits(BigInt(balance?.balance || 0), decimals || 18) const displayBalance = formatDisplay(bal) const symbol = isNativeToken ? nativeTokenInfo.symbol : balance?.contractInfo?.symbol + const fiatBalance = (balance?.price?.value || 0) * Number(bal) return { isNativeToken, @@ -57,7 +58,7 @@ export const formatTokenInfo = ( name: selectedCoinName, symbol: selectedCoinSymbol, displayBalance: `${displayBalance} ${symbol}`, - fiatBalance: `${fiatSign}${(balance.price.value * Number(bal)).toFixed(2)}` + fiatBalance: `${fiatSign}${fiatBalance.toFixed(2)}` } } diff --git a/packages/wallet-widget/src/utils/tokens.ts b/packages/wallet-widget/src/utils/tokens.ts index 5198c8e6e..210e98b18 100644 --- a/packages/wallet-widget/src/utils/tokens.ts +++ b/packages/wallet-widget/src/utils/tokens.ts @@ -4,8 +4,9 @@ import type { GetTransactionHistoryReturn, TokenBalance, Transaction } from '@0x import type { InfiniteData } from '@tanstack/react-query' import { formatUnits, zeroAddress } from 'viem' -export interface TokenBalanceWithPrice extends TokenBalance { - price: Price +export interface TokenBalanceWithDetails extends TokenBalance { + price?: Price + _type?: 'coin' | 'collectible' | 'collection' } export const getPercentageColor = (value: number) => { diff --git a/packages/wallet-widget/src/views/Buy/index.tsx b/packages/wallet-widget/src/views/Buy/index.tsx new file mode 100644 index 000000000..0b769fd62 --- /dev/null +++ b/packages/wallet-widget/src/views/Buy/index.tsx @@ -0,0 +1,32 @@ +import { useAddFundsModal } from '@0xsequence/checkout' +import { useWallets } from '@0xsequence/connect' +import { AddIcon, Text } from '@0xsequence/design-system' +import { useAccount } from 'wagmi' + +import { WalletSelect } from '../../components/Select/WalletSelect.js' + +export const Buy = () => { + const { address } = useAccount() + const { setActiveWallet } = useWallets() + const { triggerAddFunds } = useAddFundsModal() + + const onClickWallet = (address: string) => { + setActiveWallet(address) + } + + const onClickAddFunds = () => { + triggerAddFunds({ walletAddress: address || '' }) + } + + return ( +
+ +
+ + + Add Funds to Selected Wallet + +
+
+ ) +} diff --git a/packages/wallet-widget/src/views/CoinDetails/Skeleton.tsx b/packages/wallet-widget/src/views/CoinDetails/Skeleton.tsx index d25b33538..34e599933 100644 --- a/packages/wallet-widget/src/views/CoinDetails/Skeleton.tsx +++ b/packages/wallet-widget/src/views/CoinDetails/Skeleton.tsx @@ -2,7 +2,6 @@ import { Button, SendIcon, Skeleton, Text } from '@0xsequence/design-system' import { NetworkBadge } from '../../components/NetworkBadge.js' import { TransactionHistorySkeleton } from '../../components/TransactionHistoryList/TransactionHistorySkeleton.js' -import { HEADER_HEIGHT } from '../../constants/index.js' interface CoinDetailsSkeletonProps { chainId: number @@ -11,7 +10,7 @@ interface CoinDetailsSkeletonProps { export const CoinDetailsSkeleton = ({ chainId, isReadOnly }: CoinDetailsSkeletonProps) => { return ( -
+
diff --git a/packages/wallet-widget/src/views/CoinDetails/index.tsx b/packages/wallet-widget/src/views/CoinDetails/index.tsx index 159308fc5..4df9ded45 100644 --- a/packages/wallet-widget/src/views/CoinDetails/index.tsx +++ b/packages/wallet-widget/src/views/CoinDetails/index.tsx @@ -8,7 +8,6 @@ import { useConfig } from 'wagmi' import { InfiniteScroll } from '../../components/InfiniteScroll.js' import { NetworkBadge } from '../../components/NetworkBadge.js' import { TransactionHistoryList } from '../../components/TransactionHistoryList/index.js' -import { HEADER_HEIGHT } from '../../constants/index.js' import { useNavigation, useSettings } from '../../hooks/index.js' import { computeBalanceFiat, flattenPaginatedTransactionHistory } from '../../utils/index.js' @@ -104,7 +103,7 @@ export const CoinDetails = ({ contractAddress, chainId, accountAddress }: CoinDe }) } return ( -
+
diff --git a/packages/wallet-widget/src/views/CollectibleDetails/Skeleton.tsx b/packages/wallet-widget/src/views/CollectibleDetails/Skeleton.tsx index dd2d077fe..e32b82c31 100644 --- a/packages/wallet-widget/src/views/CollectibleDetails/Skeleton.tsx +++ b/packages/wallet-widget/src/views/CollectibleDetails/Skeleton.tsx @@ -1,7 +1,6 @@ import { Button, SendIcon, Skeleton, Text } from '@0xsequence/design-system' import { TransactionHistorySkeleton } from '../../components/TransactionHistoryList/TransactionHistorySkeleton.js' -import { HEADER_HEIGHT } from '../../constants/index.js' interface CollectibleDetailsSkeletonProps { isReadOnly: boolean @@ -9,7 +8,7 @@ interface CollectibleDetailsSkeletonProps { export const CollectibleDetailsSkeleton = ({ isReadOnly }: CollectibleDetailsSkeletonProps) => { return ( -
+
+
- +
{/* balance */} diff --git a/packages/wallet-widget/src/views/History.tsx b/packages/wallet-widget/src/views/History.tsx index 4d53d6dca..7f4bfed78 100644 --- a/packages/wallet-widget/src/views/History.tsx +++ b/packages/wallet-widget/src/views/History.tsx @@ -8,7 +8,6 @@ import { useMemo, useState } from 'react' import { zeroAddress } from 'viem' import { useConfig } from 'wagmi' -import { FilterButton } from '../components/Filter/FilterButton.js' import { TransactionHistoryList } from '../components/TransactionHistoryList/index.js' import { useSettings } from '../hooks/index.js' @@ -141,7 +140,6 @@ export const History = () => { data-1p-ignore />
-
void - icon: ComponentType -}) => { - return ( -
- {Icon && } - - {label} - -
- ) -} diff --git a/packages/wallet-widget/src/views/Home/index.tsx b/packages/wallet-widget/src/views/Home/index.tsx index eb27a44a0..0dd3e8049 100644 --- a/packages/wallet-widget/src/views/Home/index.tsx +++ b/packages/wallet-widget/src/views/Home/index.tsx @@ -1,52 +1,20 @@ -import { useAddFundsModal } from '@0xsequence/checkout' -import { compareAddress, formatAddress, getNativeTokenInfoByChainId, useOpenConnectModal, useWallets } from '@0xsequence/connect' -import { - AddIcon, - ArrowUpIcon, - Button, - ChevronUpDownIcon, - EllipsisIcon, - ScanIcon, - Skeleton, - SwapIcon, - Text -} from '@0xsequence/design-system' -import { useGetCoinPrices, useGetExchangeRate } from '@0xsequence/hooks' -import { ethers } from 'ethers' -import { useObservable } from 'micro-observables' -import { AnimatePresence } from 'motion/react' -import { useEffect, useMemo, useState } from 'react' -import { useAccount, useConfig } from 'wagmi' +import { truncateAtIndex, useWallets } from '@0xsequence/connect' +import { Text } from '@0xsequence/design-system' +import { useEffect, useState } from 'react' +import { useAccount } from 'wagmi' import { CopyButton } from '../../components/CopyButton.js' -import { WalletsFilter } from '../../components/Filter/WalletsFilter.js' -import { StackedIconTag } from '../../components/IconWrappers/StackedIconTag.js' -import { ListCardNav } from '../../components/ListCard/ListCardNav.js' -import { ListCardNavTable } from '../../components/ListCardTable/ListCardNavTable.js' -import { SelectWalletRow } from '../../components/Select/SelectWalletRow.js' -import { SlideupDrawer } from '../../components/Select/SlideupDrawer.js' +import { GeneralList } from '../../components/SearchLists/index.js' import { WalletAccountGradient } from '../../components/WalletAccountGradient.js' -import { useFiatWalletsMap, useGetAllTokensDetails, useNavigation, useSettings } from '../../hooks/index.js' -import { computeBalanceFiat } from '../../utils/index.js' - -import { OperationButtonTemplate } from './OperationButtonTemplate.js' +import { useFiatWalletsMap, useSettings } from '../../hooks/index.js' export const Home = () => { - const { setNavigation } = useNavigation() - const { selectedWalletsObservable, selectedNetworks, hideUnlistedTokens, fiatCurrency, selectedCollections } = useSettings() - const { fiatWalletsMap } = useFiatWalletsMap() + const { wallets: allWallets } = useWallets() + const { fiatCurrency } = useSettings() + const { totalFiatValue } = useFiatWalletsMap() const { connector } = useAccount() - const selectedWallets = useObservable(selectedWalletsObservable) - const { chains } = useConfig() const { address: accountAddress } = useAccount() - const { wallets, setActiveWallet } = useWallets() - - const { setOpenConnectModal } = useOpenConnectModal() - const { triggerAddFunds } = useAddFundsModal() - const [accountSelectorModalOpen, setAccountSelectorModalOpen] = useState(false) - const [walletFilterOpen, setWalletFilterOpen] = useState(false) - const [signInDisplay, setSignInDisplay] = useState('') useEffect(() => { @@ -70,315 +38,44 @@ export const Home = () => { fetchSignInDisplay() }, [connector]) - const { data: tokenBalancesData, isLoading: isLoadingTokenBalances } = useGetAllTokensDetails({ - accountAddresses: [accountAddress || ''], - chainIds: selectedNetworks, - contractWhitelist: selectedCollections.map(collection => collection.contractAddress), - hideUnlistedTokens - }) - - const coinBalancesUnordered = - tokenBalancesData?.filter(b => b.contractType === 'ERC20' || compareAddress(b.contractAddress, ethers.ZeroAddress)) || [] - - const isSingleCollectionSelected = selectedCollections.length === 1 - - const collectibleBalancesUnordered = - tokenBalancesData?.filter(token => { - if (token.contractType !== 'ERC721' && token.contractType !== 'ERC1155') { - return false - } - if (isSingleCollectionSelected) { - return token.chainId === selectedCollections[0].chainId - } - return true - }) || [] - - const { data: coinPrices = [], isLoading: isLoadingCoinPrices } = useGetCoinPrices( - coinBalancesUnordered.map(token => ({ - chainId: token.chainId, - contractAddress: token.contractAddress - })) - ) - - const { data: conversionRate, isLoading: isLoadingConversionRate } = useGetExchangeRate(fiatCurrency.symbol) - - const isLoading = isLoadingTokenBalances || isLoadingCoinPrices || isLoadingConversionRate - - const totalFiatValue = fiatWalletsMap - .reduce((acc, wallet) => { - if (selectedWallets.some(selectedWallet => selectedWallet.address === wallet.accountAddress)) { - const walletFiatValue = Number(wallet.fiatValue) - return acc + walletFiatValue - } - return acc - }, 0) - .toFixed(2) - - const coinBalances = coinBalancesUnordered.sort((a, b) => { - const isHigherFiat = - Number( - computeBalanceFiat({ - balance: b, - prices: coinPrices, - conversionRate: conversionRate || 1, - decimals: b.contractInfo?.decimals || 18 - }) - ) - - Number( - computeBalanceFiat({ - balance: a, - prices: coinPrices, - conversionRate: conversionRate || 1, - decimals: a.contractInfo?.decimals || 18 - }) - ) - return isHigherFiat - }) - - const collectibleBalances = collectibleBalancesUnordered.sort((a, b) => { - return Number(b.balance) - Number(a.balance) - }) - - const coinBalancesIconSet = new Set() - const coinBalancesIcons = useMemo( - () => - coinBalances - .map(coin => { - const isNativeToken = compareAddress(coin.contractAddress, ethers.ZeroAddress) - const nativeTokenInfo = getNativeTokenInfoByChainId(coin.chainId, chains) - const logoURI = isNativeToken ? nativeTokenInfo.logoURI : coin.contractInfo?.logoURI - const tokenName = isNativeToken ? nativeTokenInfo.name : coin.contractInfo?.name - - if (coinBalancesIconSet.has(tokenName) || !logoURI) { - return - } - - coinBalancesIconSet.add(tokenName) - if (coinBalancesIconSet.size <= 3) { - return logoURI - } - }) - .filter(Boolean) as string[], - [coinBalances, selectedWallets, selectedNetworks, hideUnlistedTokens, selectedCollections] - ) - - const collectibleBalancesIconSet = new Set() - const collectibleBalancesIcons = useMemo( - () => - collectibleBalances - .map(collectible => { - const logoURI = collectible.tokenMetadata?.image - - if (collectibleBalancesIconSet.has(logoURI) || !logoURI) { - return - } - - collectibleBalancesIconSet.add(logoURI) - if (collectibleBalancesIconSet.size <= 3) { - return logoURI - } - }) - .filter(Boolean) as string[], - [collectibleBalances, selectedWallets, selectedNetworks, hideUnlistedTokens, selectedCollections] - ) - - const onClickAccountSelector = () => { - setAccountSelectorModalOpen(true) - } - const handleAddNewWallet = () => { - setAccountSelectorModalOpen(false) - setOpenConnectModal(true) - } - const onClickSend = () => { - setNavigation({ - location: 'send-general' - }) - } - const onClickSwap = () => { - setNavigation({ - location: 'swap' - }) - } - const onClickReceive = () => { - setNavigation({ - location: 'receive' - }) - } - const onClickAddFunds = () => { - triggerAddFunds({ walletAddress: accountAddress || '' }) - } - const onClickWalletSelector = () => { - setWalletFilterOpen(true) - } - const onClickTokens = () => { - setNavigation({ - location: 'search-tokens' - }) - } - const onClickCollectibles = () => { - setNavigation({ - location: 'search-collectibles' - }) - } - const onClickTransactions = () => { - setNavigation({ - location: 'history' - }) - } - const onClickSettings = () => { - setNavigation({ - location: 'settings' - }) - } + return ( +
+
+
+ {allWallets.length > 1 ? ( + wallet.address)} /> + ) : ( +
+ wallet.address)} /> +
+
+ + {truncateAtIndex(accountAddress || '', 8)} + + +
+ {signInDisplay && ( + + {signInDisplay} + + )} +
+
+ )} - const homeNavTableItems = [ - 0 ? ( -
- - {fiatCurrency.sign} - {isLoading ? : `${totalFiatValue}`} +
+ + Balance - - {coinBalances.length} - - } - /> -
- ) : ( - - No Tokens - - ) - } - shape="square" - > - - Tokens - - , - 0 ? ( - - {collectibleBalances.length} -
- } - shape="square" - /> - ) : ( - - No Collectibles - - ) - } - shape="square" - > - - Collectibles - - - ] - - return ( -
-
- -
-
- - {formatAddress(accountAddress || '')} + + {fiatCurrency.symbol} {fiatCurrency.sign} + {totalFiatValue} -
- {signInDisplay && ( - - {signInDisplay} - - )}
- -
-
- - - - -
-
- - <> - - Items - - wallet.address)} - label={ -
- - {`${selectedWallets.length} Wallet${selectedWallets.length === 1 ? '' : 's'}`} - - -
- } - isAccount - enabled - onClick={onClickWalletSelector} - /> - -
- - - Transactions - - - - - Settings - -
- - {accountSelectorModalOpen && ( - setAccountSelectorModalOpen(false)} - label="Select main wallet" - buttonLabel="+ Add new wallet" - handleButtonPress={handleAddNewWallet} - dragHandleWidth={74} - > -
- {wallets.map((wallet, index) => ( - setAccountSelectorModalOpen(false)} - onClick={setActiveWallet} - /> - ))} -
-
- )} - {walletFilterOpen && ( - setWalletFilterOpen(false)} label="Filter items by wallet"> - - - )} -
+
) } diff --git a/packages/wallet-widget/src/views/Receive.tsx b/packages/wallet-widget/src/views/Receive.tsx deleted file mode 100644 index f038fa565..000000000 --- a/packages/wallet-widget/src/views/Receive.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { getNetwork } from '@0xsequence/connect' -import { Button, CopyIcon, Image, ShareIcon, Text } from '@0xsequence/design-system' -import { useClipboard } from '@0xsequence/hooks' -import { QRCodeCanvas } from 'qrcode.react' -import { useAccount } from 'wagmi' - -import { NetworkSelect } from '../components/Select/NetworkSelect.js' -import { HEADER_HEIGHT_WITH_LABEL } from '../constants/index.js' - -const isVowel = (char: string) => ['a', 'e', 'i', 'o', 'u'].includes(char.toLowerCase()) - -export const Receive = () => { - const { address, chain } = useAccount() - const [isCopied, setCopied] = useClipboard({ timeout: 4000 }) - - const networkInfo = getNetwork(chain?.id || 1) - const networkName = networkInfo.title || networkInfo.name - - const onClickShare = () => { - if (typeof window !== 'undefined') { - window.open(`https://twitter.com/intent/tweet?text=Here%20is%20my%20address%20${address}`) - } - } - - return ( -
-
- -
- -
-
-
- - My Wallet - - icon -
-
- - {address} - -
-
-
-
-
- - {`This is a${isVowel(networkName[0]) ? 'n' : ''} ${networkName} address. Please only send assets on the ${networkName} network.`} - -
-
-
- ) -} diff --git a/packages/wallet-widget/src/views/Receive/index.tsx b/packages/wallet-widget/src/views/Receive/index.tsx new file mode 100644 index 000000000..bf2c6e6f6 --- /dev/null +++ b/packages/wallet-widget/src/views/Receive/index.tsx @@ -0,0 +1,56 @@ +import { useWallets } from '@0xsequence/connect' +import { Button, CopyIcon, ShareIcon, Text } from '@0xsequence/design-system' +import { useClipboard } from '@0xsequence/hooks' +import { QRCodeCanvas } from 'qrcode.react' +import { useAccount } from 'wagmi' + +import { WalletSelect } from '../../components/Select/WalletSelect.js' + +export const Receive = () => { + const { address } = useAccount() + const { setActiveWallet } = useWallets() + const [isCopied, setCopied] = useClipboard({ timeout: 4000 }) + + const onClickWallet = (address: string) => { + setActiveWallet(address) + } + + const onClickShare = () => { + if (typeof window !== 'undefined') { + window.open(`https://twitter.com/intent/tweet?text=Here%20is%20my%20address%20${address}`) + } + } + + return ( +
+ +
+ +
+
+
+ + My Wallet + +
+
+ + {address} + +
+
+
+
+
+ ) +} diff --git a/packages/wallet-widget/src/views/Search/SearchCollectibles.tsx b/packages/wallet-widget/src/views/Search/SearchCollectibles.tsx deleted file mode 100644 index 7b0cda7fd..000000000 --- a/packages/wallet-widget/src/views/Search/SearchCollectibles.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { useGetContractInfo } from '@0xsequence/hooks' -import { useObservable } from 'micro-observables' -import { useEffect } from 'react' - -import { CollectiblesList } from '../../components/SearchLists/CollectiblesList.js' -import { useGetAllTokensDetails, useNavigation, useSettings } from '../../hooks/index.js' -import type { TokenBalanceWithPrice } from '../../utils/index.js' - -export const SearchCollectibles = ({ - contractAddress, - chainId -}: { - contractAddress: string | undefined - chainId: number | undefined -}) => { - const { - selectedWalletsObservable, - selectedNetworksObservable, - selectedCollectionsObservable, - hideUnlistedTokens, - setSelectedCollections - } = useSettings() - const { setNavigation } = useNavigation() - - // Only fetch contract info if contract address and chain id are provided - const { data: contractInfo } = useGetContractInfo( - { - chainID: String(chainId), - contractAddress: contractAddress || '' - }, - { disabled: !contractAddress || !chainId } - ) - - useEffect(() => { - if (contractAddress && chainId && contractInfo) { - setSelectedCollections([ - { - contractAddress, - chainId: Number(chainId), - logoURI: contractInfo.logoURI, - name: contractInfo.name - } - ]) - } else { - setSelectedCollections([]) - } - }, [contractInfo, contractAddress, chainId]) - - const selectedWallets = useObservable(selectedWalletsObservable) - const selectedNetworks = useObservable(selectedNetworksObservable) - const selectedCollections = useObservable(selectedCollectionsObservable) - - const { data: tokenBalancesData = [], isLoading } = useGetAllTokensDetails({ - accountAddresses: selectedWallets.map(wallet => wallet.address), - chainIds: selectedNetworks, - contractWhitelist: selectedCollections.map(collection => collection.contractAddress), - hideUnlistedTokens - }) - - const isSingleCollectionSelected = selectedCollections.length === 1 - - const collectibleBalancesFiltered = tokenBalancesData.filter(token => { - if (isSingleCollectionSelected && selectedCollections[0]) { - // Ensure we only show tokens from the selected collection's chain and contract - return ( - token.chainId === selectedCollections[0].chainId && - token.contractAddress.toLowerCase() === selectedCollections[0].contractAddress.toLowerCase() - ) - } - return true - }) - - const onHandleCollectibleClick = (balance: TokenBalanceWithPrice) => { - setNavigation({ - location: 'collectible-details', - params: { - contractAddress: balance.contractAddress, - chainId: balance.chainId, - tokenId: balance.tokenID || '', - accountAddress: balance.accountAddress - } - }) - } - - // Get the single selected collection details if available - const singleCollection = isSingleCollectionSelected ? selectedCollections[0] : null - - // Prepare the header info object, only if a single collection is selected - const collectionHeaderInfo = singleCollection - ? { - logoURI: singleCollection.logoURI, - name: singleCollection.name, - chainId: singleCollection.chainId - // We could add collectibleBalancesFiltered.length here if needed later - } - : undefined - - return ( -
- -
- ) -} diff --git a/packages/wallet-widget/src/views/Search/SearchTokens.tsx b/packages/wallet-widget/src/views/Search/SearchTokens.tsx deleted file mode 100644 index 6683f10a8..000000000 --- a/packages/wallet-widget/src/views/Search/SearchTokens.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { useObservable } from 'micro-observables' - -import { TokenList } from '../../components/SearchLists/index.js' -import { useNavigation, useSettings } from '../../hooks/index.js' -import { useGetAllTokensDetails } from '../../hooks/index.js' -import type { TokenBalanceWithPrice } from '../../utils/index.js' - -export const SearchTokens = () => { - const { setNavigation } = useNavigation() - const { selectedWalletsObservable, selectedNetworksObservable, hideUnlistedTokens } = useSettings() - - const selectedWallets = useObservable(selectedWalletsObservable) - const selectedNetworks = useObservable(selectedNetworksObservable) - - const { data: tokenBalancesData, isLoading } = useGetAllTokensDetails({ - accountAddresses: selectedWallets.map(wallet => wallet.address), - chainIds: selectedNetworks, - hideUnlistedTokens - }) - - const handleTokenClick = (balance: TokenBalanceWithPrice) => { - setNavigation({ - location: 'coin-details', - params: { - contractAddress: balance.contractAddress, - chainId: balance.chainId, - accountAddress: balance.accountAddress - } - }) - } - - return ( -
- -
- ) -} diff --git a/packages/wallet-widget/src/views/Search/index.ts b/packages/wallet-widget/src/views/Search/index.ts deleted file mode 100644 index 41bd2e7b9..000000000 --- a/packages/wallet-widget/src/views/Search/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './SearchTokens.js' -export * from './SearchCollectibles.js' diff --git a/packages/wallet-widget/src/views/Search/index.tsx b/packages/wallet-widget/src/views/Search/index.tsx new file mode 100644 index 000000000..3c496467c --- /dev/null +++ b/packages/wallet-widget/src/views/Search/index.tsx @@ -0,0 +1,5 @@ +import { GeneralList } from '../../components/SearchLists/GeneralList.js' + +export const Search = () => { + return +} diff --git a/packages/wallet-widget/src/views/Send/SendCoin.tsx b/packages/wallet-widget/src/views/Send/SendCoin.tsx index 51bdd0b8d..cd88b5176 100644 --- a/packages/wallet-widget/src/views/Send/SendCoin.tsx +++ b/packages/wallet-widget/src/views/Send/SendCoin.tsx @@ -35,11 +35,11 @@ import { useEffect, useRef, useState, type ChangeEvent } from 'react' import { encodeFunctionData, formatUnits, parseUnits, toHex, zeroAddress, type Hex } from 'viem' import { useAccount, useChainId, useConfig, usePublicClient, useSwitchChain, useWalletClient } from 'wagmi' -import { WalletSelect } from '../../components/Select/WalletSelect.js' +import { ConnectedWalletSelect } from '../../components/Select/ConnectedWalletSelect.js' import { SendItemInfo } from '../../components/SendItemInfo.js' import { TransactionConfirmation } from '../../components/TransactionConfirmation.js' import { EVENT_SOURCE, EVENT_TYPES } from '../../constants/analytics.js' -import { ERC_20_ABI, HEADER_HEIGHT_WITH_LABEL } from '../../constants/index.js' +import { ERC_20_ABI } from '../../constants/index.js' import { useNavigationContext } from '../../contexts/Navigation.js' import { useNavigation, useSettings } from '../../hooks/index.js' import { computeBalanceFiat, isEthAddress, limitDecimals } from '../../utils/index.js' @@ -321,13 +321,7 @@ export const SendCoin = ({ chainId, contractAddress }: SendCoinProps) => { } return ( -
+ {!showConfirmation && ( <>
@@ -397,7 +391,7 @@ export const SendCoin = ({ chainId, contractAddress }: SendCoinProps) => { /> } /> - {wallets.length > 1 && } + {wallets.length > 1 && } )}
diff --git a/packages/wallet-widget/src/views/Send/SendCollectible.tsx b/packages/wallet-widget/src/views/Send/SendCollectible.tsx index adffba3a4..1bbe89d57 100644 --- a/packages/wallet-widget/src/views/Send/SendCollectible.tsx +++ b/packages/wallet-widget/src/views/Send/SendCollectible.tsx @@ -30,11 +30,11 @@ import { useEffect, useRef, useState, type ChangeEvent } from 'react' import { encodeFunctionData, formatUnits, parseUnits, toHex, type Hex } from 'viem' import { useAccount, useChainId, useConfig, usePublicClient, useSwitchChain, useWalletClient } from 'wagmi' -import { WalletSelect } from '../../components/Select/WalletSelect.js' +import { ConnectedWalletSelect } from '../../components/Select/ConnectedWalletSelect.js' import { SendItemInfo } from '../../components/SendItemInfo.js' import { TransactionConfirmation } from '../../components/TransactionConfirmation.js' import { EVENT_SOURCE, EVENT_TYPES } from '../../constants/analytics.js' -import { ERC_1155_ABI, ERC_721_ABI, HEADER_HEIGHT_WITH_LABEL } from '../../constants/index.js' +import { ERC_1155_ABI, ERC_721_ABI } from '../../constants/index.js' import { useNavigationContext } from '../../contexts/Navigation.js' import { useNavigation } from '../../hooks/index.js' import { isEthAddress, limitDecimals } from '../../utils/index.js' @@ -365,13 +365,7 @@ export const SendCollectible = ({ chainId, contractAddress, tokenId }: SendColle const isMaximum = Number(amount) >= Number(maxAmount) return ( - + {!showConfirmation && ( <>
@@ -445,7 +439,7 @@ export const SendCollectible = ({ chainId, contractAddress, tokenId }: SendColle /> } /> - {wallets.length > 1 && } + {wallets.length > 1 && } )}
diff --git a/packages/wallet-widget/src/views/Send/SendGeneral.tsx b/packages/wallet-widget/src/views/Send/SendGeneral.tsx index 213609c74..7ac49aeb5 100644 --- a/packages/wallet-widget/src/views/Send/SendGeneral.tsx +++ b/packages/wallet-widget/src/views/Send/SendGeneral.tsx @@ -1,79 +1,30 @@ import { useWallets } from '@0xsequence/connect' -import { TabsContent, TabsHeader, TabsRoot } from '@0xsequence/design-system' -import { useState } from 'react' +import { useEffect } from 'react' +import { useAccount } from 'wagmi' -import { CollectiblesList, TokenList } from '../../components/SearchLists/index.js' -import { useGetAllTokensDetails, useNavigation, useSettings } from '../../hooks/index.js' -import type { TokenBalanceWithPrice } from '../../utils/tokens.js' +import { GeneralList } from '../../components/SearchLists/index.js' +import { WalletSelect } from '../../components/Select/WalletSelect.js' +import { useNavigationHeader } from '../../hooks/useNavigationHeader.js' export const SendGeneral = () => { - const { setNavigation } = useNavigation() - const { wallets } = useWallets() - const { allNetworks, hideUnlistedTokens } = useSettings() - const [selectedTab, setSelectedTab] = useState<'coins' | 'collectibles'>('coins') + const { setActiveWallet } = useWallets() + const { address } = useAccount() + const { setSelectedTab } = useNavigationHeader() - const activeWallet = wallets.find(wallet => wallet.isActive) - - const { data: tokenBalancesData = [], isLoading: isLoadingTokenBalances } = useGetAllTokensDetails({ - accountAddresses: [activeWallet?.address || ''], - chainIds: allNetworks, - contractWhitelist: [], - hideUnlistedTokens - }) - - const handleTokenClick = (token: TokenBalanceWithPrice) => { - setNavigation({ - location: 'send-coin', - params: { - chainId: token.chainId, - contractAddress: token.contractAddress - } - }) + const onClickWallet = (address: string) => { + setActiveWallet(address) } - const handleCollectibleClick = (token: TokenBalanceWithPrice) => { - setNavigation({ - location: 'send-collectible', - params: { - chainId: token.chainId, - contractAddress: token.contractAddress, - tokenId: token.tokenID || '' - } - }) - } + useEffect(() => { + setSelectedTab('tokens') + }, []) return ( -
- setSelectedTab(value as 'coins' | 'collectibles')} - > - - - - - - - - +
+
+ +
+
) } diff --git a/packages/wallet-widget/src/views/Settings/Menu.tsx b/packages/wallet-widget/src/views/Settings/Menu.tsx index dac5631c1..ab20d2bbf 100644 --- a/packages/wallet-widget/src/views/Settings/Menu.tsx +++ b/packages/wallet-widget/src/views/Settings/Menu.tsx @@ -1,14 +1,13 @@ import { useWallets } from '@0xsequence/connect' -import { CurrencyIcon, NetworkIcon, Text, WalletIcon } from '@0xsequence/design-system' +import { CurrencyIcon, Text, WalletIcon } from '@0xsequence/design-system' import { StackedIconTag } from '../../components/IconWrappers/StackedIconTag.js' import { ListCardNav } from '../../components/ListCard/ListCardNav.js' -import { HEADER_HEIGHT } from '../../constants/index.js' import { useNavigation, useSettings } from '../../hooks/index.js' export const SettingsMenu = () => { const { wallets } = useWallets() - const { selectedNetworks, fiatCurrency } = useSettings() + const { fiatCurrency } = useSettings() // const activeWallet = wallets.find(wallet => wallet.isActive) // const isEmbedded = activeWallet?.id.includes('waas') @@ -20,12 +19,6 @@ export const SettingsMenu = () => { }) } - const onClickNetworks = () => { - setNavigation({ - location: 'settings-networks' - }) - } - // const onClickProfiles = () => { // setNavigation({ // location: 'settings-profiles' @@ -59,15 +52,6 @@ export const SettingsMenu = () => { /> ) - const networksPreview = ( - {selectedNetworks.length}} - iconList={selectedNetworks.map(network => { - return `https://assets.sequence.info/images/networks/medium/${network}.webp` - })} - /> - ) - const currencyPreview = ( {fiatCurrency.symbol} {fiatCurrency.sign} @@ -75,7 +59,7 @@ export const SettingsMenu = () => { ) return ( -
+
@@ -83,12 +67,6 @@ export const SettingsMenu = () => { Manage Wallets - - - - Manage Networks - - diff --git a/packages/wallet-widget/src/views/Settings/Networks.tsx b/packages/wallet-widget/src/views/Settings/Networks.tsx deleted file mode 100644 index 185570394..000000000 --- a/packages/wallet-widget/src/views/Settings/Networks.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { NetworksFilter } from '../../components/Filter/NetworksFilter.js' - -export const SettingsNetworks = () => { - return ( -
- -
- ) -} diff --git a/packages/wallet-widget/src/views/Settings/Preferences.tsx b/packages/wallet-widget/src/views/Settings/Preferences.tsx index f78c3992a..90b7afdb1 100644 --- a/packages/wallet-widget/src/views/Settings/Preferences.tsx +++ b/packages/wallet-widget/src/views/Settings/Preferences.tsx @@ -2,7 +2,6 @@ import { Switch, Text } from '@0xsequence/design-system' import { useObservable } from 'micro-observables' import { ListCardSelect } from '../../components/ListCard/ListCardSelect.js' -import { HEADER_HEIGHT } from '../../constants/index.js' import { useSettings } from '../../hooks/index.js' export const SettingsPreferences = () => { @@ -10,7 +9,7 @@ export const SettingsPreferences = () => { const hideUnlistedTokens = useObservable(hideUnlistedTokensObservable) return ( -
+
} diff --git a/packages/wallet-widget/src/views/Settings/QrScan.tsx b/packages/wallet-widget/src/views/Settings/QrScan.tsx index 79386f1dd..9c9b7875f 100644 --- a/packages/wallet-widget/src/views/Settings/QrScan.tsx +++ b/packages/wallet-widget/src/views/Settings/QrScan.tsx @@ -2,7 +2,6 @@ // import { Scanner } from '@yudiel/react-qr-scanner' // import { ChangeEvent, useState } from 'react' -// import { HEADER_HEIGHT } from '../../constants' // // import { useWalletConnectContext } from '../../contexts/WalletConnect' // import { useNavigation } from '../../hooks' @@ -26,7 +25,7 @@ // } // return ( -//
+//
// // Connect to a DApp using WalletConnect // diff --git a/packages/wallet-widget/src/views/Settings/Wallets.tsx b/packages/wallet-widget/src/views/Settings/Wallets.tsx index 23046e60c..738929584 100644 --- a/packages/wallet-widget/src/views/Settings/Wallets.tsx +++ b/packages/wallet-widget/src/views/Settings/Wallets.tsx @@ -3,7 +3,6 @@ import { cardVariants, CheckmarkIcon, CloseIcon, cn, Divider, IconButton, Spinne import { useState } from 'react' import { CopyButton } from '../../components/CopyButton.js' -import { MediaIconWrapper } from '../../components/IconWrappers/index.js' import { ListCardSelect } from '../../components/ListCard/ListCardSelect.js' import { WalletAccountGradient } from '../../components/WalletAccountGradient.js' @@ -51,8 +50,8 @@ export const SettingsWallets = () => { } return ( -
-
+
+
{wallets.length > 1 && ( { ) } > - wallet.address)} size="sm" isAccount /> + wallet.address)} /> All @@ -95,10 +94,10 @@ export const SettingsWallets = () => { ) } > - + {formatAddress(wallet.address)} - + ))} diff --git a/packages/wallet-widget/src/views/Settings/index.ts b/packages/wallet-widget/src/views/Settings/index.ts index 0eeac9e57..b6757fb21 100644 --- a/packages/wallet-widget/src/views/Settings/index.ts +++ b/packages/wallet-widget/src/views/Settings/index.ts @@ -3,6 +3,5 @@ export * from './Wallets.js' export * from './Profiles.js' export * from './Apps.js' export * from './Currency.js' -export * from './Networks.js' export * from './Preferences.js' // export * from './QrScan' diff --git a/packages/wallet-widget/src/views/Swap/CoinInput.tsx b/packages/wallet-widget/src/views/Swap/CoinInput.tsx index 8efe6cb02..ee8772e29 100644 --- a/packages/wallet-widget/src/views/Swap/CoinInput.tsx +++ b/packages/wallet-widget/src/views/Swap/CoinInput.tsx @@ -15,7 +15,7 @@ export const CoinInput = ({ type, disabled }: { type: 'from' | 'to'; disabled?: const fiatBalance = formatFiatBalance( type === recentInput ? amount : nonRecentAmount, - coin?.price.value || 0, + coin?.price?.value || 0, coin?.contractInfo?.decimals || 18, fiatCurrency.sign ) diff --git a/packages/wallet-widget/src/views/Swap/CoinSelect.tsx b/packages/wallet-widget/src/views/Swap/CoinSelect.tsx index 132d45e92..163ce444e 100644 --- a/packages/wallet-widget/src/views/Swap/CoinSelect.tsx +++ b/packages/wallet-widget/src/views/Swap/CoinSelect.tsx @@ -7,7 +7,7 @@ import { CoinRow } from '../../components/SearchLists/TokenList/CoinRow.js' import { SlideupDrawer } from '../../components/Select/SlideupDrawer.js' import { useSettings, useSwap } from '../../hooks/index.js' import { formatTokenInfo } from '../../utils/formatBalance.js' -import type { TokenBalanceWithPrice } from '../../utils/index.js' +import type { TokenBalanceWithDetails } from '../../utils/index.js' export const CoinSelect = ({ selectType, @@ -15,7 +15,7 @@ export const CoinSelect = ({ disabled }: { selectType: 'from' | 'to' - coinList: TokenBalanceWithPrice[] + coinList: TokenBalanceWithDetails[] disabled?: boolean }) => { const { fromCoin, toCoin, setFromCoin, setToCoin } = useSwap() @@ -29,7 +29,7 @@ export const CoinSelect = ({ const { logo, name, symbol, displayBalance } = formatTokenInfo(selectedCoin, fiatCurrency.sign, chains) - const handleSelect = (coin: TokenBalanceWithPrice) => { + const handleSelect = (coin: TokenBalanceWithDetails) => { setSelectedCoin(coin) setIsSelectorOpen(false) } @@ -67,7 +67,7 @@ export const CoinSelect = ({ {isSelectorOpen && ( - setIsSelectorOpen(false)}> + setIsSelectorOpen(false)}>
{coinList.map((coin, index) => ( diff --git a/packages/wallet-widget/src/views/Swap/Swap.tsx b/packages/wallet-widget/src/views/Swap/Swap.tsx index d95a878de..142284bf8 100644 --- a/packages/wallet-widget/src/views/Swap/Swap.tsx +++ b/packages/wallet-widget/src/views/Swap/Swap.tsx @@ -4,7 +4,6 @@ import { useEffect } from 'react' import { useAccount, useChainId } from 'wagmi' import { NetworkSelect } from '../../components/Select/NetworkSelect.js' -import { HEADER_HEIGHT_WITH_LABEL } from '../../constants/index.js' import { useSettings, useSwap } from '../../hooks/index.js' import { CoinInput } from './CoinInput.js' @@ -76,7 +75,7 @@ export const Swap = () => { }) return ( -
+
diff --git a/packages/wallet-widget/src/views/SwapCoin/SwapList.tsx b/packages/wallet-widget/src/views/SwapCoin/SwapList.tsx index d34e69e1d..2ff871b38 100644 --- a/packages/wallet-widget/src/views/SwapCoin/SwapList.tsx +++ b/packages/wallet-widget/src/views/SwapCoin/SwapList.tsx @@ -21,7 +21,6 @@ import { formatUnits, zeroAddress, type Hex } from 'viem' import { useAccount, useChainId, usePublicClient, useSwitchChain, useWalletClient } from 'wagmi' import { EVENT_SOURCE, EVENT_TYPES } from '../../constants/analytics.js' -import { HEADER_HEIGHT } from '../../constants/index.js' import { useNavigation } from '../../hooks/index.js' interface SwapListProps { @@ -353,7 +352,7 @@ export const SwapList = ({ chainId, contractAddress, amount, slippageBps }: Swap } return ( -
+
) diff --git a/packages/wallet-widget/src/views/SwapCoin/index.tsx b/packages/wallet-widget/src/views/SwapCoin/index.tsx index d5d258ee0..1f436894b 100644 --- a/packages/wallet-widget/src/views/SwapCoin/index.tsx +++ b/packages/wallet-widget/src/views/SwapCoin/index.tsx @@ -7,7 +7,6 @@ import { parseUnits, zeroAddress } from 'viem' import { useAccount, useConfig } from 'wagmi' import { SendItemInfo } from '../../components/SendItemInfo.js' -import { HEADER_HEIGHT } from '../../constants/index.js' import { useNavigation, useSettings } from '../../hooks/index.js' import { computeBalanceFiat, limitDecimals } from '../../utils/index.js' @@ -89,13 +88,7 @@ export const SwapCoin = ({ contractAddress, chainId }: SwapCoinProps) => { const isNonZeroAmount = amountRaw > 0n return ( - +
{ {transaction.txnHash} - +
diff --git a/packages/wallet-widget/src/views/index.ts b/packages/wallet-widget/src/views/index.ts index 2293133e8..97d5a4010 100644 --- a/packages/wallet-widget/src/views/index.ts +++ b/packages/wallet-widget/src/views/index.ts @@ -1,8 +1,7 @@ export * from './Home/index.js' -export * from './Receive.js' +export * from './Receive/index.js' export * from './Send/index.js' export * from './History.js' -export * from './Search/index.js' export * from './Settings/index.js' export * from './CoinDetails/index.js' export * from './CollectibleDetails/index.js' @@ -10,3 +9,5 @@ export * from './TransactionDetails/index.js' export * from './SwapCoin/index.js' export * from './SwapCoin/SwapList.js' export * from './Swap/Swap.js' +export * from './Buy/index.js' +export * from './Search/index.js'