Skip to content

Commit 2d6be77

Browse files
committed
finish landing page
1 parent be013c2 commit 2d6be77

File tree

7 files changed

+429
-92
lines changed

7 files changed

+429
-92
lines changed

jupyterlab_leetcode/handlers/cookie_handler.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,23 @@ def get(self):
4343
self.finish(json.dumps({"message": f"Unsupported browser: {browser}"}))
4444
return
4545

46-
cj = BROWSER_COOKIE_METHOD_MAP[browser](domain_name="leetcode.com")
46+
try:
47+
cj = BROWSER_COOKIE_METHOD_MAP[browser](domain_name="leetcode.com")
48+
except browser_cookie3.BrowserCookieError as e:
49+
self.set_status(418)
50+
self.finish(
51+
json.dumps(
52+
{
53+
"message": "Failed to retrieve cookies. Maybe not installed the browser?"
54+
}
55+
)
56+
)
57+
return
58+
except Exception as e:
59+
self.set_status(418)
60+
self.finish(json.dumps({"message": str(e.args)}))
61+
return
62+
4763
cookie_session = first(cj, lambda c: c.name == "LEETCODE_SESSION")
4864
cookie_csrf = first(cj, lambda c: c.name == "csrftoken")
4965
exist = bool(cookie_session and cookie_csrf)

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,13 @@
3030
"url": "https://github.com/Sorosliu1029/jupyterlab-leetcode.git"
3131
},
3232
"scripts": {
33-
"build": "jlpm build:lib && jlpm build:labextension:dev",
34-
"build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension",
33+
"build": "jlpm build:lib && jlpm copy && jlpm build:labextension:dev",
34+
"build:prod": "jlpm clean && jlpm build:lib:prod && jlpm copy && jlpm build:labextension",
3535
"build:labextension": "jupyter labextension build .",
3636
"build:labextension:dev": "jupyter labextension build --development True .",
3737
"build:lib": "tsc --sourceMap",
3838
"build:lib:prod": "tsc",
39+
"copy": "copyfiles -u 1 src/**/*.css lib/",
3940
"clean": "jlpm clean:lib",
4041
"clean:lib": "rimraf lib tsconfig.tsbuildinfo",
4142
"clean:lintcache": "rimraf .eslintcache .stylelintcache",
@@ -65,8 +66,7 @@
6566
"@jupyterlab/settingregistry": "^4.0.0",
6667
"@mantine/core": "^7.17.8",
6768
"@mantine/hooks": "^7.17.8",
68-
"@tabler/icons-react": "^3.34.0",
69-
"bowser": "^2.11.0"
69+
"@tabler/icons-react": "^3.34.0"
7070
},
7171
"devDependencies": {
7272
"@jupyterlab/builder": "^4.0.0",
@@ -77,6 +77,7 @@
7777
"@types/react-addons-linked-state-mixin": "^0.14.22",
7878
"@typescript-eslint/eslint-plugin": "^6.1.0",
7979
"@typescript-eslint/parser": "^6.1.0",
80+
"copyfiles": "^2.4.1",
8081
"css-loader": "^6.7.1",
8182
"eslint": "^8.36.0",
8283
"eslint-config-prettier": "^8.8.0",

src/components/BrowserCookie.tsx

Lines changed: 129 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,100 @@
11
import React, { useEffect, useState } from 'react';
22
import { Notification } from '@jupyterlab/apputils';
3+
import {
4+
IconChevronDown,
5+
IconBrandChrome,
6+
IconBrandFirefox,
7+
IconBrandEdge,
8+
IconBrandSafari,
9+
IconBrandOpera,
10+
IconBrandVivaldi,
11+
IconBrandArc,
12+
IconWorldWww
13+
} from '@tabler/icons-react';
14+
import { Button, Menu, useMantineTheme } from '@mantine/core';
315
import { getCookie } from '../services/cookie';
4-
import Bowser from 'bowser';
516

6-
const BrowserCookie: React.FC<{
7-
setCookieLoggedIn: (b: string) => void;
8-
}> = ({ setCookieLoggedIn }) => {
9-
const browsers = [
10-
'Chrome',
11-
'Firefox',
12-
'Safari',
13-
'Edge',
14-
'Opera',
15-
'Brave',
16-
'Vivaldi',
17-
'Chromium',
18-
'Arc',
19-
'LibreWolf',
20-
'Opera GX'
21-
];
17+
const BROWSERS = [
18+
{
19+
name: 'Chrome',
20+
icon: (color: string) => (
21+
<IconBrandChrome size={16} color={color} stroke={1.5} />
22+
)
23+
},
24+
{
25+
name: 'Firefox',
26+
icon: (color: string) => (
27+
<IconBrandFirefox size={16} color={color} stroke={1.5} />
28+
)
29+
},
30+
{
31+
name: 'Safari',
32+
icon: (color: string) => (
33+
<IconBrandSafari size={16} color={color} stroke={1.5} />
34+
)
35+
},
36+
{
37+
name: 'Edge',
38+
icon: (color: string) => (
39+
<IconBrandEdge size={16} color={color} stroke={1.5} />
40+
)
41+
},
42+
{
43+
name: 'Opera',
44+
icon: (color: string) => (
45+
<IconBrandOpera size={16} color={color} stroke={1.5} />
46+
)
47+
},
48+
{
49+
name: 'Brave',
50+
icon: (color: string) => (
51+
<IconWorldWww size={16} color={color} stroke={1.5} />
52+
)
53+
},
54+
{
55+
name: 'Vivaldi',
56+
icon: (color: string) => (
57+
<IconBrandVivaldi size={16} color={color} stroke={1.5} />
58+
)
59+
},
60+
{
61+
name: 'Chromium',
62+
icon: (color: string) => (
63+
<IconBrandChrome size={16} color={color} stroke={1.5} />
64+
)
65+
},
66+
{
67+
name: 'Arc',
68+
icon: (color: string) => (
69+
<IconBrandArc size={16} color={color} stroke={1.5} />
70+
)
71+
},
72+
{
73+
name: 'LibreWolf',
74+
icon: (color: string) => (
75+
<IconWorldWww size={16} color={color} stroke={1.5} />
76+
)
77+
},
78+
{
79+
name: 'Opera GX',
80+
icon: (color: string) => (
81+
<IconBrandOpera size={16} color={color} stroke={1.5} />
82+
)
83+
}
84+
];
2285

23-
const normalizeBrowserName = (name: string) =>
24-
name.toLowerCase().replace(/\s+/g, '_');
86+
const normalizeBrowserName = (name: string) =>
87+
name.toLowerCase().replace(/\s+/g, '_');
2588

89+
const BrowserCookie: React.FC<{
90+
className?: string;
91+
setCookieLoggedIn: (b: string) => void;
92+
}> = ({ className, setCookieLoggedIn }) => {
2693
const [browser, setBrowser] = useState('');
2794
const [checked, setChecked] = useState(false);
2895

29-
// set browser value by detecting current browser
3096
useEffect(() => {
31-
const browserName = Bowser.getParser(
32-
window.navigator.userAgent
33-
).getBrowserName(true);
34-
if (browserName) {
35-
const firstMatch = browsers.find(b =>
36-
new RegExp(b, 'i').test(browserName)
37-
);
38-
if (firstMatch) {
39-
setBrowser(normalizeBrowserName(firstMatch));
40-
}
41-
}
42-
}, []);
43-
44-
useEffect(() => {
45-
if (checked) {
46-
setCookieLoggedIn(browser);
47-
}
48-
}, [checked, setCookieLoggedIn]);
49-
50-
const checkCookie = () => {
5197
if (!browser) {
52-
Notification.error('Please select a browser.', { autoClose: 3000 });
5398
return;
5499
}
55100
if (browser === 'safari') {
@@ -62,37 +107,56 @@ const BrowserCookie: React.FC<{
62107

63108
getCookie(browser)
64109
.then(resp => {
110+
if (!resp['checked']) {
111+
Notification.error(
112+
`Failed to check cookie for ${browser}. Please ensure you are logged in to LeetCode in this browser.`,
113+
{ autoClose: 3000 }
114+
);
115+
}
65116
setChecked(resp['checked']);
66117
})
67118
.catch(e => Notification.error(e.message, { autoClose: 3000 }));
68-
};
119+
}, [browser]);
120+
121+
useEffect(() => {
122+
if (checked) {
123+
setCookieLoggedIn(browser);
124+
}
125+
}, [checked]);
69126

127+
const theme = useMantineTheme();
70128
return (
71-
<div>
72-
<label htmlFor="browser-selector">
73-
Choose your browser that has LeetCode logged in:
74-
</label>
75-
<select
76-
id="browser-selector"
77-
required
78-
value={browser}
79-
onChange={e => setBrowser(e.target.value)}
80-
>
81-
<option value="" disabled>
82-
Select a browser
83-
</option>
84-
{browsers.map(browser => (
85-
<option
86-
key={browser.toLowerCase()}
87-
value={normalizeBrowserName(browser)}
129+
<Menu
130+
transitionProps={{ transition: 'pop-top-right' }}
131+
position="bottom-end"
132+
width={220}
133+
withinPortal
134+
radius="md"
135+
>
136+
<Menu.Target>
137+
<Button
138+
rightSection={<IconChevronDown size={18} stroke={1.5} />}
139+
pr={12}
140+
radius="md"
141+
size="md"
142+
className={className}
143+
>
144+
Load from browser
145+
</Button>
146+
</Menu.Target>
147+
<Menu.Dropdown>
148+
<Menu.Label>With LeetCode logged in</Menu.Label>
149+
{BROWSERS.map(({ name, icon }, i) => (
150+
<Menu.Item
151+
key={name}
152+
leftSection={icon(theme.colors.blue[6])}
153+
onClick={() => setBrowser(normalizeBrowserName(name))}
88154
>
89-
{browser}
90-
</option>
155+
{name}
156+
</Menu.Item>
91157
))}
92-
</select>
93-
<button onClick={checkCookie}>Check</button>
94-
<p>Checked: {checked ? 'Yes' : 'No'}</p>
95-
</div>
158+
</Menu.Dropdown>
159+
</Menu>
96160
);
97161
};
98162

src/components/LandingPage.tsx

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,79 @@
11
import React from 'react';
22
import BrowserCookie from './BrowserCookie';
33

4+
import { Button, Container, Group, Text, Tooltip, Anchor } from '@mantine/core';
5+
import { IconBrandGithub, IconBrandLinkedin } from '@tabler/icons-react';
6+
import classes from '../styles/LandingPage.module.css';
7+
8+
const LeetCdoeGradient = { from: '#FEA512', to: '#FFDB01' };
9+
410
const LandingPage: React.FC<{
511
setCookieLoggedIn: (b: string) => void;
612
}> = ({ setCookieLoggedIn }) => {
713
const options: JSX.Element[] = [
8-
<BrowserCookie setCookieLoggedIn={setCookieLoggedIn} />
14+
<BrowserCookie
15+
setCookieLoggedIn={setCookieLoggedIn}
16+
className={classes.control}
17+
/>,
18+
<Tooltip label="Not implemented yet, contributions are welcome!">
19+
<Button
20+
size="md"
21+
className={classes.control}
22+
variant="filled"
23+
data-disabled
24+
onClick={e => e.preventDefault()}
25+
leftSection={<IconBrandGithub size={20} />}
26+
>
27+
GitHub Login
28+
</Button>
29+
</Tooltip>,
30+
<Tooltip label="Not implemented yet, contributions are welcome!">
31+
<Button
32+
size="md"
33+
className={classes.control}
34+
variant="filled"
35+
data-disabled
36+
onClick={e => e.preventDefault()}
37+
leftSection={<IconBrandLinkedin size={20} />}
38+
>
39+
LinkedIn Login
40+
</Button>
41+
</Tooltip>
942
];
43+
1044
return (
11-
<div>
12-
<p>Welcome to JupyterLab LeetCode Widget.</p>
13-
<p>
14-
For this plugin to work, you may choose one of these {options.length}{' '}
15-
methods to allow this plugin to log into LeetCode.
16-
</p>
17-
{...options}
45+
<div className={classes.wrapper}>
46+
<Container size={700} className={classes.inner}>
47+
<h1 className={classes.title}>
48+
Welcome to{' '}
49+
<Text
50+
component="span"
51+
variant="gradient"
52+
gradient={LeetCdoeGradient}
53+
inherit
54+
>
55+
JupyterLab LeetCode
56+
</Text>{' '}
57+
plugin
58+
</h1>
59+
60+
<Text className={classes.description} color="dimmed">
61+
For this plugin to work, you may choose one of these {options.length}{' '}
62+
methods to allow this plugin to{' '}
63+
<Anchor
64+
href="https://leetcode.com/accounts/login/"
65+
target="_blank"
66+
variant="gradient"
67+
gradient={LeetCdoeGradient}
68+
className={classes.description}
69+
>
70+
log into LeetCode
71+
</Anchor>
72+
.
73+
</Text>
74+
75+
<Group className={classes.controls}>{...options}</Group>
76+
</Container>
1877
</div>
1978
);
2079
};

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy