From 4499bd355f3f2053b86dab5aee794386c2275067 Mon Sep 17 00:00:00 2001 From: TED Vortex Date: Sat, 6 Aug 2022 21:10:32 +0200 Subject: [PATCH 01/11] build: add swr and beta api configuration --- .env | 3 ++- npm-shrinkwrap.json | 9 +++++++++ package.json | 1 + src/vite-env.d.ts | 1 + 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.env b/.env index f1c80035..8738dd00 100644 --- a/.env +++ b/.env @@ -1,3 +1,4 @@ VITE_SUPABASE_URL=https://lkkownkrbkmblczeoyqb.supabase.co VITE_SUPABASE_API_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJvbGUiOiJhbm9uIiwiaWF0IjoxNjQyNDU2MTc0LCJleHAiOjE5NTgwMzIxNzR9.c6nlkT05GnNacQ6OYuGcjBsILmGsSDwEEtN2zZVXFgY -VITE_POSTHOG_ID=phc_DFcN3mLP4ocKAUBKr5xyMbAnPwcl93q41ZmhoLG7GCv \ No newline at end of file +VITE_POSTHOG_ID=phc_DFcN3mLP4ocKAUBKr5xyMbAnPwcl93q41ZmhoLG7GCv +VITE_API_URL=https://beta.api.opensauced.pizza/v1 diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index e96a4509..584fbc1d 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -22,6 +22,7 @@ "react-icons": "^4.4.0", "react-router-dom": "^6.3.0", "rooks": "^6.1.0", + "swr": "^1.3.0", "tailwindcss": "^3.1.7", "tailwindcss-radix": "^2.4.0" }, @@ -8015,6 +8016,14 @@ "node": ">=10.13.0" } }, + "node_modules/swr": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/swr/-/swr-1.3.0.tgz", + "integrity": "sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw==", + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/systemjs": { "version": "6.12.1", "resolved": "https://registry.npmjs.org/systemjs/-/systemjs-6.12.1.tgz", diff --git a/package.json b/package.json index 5a75c2d7..1d5a1267 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "react-icons": "^4.4.0", "react-router-dom": "^6.3.0", "rooks": "^6.1.0", + "swr": "^1.3.0", "tailwindcss": "^3.1.7", "tailwindcss-radix": "^2.4.0" }, diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 4534a9ab..511be9ea 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -3,6 +3,7 @@ interface ImportMetaEnv { readonly VITE_SUPABASE_URL: string; readonly VITE_SUPABASE_API_KEY: string; readonly VITE_POSTHOG_ID: string; + readonly VITE_API_URL: string; } interface ImportMeta { From 05eb2c9fab42287b4ee058c683124b3e629f39c4 Mon Sep 17 00:00:00 2001 From: TED Vortex Date: Sun, 7 Aug 2022 02:44:47 +0200 Subject: [PATCH 02/11] feat: add open-sauced minimal react loading skeleton closes #276 --- npm-shrinkwrap.json | 9 ++ package.json | 1 + src/components/HotRepoCard.tsx | 143 +++++++++++++++++++++ src/components/HotRepositories.tsx | 193 ++--------------------------- src/components/PostsWrap.tsx | 2 +- src/components/StackedAvatar.tsx | 1 + src/hooks/useRepo.ts | 17 +++ src/styles/index.css | 2 + 8 files changed, 181 insertions(+), 187 deletions(-) create mode 100644 src/components/HotRepoCard.tsx create mode 100644 src/hooks/useRepo.ts diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 584fbc1d..54b00244 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -20,6 +20,7 @@ "react-dom": "^18.2.0", "react-hot-toast": "^2.3.0", "react-icons": "^4.4.0", + "react-loading-skeleton": "^3.1.0", "react-router-dom": "^6.3.0", "rooks": "^6.1.0", "swr": "^1.3.0", @@ -7252,6 +7253,14 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, + "node_modules/react-loading-skeleton": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/react-loading-skeleton/-/react-loading-skeleton-3.1.0.tgz", + "integrity": "sha512-j1U1CWWs68nBPOg7tkQqnlFcAMFF6oEK6MgqAo15f8A5p7mjH6xyKn2gHbkcimpwfO0VQXqxAswnSYVr8lWzjw==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/react-refresh": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", diff --git a/package.json b/package.json index 1d5a1267..baff22c0 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "react-dom": "^18.2.0", "react-hot-toast": "^2.3.0", "react-icons": "^4.4.0", + "react-loading-skeleton": "^3.1.0", "react-router-dom": "^6.3.0", "rooks": "^6.1.0", "swr": "^1.3.0", diff --git a/src/components/HotRepoCard.tsx b/src/components/HotRepoCard.tsx new file mode 100644 index 00000000..908f822a --- /dev/null +++ b/src/components/HotRepoCard.tsx @@ -0,0 +1,143 @@ +import useSWR from "swr"; +import { RiCheckboxCircleFill } from "react-icons/ri"; +import { FaArrowAltCircleUp } from "react-icons/fa"; +import { AiOutlineStar } from "react-icons/ai"; +import { BiGitPullRequest } from "react-icons/bi"; +import { VscIssues } from "react-icons/vsc"; +import Skeleton from "react-loading-skeleton"; +import { getAvatarLink } from "../lib/github"; +import humanizeNumber from "../lib/humanizeNumber"; +import StackedAvatar from "./StackedAvatar"; +import useSupabaseAuth from "../hooks/useSupabaseAuth"; +import handleVoteUpdateByRepo from "../lib/handleVoteUpdateByRepo"; +import useRepo from "../hooks/useRepo"; + +export declare interface HotRepoCardProps { + repoName: string; +} + +const HotRepoCard = ({ repoName }: HotRepoCardProps): JSX.Element => { + const { signIn, user } = useSupabaseAuth(); + const { user_metadata: { sub: user_id } } = user! || { user_metadata: { sub: null } }; + + const { repo, isLoading, isError } = useRepo(repoName); + + if (isError) { + return ( +
+ {`${repoName} failed to load`} +
+ ); + } + + if (isLoading) { + return ( +
+ +
+ ); + } + + const { id, full_name, name, description, issues, stars, contributions } = repo ?? {}; + const owner = full_name?.replace(`/${String(name)}`, "").trim(); + + const checkVoted = (id: number | undefined) => false; + const handleVoteUpdate = async (votes: number, repo_id: number) => { + await handleVoteUpdateByRepo(votes, repo_id, user_id); + // handleVoted(repo_id); + }; + + + return ( +
+
+
+ Hot Repo Icon + + + {owner} + +
+ + +
+ +
+ + {name} + + +

+ {description} +

+
+ +
+
+
+ + + + {humanizeNumber(issues ?? 0)} + +
+ +
+ + + + {humanizeNumber(stars ?? 0)} + +
+ +
+ + + 0 +
+
+ + +
+
+ ); +}; + +export default HotRepoCard; diff --git a/src/components/HotRepositories.tsx b/src/components/HotRepositories.tsx index 84df439d..c7d82d3c 100644 --- a/src/components/HotRepositories.tsx +++ b/src/components/HotRepositories.tsx @@ -1,93 +1,9 @@ -import { useCallback, useEffect, useState } from "react"; -import { FaArrowAltCircleUp } from "react-icons/fa"; -import { VscIssues } from "react-icons/vsc"; -import { AiOutlineStar } from "react-icons/ai"; -import { BiGitPullRequest } from "react-icons/bi"; -import { RiCheckboxCircleFill } from "react-icons/ri"; -import { User } from "@supabase/supabase-js"; -import handleVoteUpdateByRepo from "../lib/handleVoteUpdateByRepo"; -import humanizeNumber from "../lib/humanizeNumber"; -import { getAvatarLink } from "../lib/github"; -import { fetchRecommendations } from "../lib/supabase"; -import useSupabaseAuth from "../hooks/useSupabaseAuth"; -import StackedAvatar from "./StackedAvatar"; import hotIcon from "../assets/hotIcon.png"; +import HotRepoCard from "./HotRepoCard"; -export declare interface HotReposProps { - user?: User; -} - -const HotRepositories = ({ user }: HotReposProps): JSX.Element => { - const { user_metadata: { sub: user_id } } = user! || { user_metadata: { sub: null } }; - const [hotRepos, setHotRepos] = useState([]); - - const [votedReposIds, setVotedReposIds] = useState([]); - const { signIn } = useSupabaseAuth(); +const HotRepositories = (): JSX.Element => { const staticHot = ["oven-sh/bun", "tabler/tabler", "open-sauced/hot"]; - // this function is just a placeholder to help change the color and state of the selected button on the card. - const handleVoted = (repo_id: number) => { - const hasVoted = checkVoted(repo_id); - - if (hasVoted) { - setVotedReposIds(votedReposIds.filter(id => id !== repo_id)); - } else { - setVotedReposIds([...votedReposIds, repo_id]); - } - }; - - const checkVoted = (repo_id: number) => votedReposIds.includes(repo_id); - - const fetchHotData = useCallback( - async (repo: string) => - fetchRecommendations("popular", 1, user, repo) - .then(data => { - if (data[0]) { - return data[0]; - } - - throw new Error(`Unable to fetch ${repo}`); - }) - .catch(err => { - console.log(err); - throw err; - }), - [], - ); - - const fetchVotedData = useCallback(async (user?: User) => { - if (user) { - const data = await fetchRecommendations("myVotes", 1000, user, ""); - - return setVotedReposIds(data.map(repo => repo.id)); - } - - setVotedReposIds([]); - }, []); - - useEffect(() => { - const promises: Promise[] = []; - - staticHot.forEach(repo => promises.push(fetchHotData(repo))); - - Promise.allSettled(promises) - .then(data => { - const newHots = (data.filter(d => d.status === "fulfilled") as PromiseFulfilledResult[]).map( - d => d.value, - ); - - return setHotRepos(newHots); - }) - .catch(console.error); - - fetchVotedData(user).catch(console.error); - }, [user]); - - async function handleVoteUpdate (votes: number, repo_id: number) { - await handleVoteUpdateByRepo(votes, repo_id, user_id); - handleVoted(repo_id); - } - return (
@@ -101,106 +17,11 @@ const HotRepositories = ({ user }: HotReposProps): JSX.Element => {
- {hotRepos.map(({ id, full_name, name, description, issues, stars, contributions }) => ( -
- {/* header & upvote button */} - -
-
- Hot Repo Icon - - - {full_name.replace(`/${String(name)}`, "")} - -
- - -
- - {/* repo name & description */} - -
- - {name} - - -

- {description} -

-
- - {/* issues || star || PRs || Avatar */} - -
- {/* issues || star || PRs*/} - -
-
- - - - {humanizeNumber(issues)} - -
- -
- - - - {humanizeNumber(stars)} - -
- -
- - - 0 -
-
- - {/* Avatars */} - - -
-
+ {staticHot.map(repo => ( + ))}
diff --git a/src/components/PostsWrap.tsx b/src/components/PostsWrap.tsx index c8f028b8..18bc49ce 100644 --- a/src/components/PostsWrap.tsx +++ b/src/components/PostsWrap.tsx @@ -57,7 +57,7 @@ const PostsWrap = ({ textToSearch }: PostWrapProps): JSX.Element => { user={user} /> - + = async name => + fetch(`${import.meta.env.VITE_API_URL}/repos/${name}`, { headers: { accept: "application/json" } }) + .then(async r => r.json()); + +const useRepo = (name: Key) => { + const { data, error } = useSWR(name, fetcher, { revalidateOnFocus: false }); + + return { + repo: data, + isLoading: !error && !data, + isError: !!error, + }; +}; + +export default useRepo; diff --git a/src/styles/index.css b/src/styles/index.css index 5b2a7654..4a36e73c 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -1,3 +1,5 @@ +@import "react-loading-skeleton/dist/skeleton.css"; + @tailwind base; @tailwind components; @tailwind utilities; From 46d0e8b790794e1d0405e386536de5ef7b2f384e Mon Sep 17 00:00:00 2001 From: TED Vortex Date: Mon, 8 Aug 2022 00:38:15 +0200 Subject: [PATCH 03/11] refactor: make swr config global and add default api fetcher --- src/App.tsx | 11 +++++++++-- src/components/HotRepoCard.tsx | 4 ++-- src/hooks/useRepo.ts | 10 +++------- src/hooks/useSWR.ts | 9 +++++++++ 4 files changed, 23 insertions(+), 11 deletions(-) create mode 100644 src/hooks/useSWR.ts diff --git a/src/App.tsx b/src/App.tsx index 38a72305..3ff3f365 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,10 +5,12 @@ import PostsWrap from "./components/PostsWrap"; import { initiatePostHog } from "./lib/analytics"; import { BrowserRouter } from "react-router-dom"; import { Toaster } from "react-hot-toast"; +import { SWRConfig } from "swr"; import RepoSubmission from "./components/RepoSubmission"; import GradBackground from "./components/GradBackground"; import useSupabaseAuth from "./hooks/useSupabaseAuth"; import Hero from "./components/Hero"; +import apiFetcher from "./hooks/useSWR"; const App = (): JSX.Element => { initiatePostHog(); @@ -16,7 +18,12 @@ const App = (): JSX.Element => { const [textToSearch] = useState(""); return ( - <> + @@ -34,7 +41,7 @@ const App = (): JSX.Element => {