1
- import { Table } from "reactstrap" ;
2
1
import React from "react" ;
3
2
import { useLocation } from "react-router-dom" ;
3
+ import { Alert , Spinner , Table } from "reactstrap" ;
4
4
import {
5
5
useMergedProblemMap ,
6
6
useProblemModelMap ,
7
7
useVirtualContestSubmissions ,
8
8
} from "../../../../api/APIClient" ;
9
- import { clipDifficulty , ordinalSuffixOf } from "../../../../utils" ;
10
- import { VirtualContestItem } from "../../types" ;
11
9
import { ProblemLink } from "../../../../components/ProblemLink" ;
12
- import { ProblemId , UserId } from "../../../../interfaces/Status" ;
13
- import {
10
+ import { TweetButton } from "../../../../components/TweetButton" ;
11
+ import MergedProblem from "../../../../interfaces/MergedProblem" ;
12
+ import ProblemModel , {
14
13
isProblemModelWithDifficultyModel ,
15
14
isProblemModelWithTimeModel ,
16
15
ProblemModelWithDifficultyModel ,
17
16
ProblemModelWithTimeModel ,
18
17
} from "../../../../interfaces/ProblemModel" ;
18
+ import { ProblemId , UserId } from "../../../../interfaces/Status" ;
19
+ import { clipDifficulty , ordinalSuffixOf } from "../../../../utils" ;
20
+ import { getCurrentUnixtimeInSecond } from "../../../../utils/DateUtil" ;
19
21
import {
20
22
calculatePerformances ,
21
23
makeBotRunners ,
22
24
} from "../../../../utils/RatingSystem" ;
23
- import { TweetButton } from "../../../../components/TweetButton" ;
24
- import { getCurrentUnixtimeInSecond } from "../../../../utils/DateUtil" ;
25
+ import { VirtualContestItem } from "../../types" ;
26
+ import { ContestTableRow } from "./ContestTableRow" ;
27
+ import { FirstAcceptanceRow } from "./FirstAcceptanceRow" ;
25
28
import {
26
29
calcUserTotalResult ,
27
30
compareTotalResult ,
28
31
ReducedProblemResult ,
29
32
UserTotalResult ,
30
33
} from "./ResultCalcUtil" ;
31
- import { ContestTableRow } from "./ContestTableRow " ;
32
- import { FirstAcceptanceRow } from "./FirstAcceptanceRow" ;
33
- import {
34
- compareProblem ,
35
- getPointOverrideMap ,
36
- getResultsByUserMap ,
37
- } from "./util" ;
34
+ import { compareProblem , getResultsByUserMap } from "./util " ;
35
+
36
+ interface VirtualContestProblem {
37
+ item : VirtualContestItem ;
38
+ title ?: string ;
39
+ contestId ?: string ;
40
+ }
38
41
39
42
interface Props {
40
43
readonly contestId : string ;
41
44
readonly contestTitle : string ;
42
45
readonly showRating : boolean ;
43
46
readonly showProblems : boolean ;
44
- readonly problems : {
45
- item : VirtualContestItem ;
46
- title ?: string ;
47
- contestId ?: string ;
48
- } [ ] ;
47
+ readonly problems : VirtualContestProblem [ ] ;
49
48
readonly enableEstimatedPerformances : boolean ;
50
49
readonly users : string [ ] ;
51
50
readonly start : number ;
@@ -56,25 +55,65 @@ interface Props {
56
55
readonly penaltySecond : number ;
57
56
}
58
57
59
- export const ContestTable = ( props : Props ) => {
60
- const {
61
- contestId,
62
- contestTitle,
63
- showRating,
64
- showProblems,
65
- problems,
66
- users,
67
- start,
68
- end,
69
- atCoderUserId,
70
- pinMe,
71
- penaltySecond,
72
- } = props ;
73
- const query = new URLSearchParams ( useLocation ( ) . search ) ;
74
- const showBots = ! ! query . get ( "bot" ) ;
75
- const problemModels = useProblemModelMap ( ) ;
76
- const { data : problemMap } = useMergedProblemMap ( ) ;
58
+ const getPerformanceByUserId = (
59
+ lookForUserId : string ,
60
+ sortedUserIds : UserId [ ] ,
61
+ performanceMap : Map < UserId , number >
62
+ ) => {
63
+ const index = sortedUserIds . indexOf ( lookForUserId ) ;
64
+ if ( index < 0 ) {
65
+ return undefined ;
66
+ }
67
+ let upper : number | undefined ;
68
+ for ( let i = index ; i < sortedUserIds . length ; i ++ ) {
69
+ const userId = sortedUserIds [ i ] ;
70
+ const performance = performanceMap . get ( userId ) ;
71
+ if ( performance !== undefined ) {
72
+ upper = performance ;
73
+ break ;
74
+ }
75
+ }
76
+
77
+ let lower : number | undefined ;
78
+ for ( let i = index ; i >= 0 ; i -- ) {
79
+ const userId = sortedUserIds [ i ] ;
80
+ const performance = performanceMap . get ( userId ) ;
81
+ if ( performance !== undefined ) {
82
+ lower = performance ;
83
+ break ;
84
+ }
85
+ }
77
86
87
+ if ( lower !== undefined && upper !== undefined ) {
88
+ return ( lower + upper ) / 2 ;
89
+ } else if ( lower !== undefined ) {
90
+ return lower ;
91
+ } else if ( upper !== undefined ) {
92
+ return upper ;
93
+ } else {
94
+ return undefined ;
95
+ }
96
+ } ;
97
+
98
+ const constructPointOverrideMap = < T extends { item : VirtualContestItem } > (
99
+ problems : T [ ]
100
+ ) => {
101
+ const pointOverrideMap = new Map < ProblemId , number > ( ) ;
102
+ problems . forEach ( ( { item } ) => {
103
+ const problemId = item . id ;
104
+ const point = item . point ;
105
+ if ( point !== null ) {
106
+ pointOverrideMap . set ( problemId , point ) ;
107
+ }
108
+ } ) ;
109
+ return pointOverrideMap ;
110
+ } ;
111
+
112
+ const consolidateModels = (
113
+ problems : VirtualContestProblem [ ] ,
114
+ problemMap ?: Map < ProblemId , MergedProblem > ,
115
+ problemModels ?: Map < ProblemId , ProblemModel >
116
+ ) => {
78
117
const modelArray = [ ] as {
79
118
problemModel : ProblemModelWithDifficultyModel & ProblemModelWithTimeModel ;
80
119
problemId : string ;
@@ -91,27 +130,54 @@ export const ContestTable = (props: Props) => {
91
130
modelArray . push ( { problemModel, problemId, point } ) ;
92
131
}
93
132
} ) ;
133
+ return modelArray ;
134
+ } ;
94
135
136
+ export const ContestTable = ( props : Props ) => {
137
+ const {
138
+ contestId,
139
+ contestTitle,
140
+ showRating,
141
+ showProblems,
142
+ problems,
143
+ users,
144
+ start,
145
+ end,
146
+ atCoderUserId,
147
+ pinMe,
148
+ penaltySecond,
149
+ } = props ;
150
+ const query = new URLSearchParams ( useLocation ( ) . search ) ;
151
+ const showBots = ! ! query . get ( "bot" ) ;
152
+ const problemModels = useProblemModelMap ( ) ;
153
+ const { data : problemMap } = useMergedProblemMap ( ) ;
95
154
const submissions = useVirtualContestSubmissions (
96
155
props . users ,
97
156
problems . map ( ( p ) => p . item . id ) ,
98
157
start ,
99
158
end ,
100
- props . enableAutoRefresh ? 60_000 : 1_000_000_000
159
+ props . enableAutoRefresh
101
160
) ;
161
+ if ( ! submissions . data && ! submissions . error ) {
162
+ return < Spinner /> ;
163
+ }
164
+ if ( ! submissions . data ) {
165
+ return < Alert color = "danger" > Failed to fetch submissions.</ Alert > ;
166
+ }
102
167
103
- const pointOverrideMap = getPointOverrideMap ( problems ) ;
168
+ const modelArray = consolidateModels ( problems , problemMap , problemModels ) ;
169
+ const pointOverrideMap = constructPointOverrideMap ( problems ) ;
104
170
const resultsByUser = getResultsByUserMap (
105
- submissions ?? [ ] ,
171
+ submissions . data ,
106
172
users ,
107
173
( problemId ) => pointOverrideMap . get ( problemId )
108
174
) ;
109
175
110
- const currentSecond = Math . floor ( new Date ( ) . getTime ( ) / 1000 ) ;
176
+ const now = getCurrentUnixtimeInSecond ( ) ;
111
177
const showEstimatedPerformances =
112
178
props . enableEstimatedPerformances &&
113
179
modelArray . length === problems . length &&
114
- currentSecond >= start ;
180
+ now >= start ;
115
181
const botRunnerIds = new Set < UserId > ( ) ;
116
182
const ratingMap = new Map < UserId , number > ( ) ;
117
183
if ( showEstimatedPerformances ) {
@@ -157,42 +223,6 @@ export const ContestTable = (props: Props) => {
157
223
}
158
224
}
159
225
160
- const getPerformanceByUserId = ( lookForUserId : string ) => {
161
- const index = sortedUserIds . indexOf ( lookForUserId ) ;
162
- if ( index < 0 ) {
163
- return undefined ;
164
- }
165
- let upper : number | undefined ;
166
- for ( let i = index ; i < sortedUserIds . length ; i ++ ) {
167
- const userId = sortedUserIds [ i ] ;
168
- const performance = performanceMap . get ( userId ) ;
169
- if ( performance !== undefined ) {
170
- upper = performance ;
171
- break ;
172
- }
173
- }
174
-
175
- let lower : number | undefined ;
176
- for ( let i = index ; i >= 0 ; i -- ) {
177
- const userId = sortedUserIds [ i ] ;
178
- const performance = performanceMap . get ( userId ) ;
179
- if ( performance !== undefined ) {
180
- lower = performance ;
181
- break ;
182
- }
183
- }
184
-
185
- if ( lower !== undefined && upper !== undefined ) {
186
- return ( lower + upper ) / 2 ;
187
- } else if ( lower !== undefined ) {
188
- return lower ;
189
- } else if ( upper !== undefined ) {
190
- return upper ;
191
- } else {
192
- return undefined ;
193
- }
194
- } ;
195
-
196
226
const showingUserIds = sortedUserIds . filter (
197
227
( userId ) => ! botRunnerIds . has ( userId ) || showBots
198
228
) ;
@@ -209,21 +239,18 @@ export const ContestTable = (props: Props) => {
209
239
} ) )
210
240
. sort ( compareProblem ) ;
211
241
212
- const now = getCurrentUnixtimeInSecond ( ) ;
213
-
214
242
const loginUserRank = loginUserIndex + 1 ;
215
- const tweetButton =
216
- end < now ? (
217
- < TweetButton
218
- id = { contestId }
219
- text = { `${ atCoderUserId } took ${ loginUserRank } ${ ordinalSuffixOf (
220
- loginUserRank
221
- ) } place in ${ contestTitle } !`}
222
- color = "link"
223
- >
224
- Share it!
225
- </ TweetButton >
226
- ) : undefined ;
243
+ const tweetButton = end < now && (
244
+ < TweetButton
245
+ id = { contestId }
246
+ text = { `${ atCoderUserId } took ${ loginUserRank } ${ ordinalSuffixOf (
247
+ loginUserRank
248
+ ) } place in ${ contestTitle } !`}
249
+ color = "link"
250
+ >
251
+ Share it!
252
+ </ TweetButton >
253
+ ) ;
227
254
228
255
return (
229
256
< Table striped bordered size = "sm" >
@@ -259,7 +286,11 @@ export const ContestTable = (props: Props) => {
259
286
showRating = { showRating }
260
287
showProblems = { showProblems }
261
288
start = { start }
262
- estimatedPerformance = { getPerformanceByUserId ( atCoderUserId ) }
289
+ estimatedPerformance = { getPerformanceByUserId (
290
+ atCoderUserId ,
291
+ sortedUserIds ,
292
+ performanceMap
293
+ ) }
263
294
reducedProblemResults = {
264
295
resultsByUser . get ( atCoderUserId ) ??
265
296
new Map < ProblemId , ReducedProblemResult > ( )
@@ -271,15 +302,19 @@ export const ContestTable = (props: Props) => {
271
302
{ showingUserIds . map ( ( userId , i ) => {
272
303
return (
273
304
< ContestTableRow
274
- tweetButton = { atCoderUserId === userId ? tweetButton : undefined }
305
+ tweetButton = { atCoderUserId === userId && tweetButton }
275
306
key = { userId }
276
307
userId = { userId }
277
308
rank = { i }
278
309
sortedItems = { sortedItems }
279
310
showRating = { showRating }
280
311
showProblems = { showProblems }
281
312
start = { start }
282
- estimatedPerformance = { getPerformanceByUserId ( userId ) }
313
+ estimatedPerformance = { getPerformanceByUserId (
314
+ userId ,
315
+ sortedUserIds ,
316
+ performanceMap
317
+ ) }
283
318
reducedProblemResults = {
284
319
resultsByUser . get ( userId ) ??
285
320
new Map < ProblemId , ReducedProblemResult > ( )
0 commit comments