Skip to content

Commit

Permalink
feat(ui): add material ui
Browse files Browse the repository at this point in the history
  • Loading branch information
cascandaliato committed Nov 4, 2020
1 parent 8e2e5f9 commit 1f3dee5
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 67 deletions.
7 changes: 5 additions & 2 deletions pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import "../styles/globals.css";
import CssBaseline from "@material-ui/core/CssBaseline";
import { AppProps } from "next/app";

const MyApp = ({ Component, pageProps }: AppProps) => (
<Component {...pageProps} />
<>
<CssBaseline />
<Component {...pageProps} />
</>
);

export default MyApp;
6 changes: 3 additions & 3 deletions pages/api/users/[user].ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ interface LeetCodeResponse {
}

interface Output {
error: null | string;
ranking: number | string;
solved: number | string;
solvedOverTotal: string;
solvedPercentage: string;
error: null | string;
}

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

output = {
error: null,
ranking,
solved,
solvedOverTotal: `${solved}/${total}`,
solvedPercentage: `${((solved / total) * 100).toFixed(1)}%`,
error: null,
};
} catch ({ message }) {
output = {
error: message,
ranking: genericErrorMessage,
solved: genericErrorMessage,
solvedOverTotal: genericErrorMessage,
solvedPercentage: genericErrorMessage,
error: message,
};
}

Expand Down
226 changes: 164 additions & 62 deletions pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,87 +1,189 @@
import Head from "next/head";
import { useEffect, useState, FC } from "react";
import { Subject, asyncScheduler } from "rxjs";
import { throttleTime } from "rxjs/operators";
import { Subject } from "rxjs";
import {
distinctUntilChanged,
filter,
debounceTime,
switchMap,
} from "rxjs/operators";
import axios from "axios";
import { TextField, Grid, Box, Paper } from "@material-ui/core";

interface LeetCodeStats {
error: null | string;
ranking: number | string;
solved: number | string;
solvedOverTotal: string;
solvedPercentage: string;
interface Badge {
username: string;
style: "flat" | "flat-square" | "plastic" | "for-the-badge" | "social";
labelColor: string;
color: string;
label: string;
value: "ranking" | "solved" | "solvedOverTotal" | "solvedPercentage";
showLogo: boolean;
logoColor: string;
}

const defaultStats: LeetCodeStats = {
error: null,
ranking: 0,
solved: 0,
solvedOverTotal: "0/0",
solvedPercentage: "0.0%",
};

const Home: FC = () => {
const [subject] = useState(() => new Subject<string>());
const [stats, setStats] = useState(defaultStats);
const [username, setUsername] = useState("");
const [username$] = useState(() => new Subject<string>());
const [usernameInput, setUsernameInput] = useState("");
const [error, setError] = useState("");
const [badge, setBadge] = useState<Badge>({
username: "cascandaliato",
style: "for-the-badge",
labelColor: "black",
color: "#ffa116",
label: "Solved",
value: "solvedOverTotal",
showLogo: true,
logoColor: "yellow",
});

const getLeetCodeStats = (u: string) => {
axios
.get(`/api/users/${u}`)
.then(({ data }) => data)
.then(setStats)
.catch(() => setStats(defaultStats));
.then(({ data: { error: e } }) => e)
.then((e) => {
if (e) {
setError(e);
} else {
setBadge((b) => ({ ...b, username: u }));
}
})
.catch((e) => setError("Couldn't retrieve user"));
};

useEffect(() => {
const subscription = subject
const subscription = username$
.pipe(
throttleTime(400, asyncScheduler, { leading: true, trailing: true })
filter((value) => !!value),
distinctUntilChanged(),
debounceTime(500)
)
.subscribe(getLeetCodeStats);
return () => subscription.unsubscribe();
}, []);

// <Head>
// <title>LeetCode Badge</title>
// <link rel="icon" href="/favicon.ico" />
// </Head>
return (
<main
style={{
height: "100vh",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
}}
<Grid
container
spacing={2}
direction="column"
alignItems="center"
justify="center"
style={{ minHeight: "100vh" }}
>
<Head>
<title>Create Next App</title>
<link rel="icon" href="/favicon.ico" />
</Head>

<pre>{JSON.stringify(stats, null, 2)}</pre>
<input
value={username}
onChange={({ target: { value } }) => {
setUsername(value);
subject.next(value);
}}
/>
<object
data="https://img.shields.io/badge/label-message-blue"
type="image/svg+xml"
/>
<img src="https://img.shields.io/badge/label-message-red" />
<Paper>
<Box p={2}>
<Grid item>
<TextField
id="username"
label="Username"
variant="outlined"
error={!!error}
helperText={error}
size="small"
// required={true}
// placeholder="Your LeetCode username"
value={usernameInput}
onChange={({ target: { value } }) => {
setUsernameInput(value);
setError("");
username$.next(value);
}}
/>
</Grid>
<Grid item>
<img
src={`https://img.shields.io/badge/dynamic/json?style=${
badge.style
}&labelColor=${encodeURIComponent(
badge.labelColor
)}&color=${encodeURIComponent(
badge.color
)}&label=${encodeURIComponent(badge.label)}&query=${
badge.value
}&url=https%3A%2F%2Fleetcode-badge.vercel.app%2Fapi%2Fusers%2F${encodeURIComponent(
badge.username
)}${
badge.showLogo
? `&logo=leetcode&logoColor=${encodeURIComponent(
badge.logoColor
)}`
: ""
}`}
/>
</Grid>
<Grid item>
<pre>
{JSON.stringify({ usernameInput, error, badge }, null, 2)}
</pre>
</Grid>
</Box>
</Paper>
{/* <footer className={styles.footer}>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by{" "}
<img src="/vercel.svg" alt="Vercel Logo" className={styles.logo} />
</a>
</footer> */}
</main>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by{" "}
<img src="/vercel.svg" alt="Vercel Logo" className={styles.logo} />
</a>
</footer> */}
</Grid>
);
};

export default Home;

// Observable.create((observer) => {
// const controller = new AbortController();
// const signal = controller.signal;

// fetch(url, { signal })
// .then((response) => {
// if (response.ok) {
// return response.json();
// } else {
// observer.error("Request failed with status code: " + response.status);
// }
// })
// .then((body) => {
// observer.next(body);

// observer.complete();
// })
// .catch((err) => {
// observer.error(err);
// });

// return () => controller.abort();
// });
//------------------------------------------------
// const CancelToken = axios.CancelToken;
// const source = CancelToken.source();

// axios
// .get("/user/12345", {
// cancelToken: source.token,
// })
// .catch(function (thrown) {
// if (axios.isCancel(thrown)) {
// console.log("Request canceled", thrown.message);
// } else {
// // handle error
// }
// });

// axios.post(
// "/user/12345",
// {
// name: "new name",
// },
// {
// cancelToken: source.token,
// }
// );

// // cancel the request (the message parameter is optional)
// source.cancel("Operation canceled by the user.");
1 change: 1 addition & 0 deletions public/style-social.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

1 comment on commit 1f3dee5

@vercel
Copy link

@vercel vercel bot commented on 1f3dee5 Nov 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.