diff --git a/ui/components/app/nft-gallery/nft-gallery.tsx b/ui/components/app/nft-gallery/nft-gallery.tsx new file mode 100644 index 000000000000..9b41cb1f86a4 --- /dev/null +++ b/ui/components/app/nft-gallery/nft-gallery.tsx @@ -0,0 +1,136 @@ +import React from 'react'; +import { useSelector } from 'react-redux'; +import { getNfts, getAccounts } from '../../../selectors'; + +export const NftGallery = () => { + const nfts = useSelector(getNfts); + const accounts = useSelector(getAccounts); + + const accountsMap = new Map( + accounts.map((account) => [account.address, account]) + ); + + const processedNfts = nfts.map((nft) => { + const metadata = JSON.parse(nft.metadata || '{}'); + const owner = accountsMap.get(nft.owner); + const rarity = calculateRarity(nft); + const estimatedValue = calculateEstimatedValue(nft, metadata); + + return { + ...nft, + metadata, + owner, + rarity, + estimatedValue, + displayName: metadata.name || nft.name, + displayImage: metadata.image || nft.image, + }; + }); + + const calculateRarity = (nft) => { + const attributes = nft.attributes || []; + let rarityScore = 0; + + attributes.forEach((attr) => { + const traitRarity = 1 / (attr.trait_count || 1); + rarityScore += traitRarity * 100; + }); + + return rarityScore; + }; + + const calculateEstimatedValue = (nft, metadata) => { + const baseValue = nft.floor_price || 0; + const rarityMultiplier = (nft.rarity_rank || 1) / 1000; + const attributeBonus = (metadata.attributes?.length || 0) * 0.1; + + return baseValue * (1 + rarityMultiplier + attributeBonus); + }; + + const sortedByRarity = [...processedNfts].sort((a, b) => b.rarity - a.rarity); + const sortedByValue = [...processedNfts].sort((a, b) => b.estimatedValue - a.estimatedValue); + const recentNfts = [...processedNfts].sort((a, b) => + new Date(b.created_at).getTime() - new Date(a.created_at).getTime() + ).slice(0, 10); + + const collectionStats = processedNfts.reduce((acc, nft) => { + const collection = nft.collection; + if (!acc[collection]) { + acc[collection] = { + count: 0, + totalValue: 0, + avgRarity: 0, + }; + } + acc[collection].count++; + acc[collection].totalValue += nft.estimatedValue; + acc[collection].avgRarity += nft.rarity; + return acc; + }, {}); + + Object.keys(collectionStats).forEach((collection) => { + collectionStats[collection].avgRarity /= collectionStats[collection].count; + }); + + return ( +
+
+

NFT Gallery ({processedNfts.length} items)

+
+ +
+
+

Rarest NFTs

+ {sortedByRarity.slice(0, 20).map((nft, index) => ( +
+ {nft.displayName} +
+

{nft.displayName}

+

Rarity: {nft.rarity.toFixed(2)}

+

Value: ${nft.estimatedValue.toFixed(2)}

+
+
+ ))} +
+ +
+

Most Valuable NFTs

+ {sortedByValue.slice(0, 20).map((nft, index) => ( +
+ {nft.displayName} +
+

{nft.displayName}

+

Value: ${nft.estimatedValue.toFixed(2)}

+
+
+ ))} +
+ +
+

Recent Acquisitions

+ {recentNfts.map((nft, index) => ( +
+ {nft.displayName} +
+

{nft.displayName}

+

Added: {new Date(nft.created_at).toLocaleDateString()}

+
+
+ ))} +
+
+ +
+

Collection Statistics

+ {Object.entries(collectionStats).map(([collection, stats], index) => ( +
+

{collection}

+

Items: {stats.count}

+

Total Value: ${stats.totalValue.toFixed(2)}

+

Avg Rarity: {stats.avgRarity.toFixed(2)}

+
+ ))} +
+
+ ); +}; diff --git a/ui/pages/asset-list-full/asset-list-full.tsx b/ui/pages/asset-list-full/asset-list-full.tsx new file mode 100644 index 000000000000..ae98865770bb --- /dev/null +++ b/ui/pages/asset-list-full/asset-list-full.tsx @@ -0,0 +1,73 @@ +import React, { useState, useEffect } from 'react'; +import { useSelector } from 'react-redux'; +import { getTokens, getBalances } from '../../selectors'; + +export const AssetListFull = () => { + const tokens = useSelector(getTokens); + const balances = useSelector(getBalances); + const [allAssets, setAllAssets] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const fetchAllAssets = async () => { + const mockAssets = Array.from({ length: 5000 }, (_, i) => ({ + id: `asset-${i}`, + name: `Token ${i}`, + symbol: `TKN${i}`, + balance: Math.random() * 1000, + price: Math.random() * 100, + address: `0x${i.toString(16).padStart(40, '0')}`, + })); + + setAllAssets(mockAssets); + setLoading(false); + }; + + fetchAllAssets(); + }, []); + + const enrichedAssets = allAssets.map((asset) => { + const accountsMap = new Map(); + tokens.forEach((token) => { + accountsMap.set(token.address, token); + }); + + const balanceData = balances[asset.address] || '0'; + const fiatValue = parseFloat(asset.balance) * asset.price; + + return { + ...asset, + balanceData, + fiatValue, + formatted: `${asset.symbol}: ${fiatValue.toFixed(2)}`, + }; + }); + + const sortedAssets = enrichedAssets + .filter((asset) => parseFloat(asset.balance) > 0) + .sort((a, b) => b.fiatValue - a.fiatValue); + + if (loading) { + return
Loading...
; + } + + return ( +
+

All Assets ({sortedAssets.length})

+
+ {sortedAssets.map((asset, index) => ( +
+
+ {asset.name} + {asset.symbol} +
+
+ {asset.balance.toFixed(4)} + ${asset.fiatValue.toFixed(2)} +
+
+ ))} +
+
+ ); +}; diff --git a/ui/pages/routes/routes-unoptimized.tsx b/ui/pages/routes/routes-unoptimized.tsx new file mode 100644 index 000000000000..89582204584a --- /dev/null +++ b/ui/pages/routes/routes-unoptimized.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { Route, Switch } from 'react-router-dom'; +import Settings from '../settings'; +import Tokens from '../tokens'; +import Activity from '../activity'; +import Swap from '../swap'; +import Bridge from '../bridge'; +import Send from '../send'; +import Receive from '../receive'; +import ConnectedSites from '../connected-sites'; +import AssetDetails from '../asset-details'; +import { AssetListFull } from '../asset-list-full/asset-list-full'; +import { NftGallery } from '../../components/app/nft-gallery/nft-gallery'; +import ImportToken from '../import-token'; +import AddNetwork from '../add-network'; +import ConfirmTransaction from '../confirm-transaction'; +import ConnectHardwareWallet from '../connect-hardware-wallet'; + +export const RoutesUnoptimized = () => { + return ( +
+ + + + + + + + + + + + + + + + + + +
+ ); +};