Skip to content

Commit

Permalink
complete a first working version
Browse files Browse the repository at this point in the history
  • Loading branch information
cascandaliato committed Nov 8, 2020
1 parent 1f3dee5 commit 8592157
Show file tree
Hide file tree
Showing 16 changed files with 745 additions and 270 deletions.
108 changes: 108 additions & 0 deletions components/BadgeContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import {
Checkbox,
FormControl,
FormControlLabel,
FormLabel,
InputLabel,
MenuItem,
Select,
TextField,
} from "@material-ui/core";
import { Dispatch, FC, SetStateAction } from "react";
import { Badge, ContentPresetName, contentPresets } from "../utils/badge";

const BadgeContent: FC<{
badge: Badge;
setBadge: Dispatch<SetStateAction<Badge>>;
}> = ({ badge, setBadge }) => (
<FormControl component="fieldset">
<FormLabel component="legend">Content</FormLabel>
<FormControl style={{ marginTop: "16px" }}>
<InputLabel id="badge-content-preset-label">Preset</InputLabel>
<Select
labelId="badge-content-preset-label"
id="badge-content-preset-select"
onChange={({ target: { value } }) => {
if (value === "custom") {
setBadge((b) => ({
...b,
contentPreset: "custom",
}));
} else {
const v = value as ContentPresetName;
setBadge((b) => ({
...b,
contentPreset: v,
label: contentPresets[v].badgeLabel,
value: v,
}));
}
}}
label="Preset"
defaultValue={badge.contentPreset}
autoWidth={true}
>
{Object.entries(contentPresets).map(
([option, { presetLabel: display }]) => (
<MenuItem value={option} key={option}>
{display}
</MenuItem>
)
)}
<MenuItem value="custom">
<em>Custom</em>
</MenuItem>
</Select>
</FormControl>
<TextField
id="badge-label"
label="Label"
value={badge.label}
onChange={(e) => setBadge((b) => ({ ...b, label: e.target.value }))}
disabled={badge.contentPreset !== "custom"}
style={{ marginTop: "24px" }}
/>
<FormControl style={{ marginTop: "24px" }}>
<InputLabel id="badge-value-label">Displayed value</InputLabel>
<Select
labelId="badge-value-label"
id="badge-value-select"
value={badge.value}
onChange={(e) =>
setBadge((b) => ({
...b,
value: e.target.value as keyof typeof contentPresets,
}))
}
label="Displayed value"
defaultValue={badge.contentPreset}
autoWidth={true}
disabled={badge.contentPreset !== "custom"}
>
{Object.entries(contentPresets).map(
([option, { badgeValueDisplay }]) => (
<MenuItem value={option} key={option}>
{badgeValueDisplay}
</MenuItem>
)
)}
</Select>
</FormControl>
<FormControlLabel
control={
<Checkbox
checked={badge.showLogo}
onChange={({ target: { checked: showLogo } }) =>
setBadge((b) => ({ ...b, showLogo }))
}
name="showLogo"
color="primary"
/>
}
style={{ marginTop: "24px" }}
label="Show logo"
/>
</FormControl>
);

export default BadgeContent;
48 changes: 48 additions & 0 deletions components/BadgeStyle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {
FormControl,
FormControlLabel,
FormLabel,
Radio,
RadioGroup,
} from "@material-ui/core";
import { Dispatch, FC, SetStateAction } from "react";
import { Badge, styles } from "../utils/badge";

const BadgeStyle: FC<{
badge: Badge;
setBadge: Dispatch<SetStateAction<Badge>>;
}> = ({ badge, setBadge }) => (
<FormControl component="fieldset">
<FormLabel component="legend">Style</FormLabel>
<RadioGroup
aria-label="style"
name="style"
value={badge.style}
onChange={({ target: { value } }) => {
setBadge((b) => ({
...b,
style: value as Badge["style"],
}));
}}
style={{ marginTop: "8px" }}
>
{Object.entries(styles).map(([name, { width, height }]) => (
<FormControlLabel
key={name}
value={name}
control={<Radio color="primary" />}
label={
<img
src={`style-${name}.svg`}
alt={`badge style "${name.replace("-", " ")}"`}
width={width}
height={height}
/>
}
/>
))}
</RadioGroup>
</FormControl>
);

export default BadgeStyle;
3 changes: 3 additions & 0 deletions components/CopyToClipboard.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.main:hover {
color: #ffa116;
}
65 changes: 65 additions & 0 deletions components/CopyToClipboard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Box, Tooltip, Zoom } from "@material-ui/core";
import { makeStyles } from "@material-ui/core/styles";
import copy from "copy-to-clipboard";
import { FC, useEffect, useState } from "react";
import styles from "./CopyToClipboard.module.css";

const COPY_TO_CLIPBOARD = "Copy to clipboard";

const useDarkStyle = makeStyles((theme) => ({
arrow: {
color: "#212121",
},
tooltip: {
backgroundColor: "#212121",
},
}));

const CopyToClipboard: FC<{
icon: IconDefinition;
label: string;
textToCopy: string;
}> = ({ icon, label, textToCopy }) => {
const [tooltipOpen, setTooltipOpen] = useState(false);
const [tooltipText, setTooltipText] = useState(COPY_TO_CLIPBOARD);
const classes = useDarkStyle();

useEffect(() => {
const tId = setTimeout(() => {
if (!tooltipOpen) setTooltipText(COPY_TO_CLIPBOARD);
}, 500);
return () => clearTimeout(tId);
}, [tooltipOpen]);

return (
<Tooltip
TransitionComponent={Zoom}
open={tooltipOpen}
onOpen={() => setTooltipOpen(true)}
onClose={() => setTooltipOpen(false)}
title={tooltipText}
placement="bottom"
classes={classes}
arrow
>
<Box
display="flex"
flexDirection="column"
justifyContent="center"
alignItems="center"
className={styles.main}
onClick={() => {
copy(textToCopy);
setTooltipText("Copied!");
}}
>
<FontAwesomeIcon icon={icon} size="2x" />
<span style={{ fontWeight: 400 }}>{label}</span>
</Box>
</Tooltip>
);
};

export default CopyToClipboard;
47 changes: 47 additions & 0 deletions components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { faGithub, faTwitter } from "@fortawesome/free-brands-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Box, Grid, Link, Typography } from "@material-ui/core";
import { FC } from "react";

const Footer: FC = () => (
<footer>
<Box
display="flex"
flexDirection="column"
alignItems="center"
justifyContent="center"
>
<Typography variant="caption">
Built on top of <Link href="https://leetcode.com/">LeetCode.com</Link>{" "}
and <Link href="https://shields.io/">Shields.io</Link>
</Typography>
<Grid
container
spacing={2}
justify="center"
style={{ marginTop: "-6px" }}
>
<Grid item>
<Link
href="https://github.com/cascandaliato/"
color="textPrimary"
aria-label="Author's GitHub profile"
>
<FontAwesomeIcon icon={faGithub} size="lg" />
</Link>
</Grid>
<Grid item>
<Link
href="https://twitter.com/cascandaliato"
color="textPrimary"
aria-label="Author's Twitter profile"
>
<FontAwesomeIcon icon={faTwitter} size="lg" />
</Link>
</Grid>
</Grid>
</Box>
</footer>
);

export default Footer;
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@
"start": "next start"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.32",
"@fortawesome/free-brands-svg-icons": "^5.15.1",
"@fortawesome/free-regular-svg-icons": "^5.15.1",
"@fortawesome/react-fontawesome": "^0.1.12",
"@material-ui/core": "^4.11.0",
"axios": "^0.21.0",
"copy-to-clipboard": "^3.3.1",
"next": "10.0.0",
"react": "17.0.1",
"react-dom": "17.0.1",
Expand Down
28 changes: 28 additions & 0 deletions pages/_document.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Document, {
DocumentContext,
Head,
Html,
Main,
NextScript,
} from "next/document";

class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
const initialProps = await Document.getInitialProps(ctx);
return initialProps;
}

render() {
return (
<Html lang="en">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}

export default MyDocument;
22 changes: 15 additions & 7 deletions pages/api/users/[user].ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NextApiRequest, NextApiResponse } from "next";
import axios from "axios";
import { NextApiRequest, NextApiResponse } from "next";

interface LeetCodeCount {
difficulty: string;
Expand All @@ -11,16 +11,16 @@ interface LeetCodeResponse {
data: {
allQuestionsCount: LeetCodeCount[];
matchedUser: {
profile: { ranking: number };
submitStats: {
acSubmissionNum: LeetCodeCount[];
};
profile: { realName: string; userAvatar: string; ranking: number };
submitStats: { acSubmissionNum: LeetCodeCount[] };
};
};
};
}

interface Output {
realName: string;
avatarUrl: string;
ranking: number | string;
solved: number | string;
solvedOverTotal: string;
Expand All @@ -32,7 +32,7 @@ const query = (user: string) =>
`{
"operationName": "getUserProfile",
"variables": { "username" : "${user}" },
"query": "query getUserProfile($username: String!) { allQuestionsCount { difficulty count } matchedUser(username: $username) { profile { starRating ranking } submitStats { acSubmissionNum { difficulty count } } } }"
"query": "query getUserProfile($username: String!) { allQuestionsCount { difficulty count } matchedUser(username: $username) { profile { realName userAvatar starRating ranking } submitStats { acSubmissionNum { difficulty count } } } }"
}`;

const genericErrorMessage =
Expand Down Expand Up @@ -60,7 +60,11 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {

if (!data.matchedUser) throw new Error("User not found");

const ranking = data.matchedUser.profile.ranking;
const {
realName,
userAvatar: avatarUrl,
ranking,
} = data.matchedUser.profile;

const solved = data.matchedUser.submitStats.acSubmissionNum.filter(
({ difficulty }) => difficulty === "All"
Expand All @@ -71,6 +75,8 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
)[0].count;

output = {
realName,
avatarUrl,
ranking,
solved,
solvedOverTotal: `${solved}/${total}`,
Expand All @@ -79,6 +85,8 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
};
} catch ({ message }) {
output = {
realName: genericErrorMessage,
avatarUrl: genericErrorMessage,
ranking: genericErrorMessage,
solved: genericErrorMessage,
solvedOverTotal: genericErrorMessage,
Expand Down
Loading

1 comment on commit 8592157

@vercel
Copy link

@vercel vercel bot commented on 8592157 Nov 8, 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.