Skip to content

Commit 6873ca2

Browse files
committed
Simplify leaderboard routing logic
1 parent bc8be3f commit 6873ca2

File tree

6 files changed

+53
-239
lines changed

6 files changed

+53
-239
lines changed

src/pages/academy/academyRoutes.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@ import { assessmentRegExp, gradingRegExp, teamRegExp } from 'src/features/academ
77
import { GuardedRoute } from 'src/routes/routeGuard';
88

99
import { store } from '../createStore';
10+
import {
11+
contestLeaderboardLoader,
12+
leaderboardLoader
13+
} from '../leaderboard/subcomponents/leaderboardUtils';
1014

1115
const notFoundPath = 'not_found';
1216

1317
const Assessment = () => import('../../commons/assessment/Assessment');
1418
const Game = () => import('./game/Game');
1519
const Sourcecast = () => import('../sourcecast/Sourcecast');
1620
const Achievement = () => import('../achievement/Achievement');
17-
const Leaderboard = () => import('../leaderboard/Leaderboard');
18-
const OverallLeaderboardWrapper = () =>
19-
import('../leaderboard/subcomponents/OverallLeaderboardWrapper');
21+
const OverallLeaderboard = () => import('../leaderboard/subcomponents/OverallLeaderboard');
2022
const ContestLeaderboardWrapper = () =>
2123
import('../leaderboard/subcomponents/ContestLeaderboardWrapper');
2224
const NotFound = () => import('../notFound/NotFound');
@@ -78,9 +80,18 @@ const getCommonAcademyRoutes = (): RouteObject[] => {
7880
},
7981
{ path: 'sourcecast/:sourcecastId?', lazy: Sourcecast },
8082
{ path: 'achievements/*', lazy: Achievement },
81-
{ path: 'leaderboard/overall', lazy: OverallLeaderboardWrapper },
82-
{ path: 'leaderboard/contests/*', lazy: ContestLeaderboardWrapper },
83-
{ path: 'leaderboard/*', lazy: Leaderboard },
83+
{
84+
path: 'leaderboard',
85+
loader: leaderboardLoader,
86+
children: [
87+
{ path: 'overall', lazy: OverallLeaderboard },
88+
{
89+
path: 'contests/:contestId?/:leaderboardType',
90+
loader: contestLeaderboardLoader,
91+
lazy: ContestLeaderboardWrapper
92+
}
93+
]
94+
},
8495
{ path: '*', lazy: NotFound }
8596
];
8697
};

src/pages/leaderboard/Leaderboard.tsx

Lines changed: 0 additions & 59 deletions
This file was deleted.

src/pages/leaderboard/subcomponents/ContestLeaderboard.tsx

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@ import LeaderboardExportButton from './LeaderboardExportButton';
1919
import LeaderboardPodium from './LeaderboardPodium';
2020

2121
type Props = {
22-
type: string;
23-
contestID: number;
22+
type: 'score' | 'popularvote';
23+
contest: LeaderboardContestDetails;
2424
};
2525

26-
const ContestLeaderboard: React.FC<Props> = ({ type, contestID }) => {
26+
const ContestLeaderboard: React.FC<Props> = ({
27+
type,
28+
contest: { contest_id: contestId, title: contestName }
29+
}) => {
2730
const courseID = useTypedSelector(store => store.session.courseId);
2831
const visibleEntries = useTypedSelector(
2932
store => store.session?.topContestLeaderboardDisplay ?? 10
@@ -37,17 +40,11 @@ const ContestLeaderboard: React.FC<Props> = ({ type, contestID }) => {
3740

3841
useEffect(() => {
3942
if (type === 'score') {
40-
dispatch(LeaderboardActions.getAllContestScores(contestID, visibleEntries));
43+
dispatch(LeaderboardActions.getAllContestScores(contestId, visibleEntries));
4144
} else {
42-
dispatch(LeaderboardActions.getAllContestPopularVotes(contestID, visibleEntries));
45+
dispatch(LeaderboardActions.getAllContestPopularVotes(contestId, visibleEntries));
4346
}
44-
}, [dispatch, contestID, type]);
45-
46-
// Retrieve contests (For dropdown)
47-
const contestDetails: LeaderboardContestDetails[] = useTypedSelector(
48-
store => store.leaderboard.contests
49-
);
50-
const contestName = contestDetails.find(contest => contest.contest_id === contestID)?.title;
47+
}, [dispatch, contestId, type]);
5148

5249
useEffect(() => {
5350
dispatch(LeaderboardActions.getContests());
@@ -161,10 +158,9 @@ const ContestLeaderboard: React.FC<Props> = ({ type, contestID }) => {
161158

162159
<div className="buttons-container">
163160
{/* Leaderboard Options Dropdown */}
164-
<LeaderboardDropdown contests={contestDetails} />
165-
161+
<LeaderboardDropdown />
166162
{/* Export Button */}
167-
<LeaderboardExportButton type={type} contest={contestName} contestID={contestID} />
163+
<LeaderboardExportButton type={type} contest={contestName} contestID={contestId} />
168164
</div>
169165

170166
{/* Leaderboard Table (Top 3) */}

src/pages/leaderboard/subcomponents/ContestLeaderboardWrapper.tsx

Lines changed: 6 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,15 @@
1-
import React, { useEffect } from 'react';
2-
import { useDispatch } from 'react-redux';
3-
import { Navigate, Route, useLocation, useParams } from 'react-router';
1+
import React from 'react';
2+
import { useParams } from 'react-router';
43
import { useTypedSelector } from 'src/commons/utils/Hooks';
5-
import LeaderboardActions from 'src/features/leaderboard/LeaderboardActions';
6-
import type { LeaderboardContestDetails } from 'src/features/leaderboard/LeaderboardTypes';
7-
import { SentryRoutes } from 'src/routes/routerConfig';
84

95
import NotFound from '../../notFound/NotFound';
106
import ContestLeaderboard from './ContestLeaderboard';
117

128
const ContestLeaderboardWrapper: React.FC = () => {
13-
const enableContestLeaderboard = useTypedSelector(
14-
store => store.session.enableContestLeaderboard
15-
);
16-
17-
const dispatch = useDispatch();
18-
const contestAssessments: LeaderboardContestDetails[] = useTypedSelector(
19-
store => store.leaderboard.contests
20-
);
21-
22-
useEffect(() => {
23-
dispatch(LeaderboardActions.getContests());
24-
}, [dispatch]);
25-
26-
const defaultContest = contestAssessments?.find(assessment => assessment.published) ?? null;
27-
28-
const courseId = useTypedSelector(store => store.session.courseId);
29-
const baseLink = `/courses/${courseId}/leaderboard`;
30-
31-
return (
32-
<SentryRoutes>
33-
<Route
34-
path="/:id/score"
35-
element={
36-
enableContestLeaderboard ? (
37-
<ContestLeaderboardWrapperHelper
38-
baseLink={baseLink}
39-
contestDetails={contestAssessments}
40-
type="score"
41-
/>
42-
) : (
43-
<NotFound />
44-
)
45-
}
46-
></Route>
47-
48-
<Route
49-
path="/:id/popularvote"
50-
element={
51-
enableContestLeaderboard ? (
52-
<ContestLeaderboardWrapperHelper
53-
baseLink={baseLink}
54-
contestDetails={contestAssessments}
55-
type="popularvote"
56-
/>
57-
) : (
58-
<NotFound />
59-
)
60-
}
61-
></Route>
62-
63-
<Route
64-
path="/"
65-
element={
66-
enableContestLeaderboard && defaultContest != null ? (
67-
<Navigate to={`${baseLink}/contests/${defaultContest.contest_id}/score`} />
68-
) : (
69-
<Navigate to={baseLink} />
70-
)
71-
}
72-
></Route>
73-
74-
<Route path="*" element={<Navigate to={baseLink} />}></Route>
75-
</SentryRoutes>
76-
);
77-
};
78-
79-
const ContestLeaderboardWrapperHelper: React.FC<{
80-
baseLink: string;
81-
contestDetails: LeaderboardContestDetails[];
82-
type: 'score' | 'popularvote';
83-
}> = ({ baseLink, contestDetails, type }) => {
84-
let { id } = useParams<{ id: string }>();
85-
const location = useLocation();
86-
87-
if (!id) {
88-
const match = location.pathname.match(/\/contests\/(\d+)\//);
89-
if (match) {
90-
id = match[1];
91-
}
92-
}
93-
94-
if (!contestDetails.length) {
95-
// Wait for contestDetails to load
96-
return null;
97-
}
98-
const contest = contestDetails.find(contest => contest.contest_id === parseInt(id ?? '', 10));
99-
100-
return contest !== undefined && contest.published ? (
101-
<ContestLeaderboard type={type} contestID={contest.contest_id} />
102-
) : (
103-
<Navigate to={baseLink} />
104-
);
9+
const { contestId, type } = useParams<{ contestId: string; type: 'score' | 'popularvote' }>();
10+
const contests = useTypedSelector(state => state.leaderboard.contests);
11+
const contest = contests.find(d => d.contest_id === parseInt(contestId!, 10));
12+
return contest ? <ContestLeaderboard type={type!} contest={contest} /> : <NotFound />;
10513
};
10614

10715
// react-router lazy loading

src/pages/leaderboard/subcomponents/LeaderboardDropdown.tsx

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,8 @@ import 'src/styles/Leaderboard.scss';
33
import React from 'react';
44
import { useLocation, useNavigate } from 'react-router';
55
import { useTypedSelector } from 'src/commons/utils/Hooks';
6-
import { LeaderboardContestDetails } from 'src/features/leaderboard/LeaderboardTypes';
76

8-
type Props = {
9-
contests: LeaderboardContestDetails[];
10-
};
11-
12-
const LeaderboardDropdown: React.FC<Props> = ({ contests }) => {
7+
const LeaderboardDropdown: React.FC = () => {
138
const enableOverallLeaderboard = useTypedSelector(
149
store => store.session.enableOverallLeaderboard
1510
);
@@ -28,6 +23,7 @@ const LeaderboardDropdown: React.FC<Props> = ({ contests }) => {
2823
};
2924

3025
const currentPath = location.pathname;
26+
const contests = useTypedSelector(state => state.leaderboard.contests);
3127
const publishedContests = enableContestLeaderboard
3228
? contests.filter(contest => contest.published)
3329
: [];
@@ -45,24 +41,23 @@ const LeaderboardDropdown: React.FC<Props> = ({ contests }) => {
4541
) : null
4642
}
4743

48-
{enableContestLeaderboard
49-
? publishedContests.map(contest => (
50-
<>
51-
<option
52-
key={`${contest.contest_id}-score`}
53-
value={`${baseLink}/contests/${contest.contest_id}/score`}
54-
>
55-
{contest.title} (Score)
56-
</option>
57-
<option
58-
key={`${contest.contest_id}-popularvote`}
59-
value={`${baseLink}/contests/${contest.contest_id}/popularvote`}
60-
>
61-
{contest.title} (Popular Vote)
62-
</option>
63-
</>
64-
))
65-
: null}
44+
{enableContestLeaderboard &&
45+
publishedContests.map(contest => (
46+
<>
47+
<option
48+
key={`${contest.contest_id}-score`}
49+
value={`${baseLink}/contests/${contest.contest_id}/score`}
50+
>
51+
{contest.title} (Score)
52+
</option>
53+
<option
54+
key={`${contest.contest_id}-popularvote`}
55+
value={`${baseLink}/contests/${contest.contest_id}/popularvote`}
56+
>
57+
{contest.title} (Popular Vote)
58+
</option>
59+
</>
60+
))}
6661
</select>
6762
</>
6863
);

src/pages/leaderboard/subcomponents/OverallLeaderboardWrapper.tsx

Lines changed: 0 additions & 37 deletions
This file was deleted.

0 commit comments

Comments
 (0)