Skip to content

Commit 1f3dee5

Browse files
committed
feat(ui): add material ui
1 parent 8e2e5f9 commit 1f3dee5

File tree

4 files changed

+173
-67
lines changed

4 files changed

+173
-67
lines changed

pages/_app.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
import "../styles/globals.css";
1+
import CssBaseline from "@material-ui/core/CssBaseline";
22
import { AppProps } from "next/app";
33

44
const MyApp = ({ Component, pageProps }: AppProps) => (
5-
<Component {...pageProps} />
5+
<>
6+
<CssBaseline />
7+
<Component {...pageProps} />
8+
</>
69
);
710

811
export default MyApp;

pages/api/users/[user].ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ interface LeetCodeResponse {
2121
}
2222

2323
interface Output {
24-
error: null | string;
2524
ranking: number | string;
2625
solved: number | string;
2726
solvedOverTotal: string;
2827
solvedPercentage: string;
28+
error: null | string;
2929
}
3030

3131
const query = (user: string) =>
@@ -71,19 +71,19 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
7171
)[0].count;
7272

7373
output = {
74-
error: null,
7574
ranking,
7675
solved,
7776
solvedOverTotal: `${solved}/${total}`,
7877
solvedPercentage: `${((solved / total) * 100).toFixed(1)}%`,
78+
error: null,
7979
};
8080
} catch ({ message }) {
8181
output = {
82-
error: message,
8382
ranking: genericErrorMessage,
8483
solved: genericErrorMessage,
8584
solvedOverTotal: genericErrorMessage,
8685
solvedPercentage: genericErrorMessage,
86+
error: message,
8787
};
8888
}
8989

pages/index.tsx

Lines changed: 164 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,189 @@
1-
import Head from "next/head";
21
import { useEffect, useState, FC } from "react";
3-
import { Subject, asyncScheduler } from "rxjs";
4-
import { throttleTime } from "rxjs/operators";
2+
import { Subject } from "rxjs";
3+
import {
4+
distinctUntilChanged,
5+
filter,
6+
debounceTime,
7+
switchMap,
8+
} from "rxjs/operators";
59
import axios from "axios";
10+
import { TextField, Grid, Box, Paper } from "@material-ui/core";
611

7-
interface LeetCodeStats {
8-
error: null | string;
9-
ranking: number | string;
10-
solved: number | string;
11-
solvedOverTotal: string;
12-
solvedPercentage: string;
12+
interface Badge {
13+
username: string;
14+
style: "flat" | "flat-square" | "plastic" | "for-the-badge" | "social";
15+
labelColor: string;
16+
color: string;
17+
label: string;
18+
value: "ranking" | "solved" | "solvedOverTotal" | "solvedPercentage";
19+
showLogo: boolean;
20+
logoColor: string;
1321
}
1422

15-
const defaultStats: LeetCodeStats = {
16-
error: null,
17-
ranking: 0,
18-
solved: 0,
19-
solvedOverTotal: "0/0",
20-
solvedPercentage: "0.0%",
21-
};
22-
2323
const Home: FC = () => {
24-
const [subject] = useState(() => new Subject<string>());
25-
const [stats, setStats] = useState(defaultStats);
26-
const [username, setUsername] = useState("");
24+
const [username$] = useState(() => new Subject<string>());
25+
const [usernameInput, setUsernameInput] = useState("");
26+
const [error, setError] = useState("");
27+
const [badge, setBadge] = useState<Badge>({
28+
username: "cascandaliato",
29+
style: "for-the-badge",
30+
labelColor: "black",
31+
color: "#ffa116",
32+
label: "Solved",
33+
value: "solvedOverTotal",
34+
showLogo: true,
35+
logoColor: "yellow",
36+
});
2737

2838
const getLeetCodeStats = (u: string) => {
2939
axios
3040
.get(`/api/users/${u}`)
31-
.then(({ data }) => data)
32-
.then(setStats)
33-
.catch(() => setStats(defaultStats));
41+
.then(({ data: { error: e } }) => e)
42+
.then((e) => {
43+
if (e) {
44+
setError(e);
45+
} else {
46+
setBadge((b) => ({ ...b, username: u }));
47+
}
48+
})
49+
.catch((e) => setError("Couldn't retrieve user"));
3450
};
3551

3652
useEffect(() => {
37-
const subscription = subject
53+
const subscription = username$
3854
.pipe(
39-
throttleTime(400, asyncScheduler, { leading: true, trailing: true })
55+
filter((value) => !!value),
56+
distinctUntilChanged(),
57+
debounceTime(500)
4058
)
4159
.subscribe(getLeetCodeStats);
4260
return () => subscription.unsubscribe();
4361
}, []);
44-
62+
// <Head>
63+
// <title>LeetCode Badge</title>
64+
// <link rel="icon" href="/favicon.ico" />
65+
// </Head>
4566
return (
46-
<main
47-
style={{
48-
height: "100vh",
49-
display: "flex",
50-
flexDirection: "column",
51-
justifyContent: "center",
52-
alignItems: "center",
53-
}}
67+
<Grid
68+
container
69+
spacing={2}
70+
direction="column"
71+
alignItems="center"
72+
justify="center"
73+
style={{ minHeight: "100vh" }}
5474
>
55-
<Head>
56-
<title>Create Next App</title>
57-
<link rel="icon" href="/favicon.ico" />
58-
</Head>
59-
60-
<pre>{JSON.stringify(stats, null, 2)}</pre>
61-
<input
62-
value={username}
63-
onChange={({ target: { value } }) => {
64-
setUsername(value);
65-
subject.next(value);
66-
}}
67-
/>
68-
<object
69-
data="https://img.shields.io/badge/label-message-blue"
70-
type="image/svg+xml"
71-
/>
72-
<img src="https://img.shields.io/badge/label-message-red" />
75+
<Paper>
76+
<Box p={2}>
77+
<Grid item>
78+
<TextField
79+
id="username"
80+
label="Username"
81+
variant="outlined"
82+
error={!!error}
83+
helperText={error}
84+
size="small"
85+
// required={true}
86+
// placeholder="Your LeetCode username"
87+
value={usernameInput}
88+
onChange={({ target: { value } }) => {
89+
setUsernameInput(value);
90+
setError("");
91+
username$.next(value);
92+
}}
93+
/>
94+
</Grid>
95+
<Grid item>
96+
<img
97+
src={`https://img.shields.io/badge/dynamic/json?style=${
98+
badge.style
99+
}&labelColor=${encodeURIComponent(
100+
badge.labelColor
101+
)}&color=${encodeURIComponent(
102+
badge.color
103+
)}&label=${encodeURIComponent(badge.label)}&query=${
104+
badge.value
105+
}&url=https%3A%2F%2Fleetcode-badge.vercel.app%2Fapi%2Fusers%2F${encodeURIComponent(
106+
badge.username
107+
)}${
108+
badge.showLogo
109+
? `&logo=leetcode&logoColor=${encodeURIComponent(
110+
badge.logoColor
111+
)}`
112+
: ""
113+
}`}
114+
/>
115+
</Grid>
116+
<Grid item>
117+
<pre>
118+
{JSON.stringify({ usernameInput, error, badge }, null, 2)}
119+
</pre>
120+
</Grid>
121+
</Box>
122+
</Paper>
73123
{/* <footer className={styles.footer}>
74-
<a
75-
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
76-
target="_blank"
77-
rel="noopener noreferrer"
78-
>
79-
Powered by{" "}
80-
<img src="/vercel.svg" alt="Vercel Logo" className={styles.logo} />
81-
</a>
82-
</footer> */}
83-
</main>
124+
<a
125+
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
126+
target="_blank"
127+
rel="noopener noreferrer"
128+
>
129+
Powered by{" "}
130+
<img src="/vercel.svg" alt="Vercel Logo" className={styles.logo} />
131+
</a>
132+
</footer> */}
133+
</Grid>
84134
);
85135
};
86136

87137
export default Home;
138+
139+
// Observable.create((observer) => {
140+
// const controller = new AbortController();
141+
// const signal = controller.signal;
142+
143+
// fetch(url, { signal })
144+
// .then((response) => {
145+
// if (response.ok) {
146+
// return response.json();
147+
// } else {
148+
// observer.error("Request failed with status code: " + response.status);
149+
// }
150+
// })
151+
// .then((body) => {
152+
// observer.next(body);
153+
154+
// observer.complete();
155+
// })
156+
// .catch((err) => {
157+
// observer.error(err);
158+
// });
159+
160+
// return () => controller.abort();
161+
// });
162+
//------------------------------------------------
163+
// const CancelToken = axios.CancelToken;
164+
// const source = CancelToken.source();
165+
166+
// axios
167+
// .get("/user/12345", {
168+
// cancelToken: source.token,
169+
// })
170+
// .catch(function (thrown) {
171+
// if (axios.isCancel(thrown)) {
172+
// console.log("Request canceled", thrown.message);
173+
// } else {
174+
// // handle error
175+
// }
176+
// });
177+
178+
// axios.post(
179+
// "/user/12345",
180+
// {
181+
// name: "new name",
182+
// },
183+
// {
184+
// cancelToken: source.token,
185+
// }
186+
// );
187+
188+
// // cancel the request (the message parameter is optional)
189+
// source.cancel("Operation canceled by the user.");

public/style-social.svg

Lines changed: 1 addition & 0 deletions
Loading

0 commit comments

Comments
 (0)