|
1 |
| -import Head from "next/head"; |
2 | 1 | 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"; |
5 | 9 | import axios from "axios";
|
| 10 | +import { TextField, Grid, Box, Paper } from "@material-ui/core"; |
6 | 11 |
|
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; |
13 | 21 | }
|
14 | 22 |
|
15 |
| -const defaultStats: LeetCodeStats = { |
16 |
| - error: null, |
17 |
| - ranking: 0, |
18 |
| - solved: 0, |
19 |
| - solvedOverTotal: "0/0", |
20 |
| - solvedPercentage: "0.0%", |
21 |
| -}; |
22 |
| - |
23 | 23 | 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 | + }); |
27 | 37 |
|
28 | 38 | const getLeetCodeStats = (u: string) => {
|
29 | 39 | axios
|
30 | 40 | .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")); |
34 | 50 | };
|
35 | 51 |
|
36 | 52 | useEffect(() => {
|
37 |
| - const subscription = subject |
| 53 | + const subscription = username$ |
38 | 54 | .pipe(
|
39 |
| - throttleTime(400, asyncScheduler, { leading: true, trailing: true }) |
| 55 | + filter((value) => !!value), |
| 56 | + distinctUntilChanged(), |
| 57 | + debounceTime(500) |
40 | 58 | )
|
41 | 59 | .subscribe(getLeetCodeStats);
|
42 | 60 | return () => subscription.unsubscribe();
|
43 | 61 | }, []);
|
44 |
| - |
| 62 | + // <Head> |
| 63 | + // <title>LeetCode Badge</title> |
| 64 | + // <link rel="icon" href="/favicon.ico" /> |
| 65 | + // </Head> |
45 | 66 | 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" }} |
54 | 74 | >
|
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> |
73 | 123 | {/* <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> |
84 | 134 | );
|
85 | 135 | };
|
86 | 136 |
|
87 | 137 | 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."); |
0 commit comments