Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions popup/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules/
dist/
build/
.env
.vscode/
package-lock.json
yarn.lock
4 changes: 4 additions & 0 deletions popup/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"plugins": ["prettier-plugin-tailwindcss"],
"tailwindFunctions": ["cva"]
}
856 changes: 856 additions & 0 deletions popup/bun.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion popup/components.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
"components": "@/components",
"utils": "@/lib/utils"
}
}
}
24 changes: 12 additions & 12 deletions popup/eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import js from "@eslint/js";
import globals from "globals";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import tseslint from "typescript-eslint";

export default tseslint.config({
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
ignores: ['dist'],
files: ["**/*.{ts,tsx}"],
ignores: ["dist"],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
"react-hooks": reactHooks,
"react-refresh": reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
"react-refresh/only-export-components": [
"warn",
{ allowConstantExport: true },
],
},
})
});
24 changes: 10 additions & 14 deletions popup/index.html
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon"
type="image/svg+xml"
href="/vite.svg" />
<meta name="viewport"
content="width=device-width, initial-scale=1.0" />
<title>LeetPush</title>
</head>
<body>
<div id="root"></div>
<script type="module"
src="src/main.tsx"></script>
</body>
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>LeetPush</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="src/main.tsx"></script>
</body>
</html>
4 changes: 3 additions & 1 deletion popup/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"format": "bunx prettier --write .",
"type:check": "tsc --noEmit",
"lint": "eslint .",
"preview": "vite preview"
},
Expand Down Expand Up @@ -43,7 +45,7 @@
"globals": "^15.9.0",
"postcss": "^8.4.41",
"prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.6",
"prettier-plugin-tailwindcss": "^0.7.2",
"tailwindcss": "^3.4.9",
"typescript": "^5.5.3",
"typescript-eslint": "^8.0.0",
Expand Down
2 changes: 1 addition & 1 deletion popup/postcss.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ export default {
tailwindcss: {},
autoprefixer: {},
},
}
};
8 changes: 4 additions & 4 deletions popup/prettier.config.cjs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
module.exports = {
plugins: ['prettier-plugin-tailwindcss'],
tailwindConfig: './tailwind.config.js',
plugins: ["prettier-plugin-tailwindcss"],
tailwindConfig: "./tailwind.config.js",
singleQuote: true,
semi: false,
trailingComma: 'all',
trailingComma: "all",
tabWidth: 2,
printWidth: 85,
jsxBracketSameLine: true,
}
};
26 changes: 11 additions & 15 deletions popup/src/components/App.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
import Links from '@/components/Links.tsx'
import Logo from '@/components/Logo.tsx'
import LeetCode from '@/components/LeetCode.tsx'
import Footer from '@/components/Footer.tsx'
import InputForm from '@/components/InputForm.tsx'
import { useContext } from 'react'
import { UserContext } from '@/context/userContext.tsx'
import EditButton from '@/components/EditButton.tsx'
import Links from "@/components/Links.tsx";
import Logo from "@/components/Logo.tsx";
import LeetCode from "@/components/LeetCode.tsx";
import Footer from "@/components/Footer.tsx";
import InputForm from "@/components/InputForm.tsx";
import { useContext } from "react";
import { UserContext } from "@/context/userContext.tsx";
import EditButton from "@/components/EditButton.tsx";

export default function App() {
const { username } = useContext(UserContext)
const { username } = useContext(UserContext);

return (
<main className="space-y-1 bg-stone-900 px-4 py-5 text-stone-100">
<Links />
<Logo />
{username ? (
<LeetCode />
) : (
<InputForm />
)}
{username ? <LeetCode /> : <InputForm />}
{username && <EditButton />}
<Footer />
</main>
)
);
}
21 changes: 11 additions & 10 deletions popup/src/components/Daily.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { Badge } from '@/components/ui/badge'
import { Badge } from "@/components/ui/badge";

import { getDifficultyColor } from '@/lib/utils.ts'
import { DailyProblemI } from '@/types/leetpush.interface.ts'
import { getDifficultyColor } from "@/lib/utils.ts";
import { DailyProblemI } from "@/types/leetpush.interface.ts";

export default function Daily({ data }: { data: DailyProblemI }) {
return (
<div>
<div>
<div className="flex flex-col gap-1">
<div className="flex gap-2 items-center">
<p className="font-medium text-xs text-lp-grey">Daily
Problem</p>
<div className="flex items-center gap-2">
<p className="text-xs font-medium text-lp-grey">Daily Problem</p>
<a
target="_blank"
href={`https://leetcode.com${data.link}`}
Expand All @@ -21,22 +20,24 @@ export default function Daily({ data }: { data: DailyProblemI }) {
{data.question.title}
</a>
<span
className={`text-sm font-semibold px-1.5 py-0.5 ${getDifficultyColor(data.question.difficulty as 'Easy' | 'Medium' | 'Hard')} rounded-md`}
className={`px-1.5 py-0.5 text-sm font-semibold ${getDifficultyColor(data.question.difficulty as "Easy" | "Medium" | "Hard")} rounded-md`}
>
{data.question.difficulty}
</span>
</div>

<div className="flex gap-2">
{data.question.topicTags.map((tag) => (
<Badge key={tag.name}
className="rounded-lg text-lp-grey hover:bg-lp-greyer bg-lp-greyer font-normal">
<Badge
key={tag.name}
className="rounded-lg bg-lp-greyer font-normal text-lp-grey hover:bg-lp-greyer"
>
{tag.name}
</Badge>
))}
</div>
</div>
</div>
</div>
)
);
}
16 changes: 6 additions & 10 deletions popup/src/components/EditButton.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
import { Button } from '@/components/ui/button.tsx'
import { UserContext } from '@/context/userContext.tsx'
import { useContext } from 'react'
import { Button } from "@/components/ui/button.tsx";
import { UserContext } from "@/context/userContext.tsx";
import { useContext } from "react";

export default function EditButton() {
const { setUsername } = useContext(UserContext)
const { setUsername } = useContext(UserContext);

return (
<div className="flex justify-end">
<Button
variant="secondary"
size="sm"
onClick={() => setUsername('')}
>
<Button variant="secondary" size="sm" onClick={() => setUsername("")}>
Edit
</Button>
</div>
)
);
}
13 changes: 9 additions & 4 deletions popup/src/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FaXTwitter, FaLinkedinIn } from 'react-icons/fa6'
import { FaXTwitter, FaLinkedinIn } from "react-icons/fa6";

export default function Footer() {
return (
Expand All @@ -7,19 +7,24 @@ export default function Footer() {
&copy; {new Date().getFullYear()}
<span className="ml-1 flex items-center font-semibold text-lp-grey">
Hüsam
<a href="https://twitter.com/husamahmud" target="_blank" className="ml-1">
<a
href="https://twitter.com/husamahmud"
target="_blank"
className="ml-1"
>
<FaXTwitter size="16" />
</a>
<a
href="https://www.linkedin.com/in/husamahmud/"
target="_blank"
className="ml-1">
className="ml-1"
>
<FaLinkedinIn size="16" />
</a>
</span>
</p>

<span className="text-xs font-semibold text-lp-grey">v1.7.3</span>
</footer>
)
);
}
82 changes: 45 additions & 37 deletions popup/src/components/InputForm.tsx
Original file line number Diff line number Diff line change
@@ -1,63 +1,71 @@
import { useContext, FormEvent, useState } from 'react'
import { useContext, FormEvent, useState } from "react";

import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";

import { UserContext } from '@/context/userContext.tsx'
import { fetchUserStats } from '@/lib/leetpush.api.ts'
import { UserContext } from "@/context/userContext.tsx";
import { fetchUserStats } from "@/lib/leetpush.api.ts";

export default function InputForm() {
const { username, setUsername } = useContext(UserContext)
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const { username, setUsername } = useContext(UserContext);
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");

const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault()
setLoading(true)
e.preventDefault();
setLoading(true);

const username = e.currentTarget.username.value
const username = e.currentTarget.username.value;
if (!username) {
setError('Please enter a username')
setLoading(false)
return
setError("Please enter a username");
setLoading(false);
return;
}

try {
await fetchUserStats(username)
await fetchUserStats(username);

setUsername(username)
setError('')
setUsername(username);
setError("");
} catch (error) {
if (error instanceof Error) {
if (error.message === 'User not found') {
setError('User not found')
if (error.message === "User not found") {
setError("User not found");
} else {
setError('Failed to fetch user stats')
setError("Failed to fetch user stats");
}
} else {
setError('An unknown error occurred')
setError("An unknown error occurred");
}
} finally {
setLoading(false)
setLoading(false);
}
}
};

return (
<div className="flex flex-col items-center py-7 space-y-3">
<form className="flex w-full max-w-sm justify-center items-center space-x-2 mx-auto"
onSubmit={handleSubmit}>
<Input type="text"
name="username"
defaultValue={username}
className="bg-transparent border-b border-lp-greyer text-stone-100 placeholder:text-stone-500"
placeholder="LeetCode username" />
<Button type="submit"
size="sm"
disabled={loading}
className="bg-stone-600 hover:bg-stone-700 text-white font-normal">Submit</Button>
<div className="flex flex-col items-center space-y-3 py-7">
<form
className="mx-auto flex w-full max-w-sm items-center justify-center space-x-2"
onSubmit={handleSubmit}
>
<Input
type="text"
name="username"
defaultValue={username}
className="border-b border-lp-greyer bg-transparent text-stone-100 placeholder:text-stone-500"
placeholder="LeetCode username"
/>
<Button
type="submit"
size="sm"
disabled={loading}
className="bg-stone-600 font-normal text-white hover:bg-stone-700"
>
Submit
</Button>
</form>

{error && <p className="text-red-500 font-medium text-base">{error}</p>}
{error && <p className="text-base font-medium text-red-500">{error}</p>}
</div>
)
);
}
Loading