Skip to content

Commit 85e73e3

Browse files
committed
populate topic and company filter data
1 parent bfb1a2f commit 85e73e3

15 files changed

+195
-58
lines changed

jupyterlab_leetcode/handlers/__init__.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22

33
from .base_handler import BaseHandler
44
from .cookie_handler import GetCookieHandler
5-
from .leetcode_handler import (CreateNotebookHandler, LeetCodeProfileHandler,
6-
LeetCodeQuestionHandler,
5+
from .leetcode_handler import (CreateNotebookHandler, LeetCodeCompanyHandlar,
6+
LeetCodeProfileHandler, LeetCodeQuestionHandler,
77
LeetCodeStatisticsHandler,
88
LeetCodeSubmissionCalendarHandlar,
9+
LeetCodeTopicHandlar,
910
LeetCodeWebSocketSubmitHandler,
1011
SubmitNotebookHandler)
1112

@@ -22,6 +23,8 @@ def setup_handlers(web_app):
2223
SubmitNotebookHandler,
2324
LeetCodeWebSocketSubmitHandler,
2425
LeetCodeSubmissionCalendarHandlar,
26+
LeetCodeTopicHandlar,
27+
LeetCodeCompanyHandlar,
2528
]
2629

2730
web_app.add_handlers(

jupyterlab_leetcode/handlers/leetcode_handler.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ async def get(self):
109109
username
110110
realName
111111
avatar
112+
isPremium
112113
}
113114
}"""
114115
},
@@ -224,6 +225,49 @@ async def get(self):
224225
)
225226

226227

228+
class LeetCodeTopicHandlar(LeetCodeHandler):
229+
route = r"leetcode/topics"
230+
231+
@tornado.web.authenticated
232+
async def get(self):
233+
await self.graphql(
234+
name="topic_tags",
235+
query={
236+
"query": """query questionTopicTags {
237+
questionTopicTags {
238+
edges {
239+
node {
240+
id
241+
name
242+
slug
243+
translatedName
244+
questionIds
245+
}
246+
}
247+
}
248+
}""",
249+
},
250+
)
251+
252+
253+
class LeetCodeCompanyHandlar(LeetCodeHandler):
254+
route = r"leetcode/companies"
255+
256+
@tornado.web.authenticated
257+
async def get(self):
258+
await self.graphql(
259+
name="question_tags",
260+
query={
261+
"query": """query CompanyTags {
262+
companyTags {
263+
name
264+
slug
265+
}
266+
}""",
267+
},
268+
)
269+
270+
227271
class LeetCodeQuestionHandler(LeetCodeHandler):
228272
route = r"leetcode/questions"
229273

src/components/LeetCodeMain.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,11 @@ const LeetCodeMain: React.FC<{ docManager: IDocumentManager }> = ({
7171
/>
7272
<Actions paperProps={{ ...MainHeaderPaperProps, w: '5%' }} />
7373
</Group>
74-
<QuestionTable openNotebook={openNoteBook} height={calcHeight()} />
74+
<QuestionTable
75+
openNotebook={openNoteBook}
76+
height={calcHeight()}
77+
isPremium={profile?.isPremium}
78+
/>
7579
</Stack>
7680
</Container>
7781
);

src/components/QuestionCompanyFilter.tsx

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
import React, { useState } from 'react';
2-
import { Badge, MultiSelect, MultiSelectProps } from '@mantine/core';
1+
import React, { useEffect, useState } from 'react';
2+
import { Badge, MultiSelect, MultiSelectProps, Tooltip } from '@mantine/core';
33
import { IconBuildings, IconCheck } from '@tabler/icons-react';
4-
import classes from '../styles/Filter.module.css';
4+
import classes from '../styles/LeetCodeMain.module.css';
5+
import { LeetCodeCompanyTag } from '../types/leetcode';
6+
import { getAllCompanies } from '../services/leetcode';
57

68
const CheckedIcon = <IconCheck size={12} stroke={1.5} />;
79

8-
// TODO: fill data
9-
const Data = ['facebook', 'google'];
10-
11-
// TODO: show 'wont work if not premium'
1210
const renderMultiSelectOption: MultiSelectProps['renderOption'] = ({
1311
option,
1412
checked
@@ -25,13 +23,24 @@ const renderMultiSelectOption: MultiSelectProps['renderOption'] = ({
2523

2624
const QuestionCompanyFilter: React.FC<{
2725
updateCompanies: (companies: string[]) => void;
28-
}> = ({ updateCompanies }) => {
26+
isPremium?: boolean;
27+
}> = ({ updateCompanies, isPremium }) => {
2928
const [selected, setSelected] = useState(false);
29+
const [allCompanies, setAllCompanies] = useState<LeetCodeCompanyTag[]>([]);
30+
31+
useEffect(() => {
32+
getAllCompanies().then(cs => setAllCompanies(cs));
33+
}, []);
3034

31-
return (
35+
const options = allCompanies.map(c => ({
36+
value: c.slug,
37+
label: c.name
38+
}));
39+
const disabled = isPremium === false;
40+
const ms = (
3241
<MultiSelect
3342
tt="capitalize"
34-
data={Data}
43+
data={options}
3544
renderOption={renderMultiSelectOption}
3645
maxDropdownHeight={300}
3746
placeholder="Company"
@@ -44,9 +53,20 @@ const QuestionCompanyFilter: React.FC<{
4453
setSelected(v.length > 0);
4554
updateCompanies(v);
4655
}}
47-
className={selected ? classes.filter_selected : classes.filter_empty}
56+
className={
57+
selected ? classes.filter_selected : classes.company_filter_empty
58+
}
59+
disabled={disabled}
4860
/>
4961
);
62+
63+
return disabled ? (
64+
<Tooltip label="Company filter is only available for LeetCode Premium users.">
65+
{ms}
66+
</Tooltip>
67+
) : (
68+
ms
69+
);
5070
};
5171

5272
export default QuestionCompanyFilter;

src/components/QuestionDifficultyFilter.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
22
import { Badge, MultiSelect, MultiSelectProps } from '@mantine/core';
33
import { IconCheck, IconGauge } from '@tabler/icons-react';
44
import { DifficultyColors } from './Statistics';
5-
import classes from '../styles/Filter.module.css';
5+
import classes from '../styles/LeetCodeMain.module.css';
66

77
const CheckedIcon = <IconCheck size={12} stroke={1.5} />;
88

@@ -43,7 +43,9 @@ const QuestionDifficultyFilter: React.FC<{
4343
setSelected(v.length > 0);
4444
updateDifficulties(v.map(v => v.toUpperCase()));
4545
}}
46-
className={selected ? classes.filter_selected : classes.filter_empty}
46+
className={
47+
selected ? classes.filter_selected : classes.difficulty_filter_empty
48+
}
4749
/>
4850
);
4951
};

src/components/QuestionStatusFilter.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
22
import { Badge, MultiSelect, MultiSelectProps } from '@mantine/core';
33
import { IconCheck, IconCheckbox } from '@tabler/icons-react';
44
import { StatusColors } from './QuestionItem';
5-
import classes from '../styles/Filter.module.css';
5+
import classes from '../styles/LeetCodeMain.module.css';
66

77
const CheckedIcon = <IconCheck size={12} stroke={1.5} />;
88

@@ -46,7 +46,9 @@ const QuestionStatusFilter: React.FC<{
4646
setSelected(v.length > 0);
4747
updateStatuses(v);
4848
}}
49-
className={selected ? classes.filter_selected : classes.filter_empty}
49+
className={
50+
selected ? classes.filter_selected : classes.status_filter_empty
51+
}
5052
/>
5153
);
5254
};

src/components/QuestionTable.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ import QuestionCompanyFilter from './QuestionCompanyFilter';
2323
const QuestionTable: React.FC<{
2424
openNotebook: (p: string) => void;
2525
height: number | string;
26-
}> = ({ openNotebook, height }) => {
26+
isPremium?: boolean;
27+
}> = ({ openNotebook, height, isPremium }) => {
2728
const limit = 100;
2829

2930
const [fetching, setFetching] = useState(true);
@@ -113,6 +114,7 @@ const QuestionTable: React.FC<{
113114
/>
114115
<QuestionCompanyFilter
115116
updateCompanies={cs => updateQuery({ ...query, companies: cs })}
117+
isPremium={isPremium}
116118
/>
117119
</Group>
118120
<ScrollArea

src/components/QuestionTopicFilter.tsx

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
import React, { useState } from 'react';
1+
import React, { useEffect, useState } from 'react';
22
import { Badge, MultiSelect, MultiSelectProps } from '@mantine/core';
33
import { IconCheck, IconTags } from '@tabler/icons-react';
4-
import classes from '../styles/Filter.module.css';
4+
import classes from '../styles/LeetCodeMain.module.css';
5+
import { getAllTopics } from '../services/leetcode';
6+
import { LeetCodeTopicTag } from '../types/leetcode';
57

68
const CheckedIcon = <IconCheck size={12} stroke={1.5} />;
79

8-
// TODO: fill data
9-
const Data = ['array', 'hash-table'];
10-
1110
const renderMultiSelectOption: MultiSelectProps['renderOption'] = ({
1211
option,
1312
checked
@@ -18,19 +17,29 @@ const renderMultiSelectOption: MultiSelectProps['renderOption'] = ({
1817
variant="light"
1918
tt="capitalize"
2019
>
21-
{option.value}
20+
{option.label}
2221
</Badge>
2322
);
2423

2524
const QuestionTopicFilter: React.FC<{
2625
updateTopics: (topics: string[]) => void;
2726
}> = ({ updateTopics }) => {
2827
const [selected, setSelected] = useState(false);
28+
const [allTopics, setAllTopics] = useState<LeetCodeTopicTag[]>([]);
29+
30+
useEffect(() => {
31+
getAllTopics().then(ts => setAllTopics(ts));
32+
}, []);
33+
34+
const options = allTopics.map(t => ({
35+
value: t.slug,
36+
label: t.name
37+
}));
2938

3039
return (
3140
<MultiSelect
3241
tt="capitalize"
33-
data={Data}
42+
data={options}
3443
renderOption={renderMultiSelectOption}
3544
maxDropdownHeight={300}
3645
placeholder="Topic"
@@ -43,7 +52,9 @@ const QuestionTopicFilter: React.FC<{
4352
setSelected(v.length > 0);
4453
updateTopics(v);
4554
}}
46-
className={selected ? classes.filter_selected : classes.filter_empty}
55+
className={
56+
selected ? classes.filter_selected : classes.topic_filter_empty
57+
}
4758
/>
4859
);
4960
};

src/components/SubmissionCalendar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Paper, PaperProps, Stack, Table, Tooltip } from '@mantine/core';
22
import React, { useEffect, useState } from 'react';
33
import { getSubmissionCalendar } from '../services/leetcode';
4-
import classes from '../styles/SubmissionCalendar.module.css';
4+
import classes from '../styles/LeetCodeMain.module.css';
55
import { useScrollIntoView } from '@mantine/hooks';
66

77
const ADay = 24 * 60 * 60 * 1000; // milliseconds in a day

src/services/leetcode.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import {
2+
LeetCodeCompanyTag,
23
LeetCodeProfile,
34
LeetCodeQuestion,
45
LeetCodeQuestionQuery,
56
LeetCodeStatistics,
6-
LeetCodeSubmissionCalendar
7+
LeetCodeSubmissionCalendar,
8+
LeetCodeTopicTag
79
} from '../types/leetcode';
810
import { requestAPI } from './handler';
911

@@ -46,3 +48,17 @@ export async function getSubmissionCalendar(username: string) {
4648
d => d.data.matchedUser.userCalendar
4749
);
4850
}
51+
52+
export async function getAllTopics() {
53+
return requestAPI<{
54+
data: { questionTopicTags: { edges: { node: LeetCodeTopicTag }[] } };
55+
}>('/leetcode/topics').then(d =>
56+
d.data.questionTopicTags.edges.map(e => e.node)
57+
);
58+
}
59+
60+
export async function getAllCompanies() {
61+
return requestAPI<{
62+
data: { companyTags: LeetCodeCompanyTag[] };
63+
}>('/leetcode/companies').then(d => d.data.companyTags);
64+
}

0 commit comments

Comments
 (0)