1- import React , { useCallback , useState } from 'react' ;
2- import { ScrollView } from 'react-native-gesture-handler ' ;
1+ import React , { useCallback , useMemo , useState } from 'react' ;
2+ import { FlatList , TouchableOpacity } from 'react-native' ;
33import { useNavigation } from '@react-navigation/native' ;
44import { SafeAreaView } from 'react-native-safe-area-context' ;
55import { useTailwind } from '@metamask/design-system-twrnc-preset' ;
@@ -18,68 +18,88 @@ import {
1818} from '@metamask/design-system-react-native' ;
1919import { strings } from '../../../../../locales/i18n' ;
2020import { TopTradersViewSelectorsIDs } from './TopTradersView.testIds' ;
21- import { TrendingTokenNetworkBottomSheet } from '../../../UI/Trending/components/TrendingTokensBottomSheet' ;
2221import {
2322 TraderRow ,
2423 TraderRowSkeleton ,
25- NetworkFilterButton ,
2624} from '../../Homepage/Sections/TopTraders/components' ;
2725import { useTopTraders } from '../../Homepage/Sections/TopTraders/hooks' ;
28- import type { NetworkFilterSelection } from '../../Homepage/Sections/TopTraders/types' ;
29- import type { CaipChainId } from '@metamask/utils' ;
3026
3127const SKELETON_COUNT = 5 ;
3228const SKELETON_KEYS = Array . from (
3329 { length : SKELETON_COUNT } ,
3430 ( _ , i ) => `top-trader-skeleton-${ i } ` ,
3531) ;
3632
37- /**
38- * TopTradersView — Social leaderboard detail screen.
39- *
40- * Displays the full ranked list of top-performing traders with
41- * network filtering and Follow / Following actions.
42- */
33+ type ChainFilter = 'all' | 'base' | 'solana' | 'ethereum' ;
34+
35+ const CHAIN_FILTERS : { key : ChainFilter ; label : string } [ ] = [
36+ { key : 'all' , label : 'All' } ,
37+ { key : 'base' , label : 'Base' } ,
38+ { key : 'solana' , label : 'Solana' } ,
39+ { key : 'ethereum' , label : 'Ethereum' } ,
40+ ] ;
41+
42+ interface ChainPillProps {
43+ label : string ;
44+ isSelected : boolean ;
45+ onPress : ( ) => void ;
46+ }
47+
48+ const ChainPill : React . FC < ChainPillProps > = ( {
49+ label,
50+ isSelected,
51+ onPress,
52+ } ) => (
53+ < TouchableOpacity
54+ onPress = { onPress }
55+ testID = { `chain-filter-${ label . toLowerCase ( ) } ` }
56+ >
57+ < Box
58+ twClassName = { `px-4 py-2 rounded-xl border ${
59+ isSelected ? 'bg-default border-white' : 'border-muted'
60+ } `}
61+ >
62+ < Text
63+ variant = { TextVariant . BodySm }
64+ fontWeight = { FontWeight . Medium }
65+ color = { isSelected ? TextColor . TextDefault : TextColor . TextMuted }
66+ >
67+ { label }
68+ </ Text >
69+ </ Box >
70+ </ TouchableOpacity >
71+ ) ;
72+
4373const TopTradersView = ( ) => {
4474 const navigation = useNavigation ( ) ;
4575 const tw = useTailwind ( ) ;
4676
47- const { traders, isLoading, toggleFollow } = useTopTraders ( ) ;
77+ const [ selectedChain , setSelectedChain ] = useState < ChainFilter > ( 'all' ) ;
78+
79+ const { traders, isLoading, toggleFollow } = useTopTraders ( { limit : 250 } ) ;
4880
49- const [ showNetworkBottomSheet , setShowNetworkBottomSheet ] = useState ( false ) ;
50- const [ selectedNetwork , setSelectedNetwork ] =
51- useState < NetworkFilterSelection > ( null ) ;
81+ const filteredTraders = useMemo ( ( ) => {
82+ const filtered =
83+ selectedChain === 'all'
84+ ? traders
85+ : traders . filter ( ( t ) => ( t . pnlPerChain [ selectedChain ] ?? 0 ) !== 0 ) ;
86+
87+ return filtered . slice ( 0 , 50 ) . map ( ( t , i ) => ( { ...t , rank : i + 1 } ) ) ;
88+ } , [ traders , selectedChain ] ) ;
5289
5390 const handleBack = useCallback ( ( ) => {
5491 navigation . goBack ( ) ;
5592 } , [ navigation ] ) ;
5693
5794 const handleSearchPress = useCallback ( ( ) => {
58- // Search UI will be wired when the leaderboard data layer ships.
59- } , [ ] ) ;
60-
61- const handleNetworkPress = useCallback ( ( ) => {
62- setShowNetworkBottomSheet ( true ) ;
63- } , [ ] ) ;
64-
65- const handleNetworkSelect = useCallback ( ( chainIds : CaipChainId [ ] | null ) => {
66- setSelectedNetwork ( chainIds ? chainIds [ 0 ] : null ) ;
95+ // Search UI will be wired in a future ticket.
6796 } , [ ] ) ;
6897
69- const handleNetworkBottomSheetClose = useCallback ( ( ) => {
70- setShowNetworkBottomSheet ( false ) ;
71- } , [ ] ) ;
72-
73- const selectedNetworkCaip = selectedNetwork
74- ? ( [ selectedNetwork ] as CaipChainId [ ] )
75- : null ;
76-
7798 return (
7899 < SafeAreaView
79100 style = { tw . style ( 'flex-1 bg-default' ) }
80101 testID = { TopTradersViewSelectorsIDs . CONTAINER }
81102 >
82- { /* Header row */ }
83103 < Box
84104 flexDirection = { BoxFlexDirection . Row }
85105 alignItems = { BoxAlignItems . Center }
@@ -100,7 +120,6 @@ const TopTradersView = () => {
100120 />
101121 </ Box >
102122
103- { /* Title */ }
104123 < Box twClassName = "px-4 pt-2 pb-3" >
105124 < Text
106125 variant = { TextVariant . HeadingLg }
@@ -111,40 +130,36 @@ const TopTradersView = () => {
111130 </ Text >
112131 </ Box >
113132
114- { /* Network filter */ }
115- < Box twClassName = "px-4 pb-3" >
116- < NetworkFilterButton
117- selectedNetwork = { selectedNetwork }
118- onPress = { handleNetworkPress }
119- testID = "top-traders-view-network-filter"
120- />
133+ < Box
134+ flexDirection = { BoxFlexDirection . Row }
135+ twClassName = "px-4 pb-3 justify-between"
136+ >
137+ { CHAIN_FILTERS . map ( ( { key, label } ) => (
138+ < ChainPill
139+ key = { key }
140+ label = { label }
141+ isSelected = { selectedChain === key }
142+ onPress = { ( ) => setSelectedChain ( key ) }
143+ />
144+ ) ) }
121145 </ Box >
122146
123- { /* Trader list */ }
124- < ScrollView
125- showsVerticalScrollIndicator = { false }
126- contentContainerStyle = { tw . style ( 'pb-6' ) }
127- testID = "top-traders-view-list"
128- >
129- { isLoading
130- ? SKELETON_KEYS . map ( ( key ) => < TraderRowSkeleton key = { key } /> )
131- : traders . map ( ( trader ) => (
132- < TraderRow
133- key = { trader . id }
134- trader = { trader }
135- onFollowPress = { toggleFollow }
136- />
137- ) ) }
138- </ ScrollView >
139-
140- { /* Network filter bottom sheet */ }
141- < TrendingTokenNetworkBottomSheet
142- isVisible = { showNetworkBottomSheet }
143- onClose = { handleNetworkBottomSheetClose }
144- onNetworkSelect = { handleNetworkSelect }
145- selectedNetwork = { selectedNetworkCaip }
146- networks = { [ ] }
147- />
147+ { isLoading ? (
148+ SKELETON_KEYS . map ( ( key ) => < TraderRowSkeleton key = { key } /> )
149+ ) : (
150+ < FlatList
151+ data = { filteredTraders }
152+ keyExtractor = { ( item ) => item . id }
153+ renderItem = { ( { item } ) => (
154+ < TraderRow trader = { item } onFollowPress = { toggleFollow } />
155+ ) }
156+ showsVerticalScrollIndicator = { false }
157+ contentContainerStyle = { tw . style ( 'pb-6' ) }
158+ testID = "top-traders-view-list"
159+ initialNumToRender = { 15 }
160+ windowSize = { 5 }
161+ />
162+ ) }
148163 </ SafeAreaView >
149164 ) ;
150165} ;
0 commit comments