@@ -10,6 +10,14 @@ import React, {
10
10
import { motion } from 'framer-motion'
11
11
import { Tournament } from 'src/components'
12
12
import { AnalysisListContext } from 'src/contexts'
13
+ import { getAnalysisGameList } from 'src/api'
14
+
15
+ interface GameData {
16
+ game_id : string
17
+ maia_name : string
18
+ result : string
19
+ player_color : 'white' | 'black'
20
+ }
13
21
14
22
interface AnalysisGameListProps {
15
23
currentId : string [ ] | null
@@ -43,6 +51,13 @@ export const AnalysisGameList: React.FC<AnalysisGameListProps> = ({
43
51
analysisTournamentList,
44
52
} = useContext ( AnalysisListContext )
45
53
54
+ const [ currentPage , setCurrentPage ] = useState ( 1 )
55
+ const [ totalPages , setTotalPages ] = useState ( 1 )
56
+ const [ loading , setLoading ] = useState ( false )
57
+ const [ localPlayGames , setLocalPlayGames ] = useState ( analysisPlayList )
58
+ const [ localHandGames , setLocalHandGames ] = useState ( analysisHandList )
59
+ const [ localBrainGames , setLocalBrainGames ] = useState ( analysisBrainList )
60
+
46
61
const listKeys = useMemo ( ( ) => {
47
62
return analysisTournamentList
48
63
? Array . from ( analysisTournamentList . keys ( ) ) . sort (
@@ -78,6 +93,58 @@ export const AnalysisGameList: React.FC<AnalysisGameListProps> = ({
78
93
setLoadingIndex ( null )
79
94
} , [ selected ] )
80
95
96
+ useEffect ( ( ) => {
97
+ if ( selected !== 'tournament' && selected !== 'pgn' ) {
98
+ setLoading ( true )
99
+ getAnalysisGameList ( selected , currentPage ) . then ( ( data ) => {
100
+ const parse = (
101
+ game : {
102
+ game_id : string
103
+ maia_name : string
104
+ result : string
105
+ player_color : 'white' | 'black'
106
+ } ,
107
+ type : string ,
108
+ ) => {
109
+ const raw = game . maia_name . replace ( '_kdd_' , ' ' )
110
+ const maia = raw . charAt ( 0 ) . toUpperCase ( ) + raw . slice ( 1 )
111
+
112
+ return {
113
+ id : game . game_id ,
114
+ label :
115
+ game . player_color === 'white'
116
+ ? `You vs. ${ maia } `
117
+ : `${ maia } vs. You` ,
118
+ result : game . result ,
119
+ type,
120
+ }
121
+ }
122
+
123
+ if ( selected === 'play' ) {
124
+ setLocalPlayGames (
125
+ data . games . map ( ( game : GameData ) => parse ( game , 'play' ) ) ,
126
+ )
127
+ } else if ( selected === 'hand' ) {
128
+ setLocalHandGames (
129
+ data . games . map ( ( game : GameData ) => parse ( game , 'hand' ) ) ,
130
+ )
131
+ } else if ( selected === 'brain' ) {
132
+ setLocalBrainGames (
133
+ data . games . map ( ( game : GameData ) => parse ( game , 'brain' ) ) ,
134
+ )
135
+ }
136
+ setTotalPages ( Math . ceil ( data . total / 100 ) )
137
+ setLoading ( false )
138
+ } )
139
+ }
140
+ } , [ selected , currentPage ] )
141
+
142
+ const handlePageChange = ( newPage : number ) => {
143
+ if ( newPage >= 1 && newPage <= totalPages ) {
144
+ setCurrentPage ( newPage )
145
+ }
146
+ }
147
+
81
148
return analysisTournamentList ? (
82
149
< div className = "flex h-full flex-col items-start justify-start overflow-hidden bg-background-1 md:rounded" >
83
150
< div className = "flex h-full w-full flex-col" >
@@ -114,71 +181,127 @@ export const AnalysisGameList: React.FC<AnalysisGameListProps> = ({
114
181
/>
115
182
</ div >
116
183
< div className = "red-scrollbar flex h-full flex-col overflow-y-scroll" >
117
- { selected === 'tournament' ? (
118
- < >
119
- { listKeys . map ( ( id , i ) => (
120
- < Tournament
121
- key = { i }
122
- id = { id }
123
- index = { i }
124
- openIndex = { openIndex }
125
- currentId = { currentId }
126
- openElement = { openElement as React . RefObject < HTMLDivElement > }
127
- setOpenIndex = { setOpenIndex }
128
- loadingIndex = { loadingIndex }
129
- setLoadingIndex = { setLoadingIndex }
130
- selectedGameElement = {
131
- selectedGameElement as React . RefObject < HTMLButtonElement >
132
- }
133
- loadNewTournamentGame = { loadNewTournamentGame }
134
- analysisTournamentList = { analysisTournamentList }
135
- />
136
- ) ) }
137
- </ >
184
+ { loading ? (
185
+ < div className = "flex h-full items-center justify-center" >
186
+ < div className = "h-8 w-8 animate-spin rounded-full border-b-2 border-white" > </ div >
187
+ </ div >
138
188
) : (
139
189
< >
140
- { ( selected === 'play'
141
- ? analysisPlayList
142
- : selected === 'hand'
143
- ? analysisHandList
144
- : selected === 'brain'
145
- ? analysisBrainList
146
- : analysisLichessList
147
- ) . map ( ( game , index ) => {
148
- const selectedGame = currentId && currentId [ 0 ] === game . id
149
- return (
150
- < button
151
- key = { index }
152
- onClick = { async ( ) => {
153
- setLoadingIndex ( index )
154
- if ( game . type === 'pgn' ) {
155
- await loadNewLichessGames ( game . id , game . pgn as string )
156
- } else {
157
- await loadNewUserGames (
158
- game . id ,
159
- game . type as 'play' | 'hand' | 'brain' ,
160
- )
190
+ { selected === 'tournament' ? (
191
+ < >
192
+ { listKeys . map ( ( id , i ) => (
193
+ < Tournament
194
+ key = { i }
195
+ id = { id }
196
+ index = { i }
197
+ openIndex = { openIndex }
198
+ currentId = { currentId }
199
+ openElement = {
200
+ openElement as React . RefObject < HTMLDivElement >
161
201
}
162
- setLoadingIndex ( null )
163
- } }
164
- className = { `group flex w-full cursor-pointer items-center gap-2 pr-1 ${ selectedGame ? 'bg-background-2 font-bold' : index % 2 === 0 ? 'bg-background-1/30 hover:bg-background-2' : 'bg-background-1/10 hover:bg-background-2' } ` }
165
- >
166
- < div
167
- className = { `flex h-full w-9 items-center justify-center ${ selectedGame ? 'bg-background-3' : 'bg-background-2 group-hover:bg-white/5' } ` }
168
- >
169
- < p className = "text-sm text-secondary" > { index + 1 } </ p >
170
- </ div >
171
- < div className = "flex flex-1 items-center justify-between overflow-hidden py-1" >
172
- < p className = "overflow-hidden text-ellipsis whitespace-nowrap text-sm text-primary" >
173
- { game . label }
174
- </ p >
175
- < p className = "whitespace-nowrap text-sm font-light text-secondary" >
176
- { game . result }
177
- </ p >
202
+ setOpenIndex = { setOpenIndex }
203
+ loadingIndex = { loadingIndex }
204
+ setLoadingIndex = { setLoadingIndex }
205
+ selectedGameElement = {
206
+ selectedGameElement as React . RefObject < HTMLButtonElement >
207
+ }
208
+ loadNewTournamentGame = { loadNewTournamentGame }
209
+ analysisTournamentList = { analysisTournamentList }
210
+ />
211
+ ) ) }
212
+ </ >
213
+ ) : (
214
+ < >
215
+ { ( selected === 'play'
216
+ ? localPlayGames
217
+ : selected === 'hand'
218
+ ? localHandGames
219
+ : selected === 'brain'
220
+ ? localBrainGames
221
+ : analysisLichessList
222
+ ) . map ( ( game , index ) => {
223
+ const selectedGame = currentId && currentId [ 0 ] === game . id
224
+ return (
225
+ < button
226
+ key = { index }
227
+ onClick = { async ( ) => {
228
+ setLoadingIndex ( index )
229
+ if ( game . type === 'pgn' ) {
230
+ await loadNewLichessGames (
231
+ game . id ,
232
+ game . pgn as string ,
233
+ )
234
+ } else {
235
+ await loadNewUserGames (
236
+ game . id ,
237
+ game . type as 'play' | 'hand' | 'brain' ,
238
+ )
239
+ }
240
+ setLoadingIndex ( null )
241
+ } }
242
+ className = { `group flex w-full cursor-pointer items-center gap-2 pr-1 ${ selectedGame ? 'bg-background-2 font-bold' : index % 2 === 0 ? 'bg-background-1/30 hover:bg-background-2' : 'bg-background-1/10 hover:bg-background-2' } ` }
243
+ >
244
+ < div
245
+ className = { `flex h-full w-9 items-center justify-center ${ selectedGame ? 'bg-background-3' : 'bg-background-2 group-hover:bg-white/5' } ` }
246
+ >
247
+ < p className = "text-sm text-secondary" > { index + 1 } </ p >
248
+ </ div >
249
+ < div className = "flex flex-1 items-center justify-between overflow-hidden py-1" >
250
+ < p className = "overflow-hidden text-ellipsis whitespace-nowrap text-sm text-primary" >
251
+ { game . label }
252
+ </ p >
253
+ < p className = "whitespace-nowrap text-sm font-light text-secondary" >
254
+ { game . result }
255
+ </ p >
256
+ </ div >
257
+ </ button >
258
+ )
259
+ } ) }
260
+ { selected !== 'pgn' && totalPages > 1 && (
261
+ < div className = "flex items-center justify-center gap-2 py-2" >
262
+ < button
263
+ onClick = { ( ) => handlePageChange ( 1 ) }
264
+ disabled = { currentPage === 1 }
265
+ className = "flex items-center justify-center text-secondary hover:text-primary disabled:opacity-50"
266
+ >
267
+ < span className = "material-symbols-outlined" >
268
+ first_page
269
+ </ span >
270
+ </ button >
271
+ < button
272
+ onClick = { ( ) => handlePageChange ( currentPage - 1 ) }
273
+ disabled = { currentPage === 1 }
274
+ className = "flex items-center justify-center text-secondary hover:text-primary disabled:opacity-50"
275
+ >
276
+ < span className = "material-symbols-outlined" >
277
+ arrow_back_ios
278
+ </ span >
279
+ </ button >
280
+ < span className = "text-sm text-secondary" >
281
+ Page { currentPage } of { totalPages }
282
+ </ span >
283
+ < button
284
+ onClick = { ( ) => handlePageChange ( currentPage + 1 ) }
285
+ disabled = { currentPage === totalPages }
286
+ className = "flex items-center justify-center text-secondary hover:text-primary disabled:opacity-50"
287
+ >
288
+ < span className = "material-symbols-outlined" >
289
+ arrow_forward_ios
290
+ </ span >
291
+ </ button >
292
+ < button
293
+ onClick = { ( ) => handlePageChange ( totalPages ) }
294
+ disabled = { currentPage === totalPages }
295
+ className = "flex items-center justify-center text-secondary hover:text-primary disabled:opacity-50"
296
+ >
297
+ < span className = "material-symbols-outlined" >
298
+ last_page
299
+ </ span >
300
+ </ button >
178
301
</ div >
179
- </ button >
180
- )
181
- } ) }
302
+ ) }
303
+ </ >
304
+ ) }
182
305
</ >
183
306
) }
184
307
< div className = "flex flex-1 items-start justify-center gap-1 py-2 md:items-center" >
0 commit comments