Skip to content

Commit e850409

Browse files
authored
Merge pull request #1095 from MatsuTaku/contest-problem-status
VirtualContest Problems show user acceptances
2 parents 9a7005c + e448b2a commit e850409

File tree

2 files changed

+64
-8
lines changed

2 files changed

+64
-8
lines changed

atcoder-problems-frontend/src/pages/Internal/VirtualContest/ShowContest/ContestTable.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,9 @@ const getPerformanceByUserId = (
8989
}
9090
};
9191

92-
const constructPointOverrideMap = <T extends { item: VirtualContestItem }>(
92+
export const constructPointOverrideMap = <
93+
T extends { item: VirtualContestItem }
94+
>(
9395
problems: T[]
9496
) => {
9597
const pointOverrideMap = new Map<ProblemId, number>();

atcoder-problems-frontend/src/pages/Internal/VirtualContest/ShowContest/index.tsx

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ import {
2020
NavItem,
2121
NavLink,
2222
} from "reactstrap";
23-
import { useMergedProblemMap } from "../../../../api/APIClient";
23+
import { FcCancel, FcCheckmark } from "react-icons/all";
24+
import {
25+
useMergedProblemMap,
26+
useVirtualContestSubmissions,
27+
} from "../../../../api/APIClient";
2428
import {
2529
useLoginState,
2630
useVirtualContest,
@@ -45,19 +49,57 @@ import { generatePathWithParams } from "../../../../utils/QueryString";
4549
import { ProblemLink } from "../../../../components/ProblemLink";
4650
import { joinContest, leaveContest } from "../ApiClient";
4751
import { GoogleCalendarButton } from "../../../../components/GoogleCalendarButton";
48-
import { ContestTable } from "./ContestTable";
52+
import { ProblemId, UserId } from "../../../../interfaces/Status";
53+
import { constructPointOverrideMap, ContestTable } from "./ContestTable";
4954
import { LockoutContestTable } from "./LockoutContestTable";
5055
import { TrainingContestTable } from "./TrainingContestTable";
5156
import { compareProblem } from "./util";
57+
import {
58+
ReducedProblemResult,
59+
reduceUserContestResult,
60+
} from "./ResultCalcUtil";
61+
62+
const Problems = (props: {
63+
readonly problems: VirtualContestProblem[];
64+
readonly atCoderUserId: UserId;
65+
readonly start: number;
66+
readonly end: number;
67+
}) => {
68+
const submissions = useVirtualContestSubmissions(
69+
[props.atCoderUserId],
70+
props.problems.map((p) => p.item.id),
71+
props.start,
72+
props.end,
73+
false
74+
);
75+
const pointOverrideMap = constructPointOverrideMap(props.problems);
76+
const showUserResults =
77+
props.atCoderUserId !== "" &&
78+
submissions.data !== null &&
79+
submissions.data !== undefined;
80+
const results = submissions.data
81+
? reduceUserContestResult(submissions.data, (id) =>
82+
pointOverrideMap.get(id)
83+
)
84+
: new Map<UserId, ReducedProblemResult>();
85+
const ResultIcon = (props: { id: ProblemId }) => {
86+
const result = results.get(props.id);
87+
if (!result) return null;
88+
if (result.accepted) {
89+
return <FcCheckmark />;
90+
} else {
91+
return <FcCancel />;
92+
}
93+
};
5294

53-
const Problems = (props: { problems: VirtualContestProblem[] }) => {
5495
const sortedItems = props.problems
5596
.map((p) => ({
5697
contestId: p.contestId,
5798
title: p.title,
5899
...p.item,
59100
}))
60101
.sort(compareProblem);
102+
61103
return (
62104
<div className="my-2">
63105
<Row>
@@ -75,12 +117,13 @@ const Problems = (props: { problems: VirtualContestProblem[] }) => {
75117
</Row>
76118
<Row>
77119
<Col>
78-
<Table striped size="sm">
120+
<Table striped bordered size="sm">
79121
<thead>
80122
<tr>
81123
<th> </th>
82124
<th>Problem Name</th>
83125
<th className="text-center">Score</th>
126+
{showUserResults && <th> </th>}
84127
</tr>
85128
</thead>
86129
<tbody>
@@ -109,6 +152,11 @@ const Problems = (props: { problems: VirtualContestProblem[] }) => {
109152
)}
110153
</td>
111154
<td className="text-center">{p.point !== null && p.point}</td>
155+
{showUserResults && (
156+
<td className="text-center">
157+
<ResultIcon id={p.id} />
158+
</td>
159+
)}
112160
</tr>
113161
))}
114162
</tbody>
@@ -216,9 +264,15 @@ const NormalContestTabTypes = ["Problems", "Standings"] as const;
216264
type NormalContestTabType = typeof NormalContestTabTypes[number];
217265
const TAB_PARAM = "activeTab";
218266

219-
const NormalContestPage = (props: StandingsProps) => {
267+
interface NormalContestPageProps extends StandingsProps {
268+
readonly enableEstimatedPerformances: boolean;
269+
readonly atCoderUserId: string;
270+
readonly penaltySecond: number;
271+
}
272+
273+
const NormalContestPage = (props: NormalContestPageProps) => {
220274
const location = useLocation();
221-
const { showProblems, problems } = props;
275+
const { showProblems } = props;
222276
const tabs: NormalContestTabType[] = showProblems
223277
? ["Problems", "Standings"]
224278
: ["Standings"];
@@ -253,7 +307,7 @@ const NormalContestPage = (props: StandingsProps) => {
253307
</Col>
254308
</Row>
255309

256-
{activeTab === "Problems" && <Problems problems={problems} />}
310+
{activeTab === "Problems" && <Problems {...props} />}
257311
{activeTab === "Standings" && <Standings {...props} />}
258312
</>
259313
);

0 commit comments

Comments
 (0)