@@ -6,21 +6,7 @@ import { Pagination } from '@/ui/components/Pagination';
6
6
import { useExtensionIsInTab } from '@/ui/features/browser/tabs' ;
7
7
import { LoadingOutlined } from '@ant-design/icons' ;
8
8
9
- // Generic cache structure
10
- interface DataCache < T > {
11
- [ key : string ] : {
12
- items : T [ ] ;
13
- total : number ;
14
- timestamp : number ;
15
- } ;
16
- }
17
-
18
- // Global cache object
19
- const globalCache : Record < string , DataCache < any > > = { } ;
20
-
21
- export interface CachedListProps < T > {
22
- // Unique namespace for this list type (e.g. 'inscriptions', 'tokens', etc.)
23
- namespace : string ;
9
+ export interface VirtualListProps < T > {
24
10
// Current account address
25
11
address : string ;
26
12
// Current chain type
@@ -33,8 +19,6 @@ export interface CachedListProps<T> {
33
19
itemsPerRow ?: number ;
34
20
// Initial page size
35
21
pageSize ?: number ;
36
- // Cache expiration time in milliseconds
37
- cacheExpiration ?: number ;
38
22
// Error handler
39
23
onError ?: ( error : Error ) => void ;
40
24
// Empty state message
@@ -43,26 +27,16 @@ export interface CachedListProps<T> {
43
27
containerHeight ?: number ;
44
28
}
45
29
46
- // Custom event for data loaded
47
- const dispatchDataLoadedEvent = < T , > ( namespace : string , items : T [ ] ) => {
48
- const event = new CustomEvent ( `cachedList:${ namespace } :dataLoaded` , {
49
- detail : { items }
50
- } ) ;
51
- window . dispatchEvent ( event ) ;
52
- } ;
53
-
54
- export function CachedList < T > ( {
55
- namespace,
30
+ export function VirtualList < T > ( {
56
31
address,
57
32
chainType,
58
33
fetchData,
59
34
renderItem,
60
35
itemsPerRow = 2 ,
61
36
pageSize,
62
- cacheExpiration = 3 * 60 * 1000 , // 3 minutes default
63
37
onError,
64
38
emptyText = 'Empty'
65
- } : CachedListProps < T > ) {
39
+ } : VirtualListProps < T > ) {
66
40
const [ items , setItems ] = useState < T [ ] > ( [ ] ) ;
67
41
const [ total , setTotal ] = useState ( - 1 ) ;
68
42
const isInTab = useExtensionIsInTab ( ) ;
@@ -74,6 +48,7 @@ export function CachedList<T>({
74
48
const isLoadingRef = useRef ( false ) ;
75
49
const containerRef = useRef < HTMLDivElement > ( null ) ;
76
50
const prevChainTypeRef = useRef < string | null > ( null ) ;
51
+ const chainChangedRef = useRef ( false ) ;
77
52
78
53
useEffect ( ( ) => {
79
54
const hasViewModeChanged = prevIsInTabRef . current !== isInTab ;
@@ -84,31 +59,16 @@ export function CachedList<T>({
84
59
85
60
if ( prevPageSizeRef . current !== newPageSize ) {
86
61
prevPageSizeRef . current = newPageSize ;
87
-
88
62
setPagination ( { currentPage : 1 , pageSize : newPageSize } ) ;
89
63
90
- if ( globalCache [ namespace ] ) {
91
- Object . keys ( globalCache [ namespace ] ) . forEach ( ( key ) => {
92
- delete globalCache [ namespace ] [ key ] ;
93
- } ) ;
94
- }
95
-
96
64
if ( address ) {
97
65
setIsLoading ( true ) ;
98
66
isLoadingRef . current = true ;
99
67
100
68
fetchData ( address , 1 , newPageSize )
101
69
. then ( ( { list, total } ) => {
102
- const cacheKey = `${ address } _${ chainType } _1_${ newPageSize } ` ;
103
- globalCache [ namespace ] [ cacheKey ] = {
104
- items : list ,
105
- total,
106
- timestamp : Date . now ( )
107
- } ;
108
-
109
70
setItems ( list ) ;
110
71
setTotal ( total ) ;
111
- dispatchDataLoadedEvent ( namespace , list ) ;
112
72
} )
113
73
. catch ( ( e ) => {
114
74
if ( onError && e instanceof Error ) {
@@ -122,62 +82,23 @@ export function CachedList<T>({
122
82
}
123
83
}
124
84
}
125
- } , [ isInTab , address , namespace , chainType , fetchData , onError , pageSize ] ) ;
126
-
127
- // Initialize namespace cache if not exists
128
- if ( ! globalCache [ namespace ] ) {
129
- globalCache [ namespace ] = { } ;
130
- }
131
-
132
- // Get current cache key
133
- const getCacheKey = useCallback (
134
- ( page : number ) => {
135
- return `${ address } _${ chainType } _${ page } _${ pagination . pageSize } ` ;
136
- } ,
137
- [ address , chainType , pagination . pageSize ]
138
- ) ;
139
-
140
- // Check if cache is valid
141
- const isCacheValid = useCallback (
142
- ( cacheEntry : { timestamp : number } ) => {
143
- return Date . now ( ) - cacheEntry . timestamp < cacheExpiration ;
144
- } ,
145
- [ cacheExpiration ]
146
- ) ;
85
+ } , [ isInTab , address , fetchData , onError , pageSize ] ) ;
147
86
148
87
const loadData = useCallback ( async ( ) => {
149
88
if ( ! address || isLoadingRef . current ) return ;
150
89
151
- isLoadingRef . current = true ;
152
- setIsLoading ( true ) ;
153
-
154
- const cacheKey = getCacheKey ( pagination . currentPage ) ;
155
- const cachedData = globalCache [ namespace ] [ cacheKey ] ;
156
-
157
- if ( cachedData && isCacheValid ( cachedData ) ) {
158
- setItems ( cachedData . items ) ;
159
- setTotal ( cachedData . total ) ;
160
- setIsLoading ( false ) ;
161
- isLoadingRef . current = false ;
162
-
163
- // Dispatch data loaded event
164
- dispatchDataLoadedEvent ( namespace , cachedData . items ) ;
90
+ if ( chainChangedRef . current ) {
91
+ chainChangedRef . current = false ;
165
92
return ;
166
93
}
167
94
95
+ isLoadingRef . current = true ;
96
+ setIsLoading ( true ) ;
97
+
168
98
try {
169
99
const { list, total } = await fetchData ( address , pagination . currentPage , pagination . pageSize ) ;
170
-
171
- globalCache [ namespace ] [ cacheKey ] = {
172
- items : list ,
173
- total,
174
- timestamp : Date . now ( )
175
- } ;
176
-
177
100
setItems ( list ) ;
178
101
setTotal ( total ) ;
179
-
180
- dispatchDataLoadedEvent ( namespace , list ) ;
181
102
} catch ( e ) {
182
103
if ( onError && e instanceof Error ) {
183
104
onError ( e ) ;
@@ -186,42 +107,48 @@ export function CachedList<T>({
186
107
setIsLoading ( false ) ;
187
108
isLoadingRef . current = false ;
188
109
}
189
- } , [ address , chainType , pagination , namespace , getCacheKey , isCacheValid , fetchData , onError ] ) ;
110
+ } , [ address , pagination , fetchData , onError ] ) ;
190
111
191
- // Clear cache when account or chain type changes
192
112
useEffect ( ( ) => {
193
- if ( prevChainTypeRef . current !== null && prevChainTypeRef . current !== chainType ) {
113
+ if ( prevChainTypeRef . current !== chainType ) {
114
+ chainChangedRef . current = true ;
115
+
194
116
setPagination ( { currentPage : 1 , pageSize : effectivePageSize } ) ;
195
117
setItems ( [ ] ) ;
196
118
setTotal ( - 1 ) ;
197
119
if ( containerRef . current ) {
198
120
containerRef . current . scrollTop = 0 ;
199
121
}
200
122
201
- Object . keys ( globalCache [ namespace ] ) . forEach ( ( key ) => {
202
- delete globalCache [ namespace ] [ key ] ;
203
- } ) ;
204
-
205
- fetchData ( address , 1 , effectivePageSize )
206
- . then ( ( { list, total } ) => {
207
- setItems ( list ) ;
208
- setTotal ( total ) ;
209
- } )
210
- . catch ( ( e ) => {
211
- if ( onError && e instanceof Error ) {
212
- onError ( e ) ;
213
- }
214
- } ) ;
123
+ if ( ! isLoadingRef . current && address ) {
124
+ isLoadingRef . current = true ;
125
+ setIsLoading ( true ) ;
126
+
127
+ fetchData ( address , 1 , effectivePageSize )
128
+ . then ( ( { list, total } ) => {
129
+ setItems ( list ) ;
130
+ setTotal ( total ) ;
131
+ } )
132
+ . catch ( ( e ) => {
133
+ if ( onError && e instanceof Error ) {
134
+ onError ( e ) ;
135
+ }
136
+ } )
137
+ . finally ( ( ) => {
138
+ setIsLoading ( false ) ;
139
+ isLoadingRef . current = false ;
140
+ chainChangedRef . current = false ;
141
+ } ) ;
142
+ }
215
143
}
216
144
217
145
prevChainTypeRef . current = chainType ;
218
- } , [ chainType , address , namespace , effectivePageSize , fetchData , onError ] ) ;
146
+ } , [ chainType , address , effectivePageSize , fetchData , onError ] ) ;
219
147
220
148
useEffect ( ( ) => {
221
149
loadData ( ) ;
222
- } , [ pagination , address , loadData , chainType ] ) ;
150
+ } , [ pagination , address , loadData ] ) ;
223
151
224
- // Create grid rows
225
152
const gridRows = useMemo ( ( ) => {
226
153
const rows : T [ ] [ ] = [ ] ;
227
154
for ( let i = 0 ; i < items . length ; i += itemsPerRow ) {
0 commit comments