From 8378c88f0c84e9f36685b4a194b3fc33a13e8558 Mon Sep 17 00:00:00 2001 From: Jiang Bohan Date: Sun, 5 Feb 2023 22:41:08 +0800 Subject: [PATCH] feat: finish auth --- components/layout/index.tsx | 45 +++-- components/layout/user-dropdown.tsx | 34 ++-- lib/constants.ts | 5 + lib/interface.ts | 5 + lib/store/index.ts | 37 ++++ package-lock.json | 216 ++++++++++++++++++++- package.json | 5 +- pages/index.tsx | 288 ++++++++++++++++------------ service.ts/request.ts | 47 +++++ yarn.lock | 76 +++++++- 10 files changed, 600 insertions(+), 158 deletions(-) create mode 100644 lib/interface.ts create mode 100644 lib/store/index.ts create mode 100644 service.ts/request.ts diff --git a/components/layout/index.tsx b/components/layout/index.tsx index c3c3095..4545004 100644 --- a/components/layout/index.tsx +++ b/components/layout/index.tsx @@ -1,4 +1,4 @@ -import { FADE_IN_ANIMATION_SETTINGS } from "@/lib/constants"; +import { FADE_IN_ANIMATION_SETTINGS, READ_PILOT_TOKEN } from "@/lib/constants"; import { AnimatePresence, motion } from "framer-motion"; import { useSession } from "next-auth/react"; import Image from "next/image"; @@ -6,8 +6,10 @@ import Link from "next/link"; import { ReactNode } from "react"; import useScroll from "@/lib/hooks/use-scroll"; import Meta from "./meta"; -import { useSignInModal } from "./sign-in-modal"; import UserDropdown from "./user-dropdown"; +import { useGoogleLogin } from "@react-oauth/google"; +import { usePilotStore } from "@/lib/store"; +import { googleLogin } from "service.ts/request"; export default function Layout({ meta, @@ -20,9 +22,21 @@ export default function Layout({ }; children: ReactNode; }) { - const { data: session, status } = useSession(); - const { SignInModal, setShowSignInModal } = useSignInModal(); const scrolled = useScroll(50); + const { authToken, setAuthToken } = usePilotStore((state) => state); + const login = useGoogleLogin({ + onSuccess: async (tokenResponse) => { + try { + const res = await googleLogin({ + access_token: tokenResponse.access_token, + }); + localStorage.setItem(READ_PILOT_TOKEN, res.data.access); + setAuthToken(res.data.access); + } catch (error) { + console.error(error); + } + }, + }); return ( <> @@ -46,17 +60,26 @@ export default function Layout({ >

Read Pilot

-
+
- {!session && status !== "loading" ? ( - + Subscribe + + {!authToken ? ( + { + login(); + }} > - Subscribe - + Sign in + ) : ( )} diff --git a/components/layout/user-dropdown.tsx b/components/layout/user-dropdown.tsx index b7bc4a2..f4a969e 100644 --- a/components/layout/user-dropdown.tsx +++ b/components/layout/user-dropdown.tsx @@ -1,17 +1,16 @@ import { useState } from "react"; -import { signOut, useSession } from "next-auth/react"; -import { LayoutDashboard, LogOut } from "lucide-react"; +import { LogOut } from "lucide-react"; import Popover from "@/components/shared/popover"; import Image from "next/image"; import { motion } from "framer-motion"; -import { FADE_IN_ANIMATION_SETTINGS } from "@/lib/constants"; +import { FADE_IN_ANIMATION_SETTINGS, READ_PILOT_TOKEN } from "@/lib/constants"; +import { usePilotStore } from "@/lib/store"; export default function UserDropdown() { - const { data: session } = useSession(); - const { email, image } = session?.user || {}; + const { userInfo, setAuthToken } = usePilotStore((state) => state); const [openPopover, setOpenPopover] = useState(false); - if (!email) return null; + if (!userInfo) return null; return ( - {/* - -

Dashboard

- */} - + */}
diff --git a/lib/constants.ts b/lib/constants.ts index 38abc24..971c152 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -16,3 +16,8 @@ export const FADE_UP_ANIMATION_VARIANTS = { export const DEPLOY_URL = "https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fsteven-tey%2Fprecedent&project-name=precedent&repository-name=precedent&demo-title=Precedent&demo-description=An%20opinionated%20collection%20of%20components%2C%20hooks%2C%20and%20utilities%20for%20your%20Next%20project.&demo-url=https%3A%2F%2Fprecedent.dev&demo-image=https%3A%2F%2Fprecedent.dev%2Fapi%2Fog&env=DATABASE_URL,GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET,NEXTAUTH_SECRET&envDescription=How%20to%20get%20these%20env%20variables%3A&envLink=https%3A%2F%2Fgithub.com%2Fsteven-tey%2Fprecedent%2Fblob%2Fmain%2F.env.example"; + +export const READ_PILOT_TOKEN = + process.env.NODE_ENV === "development" + ? "READ_PILOT_LOCAL_AUTH" + : "READ_PILOT_PROD_AUTH"; diff --git a/lib/interface.ts b/lib/interface.ts new file mode 100644 index 0000000..4f22b1e --- /dev/null +++ b/lib/interface.ts @@ -0,0 +1,5 @@ +export interface UserInfo { + avatar: string; + email: string; + id: string; +} diff --git a/lib/store/index.ts b/lib/store/index.ts new file mode 100644 index 0000000..b9fb32e --- /dev/null +++ b/lib/store/index.ts @@ -0,0 +1,37 @@ +import { getUserInfo } from "service.ts/request"; +import { create } from "zustand"; +import { UserInfo } from "../interface"; + +interface PilotStoreInterface { + userInfo?: UserInfo; + authToken?: string; + + setUserInfo: (userInfo: UserInfo) => void; + setAuthToken: (token?: string) => void; + fetchUserInfo: () => Promise; +} + +export const usePilotStore = create()((set, get) => ({ + setUserInfo: (userInfo) => { + set({ + userInfo: userInfo, + }); + }, + setAuthToken: (token) => { + set({ + authToken: token, + }); + }, + fetchUserInfo: async () => { + const { authToken } = get(); + if (!authToken) { + return; + } + try { + const res = await getUserInfo({}); + set({ + userInfo: res.data, + }); + } catch (error) {} + }, +})); diff --git a/package-lock.json b/package-lock.json index feab81e..7254e27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,11 +13,13 @@ "@prisma/client": "^4.8.1", "@radix-ui/react-popover": "^1.0.2", "@radix-ui/react-tooltip": "^1.0.2", + "@react-oauth/google": "^0.7.0", "@types/node": "18.11.18", "@types/react": "18.0.26", "@types/react-dom": "18.0.10", "@vercel/analytics": "^0.1.6", "@vercel/og": "^0.0.26", + "axios": "^1.3.2", "classnames": "^2.3.2", "eslint": "8.31.0", "eslint-config-next": "13.1.1", @@ -32,7 +34,8 @@ "react-markdown": "^8.0.4", "react-wrap-balancer": "^0.3.0", "typescript": "4.9.4", - "use-debounce": "^9.0.3" + "use-debounce": "^9.0.3", + "zustand": "^4.3.2" }, "devDependencies": { "@tailwindcss/forms": "^0.5.3", @@ -836,6 +839,15 @@ "@babel/runtime": "^7.13.10" } }, + "node_modules/@react-oauth/google": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.7.0.tgz", + "integrity": "sha512-KLLYlLYYpXf1zmcrsJPAxImvoq2b0Rs0Pi/6GYfdwjmTh66Pf6jkZ4fpO10tuJuUPWvEeqloPEC/i4BySgbYVQ==", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, "node_modules/@resvg/resvg-wasm": { "version": "2.0.0-alpha.4", "resolved": "https://registry.npmjs.org/@resvg/resvg-wasm/-/resvg-wasm-2.0.0-alpha.4.tgz", @@ -1314,6 +1326,11 @@ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/autoprefixer": { "version": "10.4.13", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", @@ -1366,6 +1383,16 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.2.tgz", + "integrity": "sha512-1M3O703bYqYuPhbHeya5bnhpYVsDDRyQSabNja04mZtboLNSuZ4YrltestrLXfHgmzua4TpUqRiVKbiQuo2epw==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", @@ -1611,6 +1638,17 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", @@ -1833,6 +1871,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -2618,6 +2664,25 @@ "react-dom": ">=16.3.0" } }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -2626,6 +2691,19 @@ "is-callable": "^1.1.3" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", @@ -4066,6 +4144,25 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mini-svg-data-uri": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", @@ -4762,6 +4859,11 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/punycode": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.2.0.tgz", @@ -5827,6 +5929,14 @@ } } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -6052,6 +6162,29 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/yoga-wasm-web/-/yoga-wasm-web-0.3.0.tgz", "integrity": "sha512-rD3L4jyMlO1m+RWU60lNwZQK5zmzglCV5fI1gTRikmpv3YzmNIZQbjyfE6cMNb9Xaly/C1SwemYGbsiOekMvnQ==" + }, + "node_modules/zustand": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.3.2.tgz", + "integrity": "sha512-rd4haDmlwMTVWVqwvgy00ny8rtti/klRoZjFbL/MAcDnmD5qSw/RZc+Vddstdv90M5Lv6RPgWvm1Hivyn0QgJw==", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "immer": ">=9.0", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } } }, "dependencies": { @@ -6596,6 +6729,12 @@ "@babel/runtime": "^7.13.10" } }, + "@react-oauth/google": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.7.0.tgz", + "integrity": "sha512-KLLYlLYYpXf1zmcrsJPAxImvoq2b0Rs0Pi/6GYfdwjmTh66Pf6jkZ4fpO10tuJuUPWvEeqloPEC/i4BySgbYVQ==", + "requires": {} + }, "@resvg/resvg-wasm": { "version": "2.0.0-alpha.4", "resolved": "https://registry.npmjs.org/@resvg/resvg-wasm/-/resvg-wasm-2.0.0-alpha.4.tgz", @@ -6940,6 +7079,11 @@ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "autoprefixer": { "version": "10.4.13", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", @@ -6964,6 +7108,16 @@ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.2.tgz", "integrity": "sha512-b1WlTV8+XKLj9gZy2DZXgQiyDp9xkkoe2a6U6UbYccScq2wgH/YwCeI2/Jq2mgo0HzQxqJOjWZBLeA/mqsk5Mg==" }, + "axios": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.2.tgz", + "integrity": "sha512-1M3O703bYqYuPhbHeya5bnhpYVsDDRyQSabNja04mZtboLNSuZ4YrltestrLXfHgmzua4TpUqRiVKbiQuo2epw==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "axobject-query": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", @@ -7132,6 +7286,14 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", @@ -7293,6 +7455,11 @@ "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", "dev": true }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, "dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -7895,6 +8062,11 @@ "tabbable": "^6.0.1" } }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + }, "for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -7903,6 +8075,16 @@ "is-callable": "^1.1.3" } }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", @@ -8806,6 +8988,19 @@ "picomatch": "^2.3.1" } }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, "mini-svg-data-uri": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", @@ -9237,6 +9432,11 @@ "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.2.0.tgz", "integrity": "sha512-kma4U7AFCTwpqq5twzC1YVIDXSqg6qQK6JN0smOw8fgRy1OkMi0CYSzFmsy6dnqSenamAtj0CyXMUJ1Mf6oROg==" }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "punycode": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.2.0.tgz", @@ -9943,6 +10143,12 @@ "tslib": "^2.0.0" } }, + "use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "requires": {} + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -10103,6 +10309,14 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/yoga-wasm-web/-/yoga-wasm-web-0.3.0.tgz", "integrity": "sha512-rD3L4jyMlO1m+RWU60lNwZQK5zmzglCV5fI1gTRikmpv3YzmNIZQbjyfE6cMNb9Xaly/C1SwemYGbsiOekMvnQ==" + }, + "zustand": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.3.2.tgz", + "integrity": "sha512-rd4haDmlwMTVWVqwvgy00ny8rtti/klRoZjFbL/MAcDnmD5qSw/RZc+Vddstdv90M5Lv6RPgWvm1Hivyn0QgJw==", + "requires": { + "use-sync-external-store": "1.2.0" + } } } } diff --git a/package.json b/package.json index 081a490..890e6ff 100644 --- a/package.json +++ b/package.json @@ -18,11 +18,13 @@ "@prisma/client": "^4.8.1", "@radix-ui/react-popover": "^1.0.2", "@radix-ui/react-tooltip": "^1.0.2", + "@react-oauth/google": "^0.7.0", "@types/node": "18.11.18", "@types/react": "18.0.26", "@types/react-dom": "18.0.10", "@vercel/analytics": "^0.1.6", "@vercel/og": "^0.0.26", + "axios": "^1.3.2", "classnames": "^2.3.2", "eslint": "8.31.0", "eslint-config-next": "13.1.1", @@ -37,7 +39,8 @@ "react-markdown": "^8.0.4", "react-wrap-balancer": "^0.3.0", "typescript": "4.9.4", - "use-debounce": "^9.0.3" + "use-debounce": "^9.0.3", + "zustand": "^4.3.2" }, "devDependencies": { "@tailwindcss/forms": "^0.5.3", diff --git a/pages/index.tsx b/pages/index.tsx index e2363e7..758654f 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -2,11 +2,16 @@ import Card from "@/components/home/card"; import Layout from "@/components/layout"; import Balancer from "react-wrap-balancer"; import { motion } from "framer-motion"; -import { FADE_DOWN_ANIMATION_VARIANTS } from "@/lib/constants"; +import { + FADE_DOWN_ANIMATION_VARIANTS, + READ_PILOT_TOKEN, +} from "@/lib/constants"; import { Github, LoadingDots, Twitter } from "@/components/shared/icons"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import LinkIcon from "@/components/shared/icons/link"; import CountingNumbers from "@/components/shared/counting-numbers"; +import { usePilotStore } from "@/lib/store"; +import { GoogleOAuthProvider } from "@react-oauth/google"; export default function Home() { const [url, setUrl] = useState(""); @@ -14,6 +19,10 @@ export default function Home() { const [loading, setLoading] = useState(false); const [results, setResults] = useState([]); + const { setAuthToken, authToken, fetchUserInfo } = usePilotStore( + (state) => state, + ); + const generateCards = async (e: any) => { e.preventDefault(); @@ -51,138 +60,173 @@ export default function Home() { setLoading(false); }; + useEffect(() => { + const token = localStorage.getItem(READ_PILOT_TOKEN); + if (token && token !== null) { + setAuthToken(token); + } + }, []); + + useEffect(() => { + if (authToken) { + fetchUserInfo(); + } + }, [authToken]); + return ( - -
- + + +
- - -

- Introducing Read Pilot -

-
- - -

Star on GitHub

-
-
+ + +

+ Introducing Read Pilot +

+
+ + +

Star on GitHub

+
+ - - - Read Online Articles With -
- - Intelligence - -
-
- - - Read Pilot analyzes online articles and generate Q&A cards for - you. - - + + + Read Online Articles With +
+ + Intelligence + +
+
+ + + Read Pilot analyzes online articles and generate Q&A cards for + you. + + - - - Trusted by{" "} - {" "} - users,{" "} - {" "} - links have been analyzed. - - + + + Trusted by{" "} + {" "} + users,{" "} + {" "} + links have been analyzed. + + - -
- - { - setUrl((e.target as HTMLInputElement).value); - }} - required - className="block w-full rounded-2xl border border-gray-200 bg-white p-2 pl-12 text-lg text-gray-600 shadow-md focus:border-black focus:outline-none focus:ring-0" - /> -
-
+ +
+ + { + setUrl((e.target as HTMLInputElement).value); + }} + required + className="block w-full rounded-2xl border border-gray-200 bg-white p-2 pl-12 text-lg text-gray-600 shadow-md focus:border-black focus:outline-none focus:ring-0" + /> +
+
- - {!loading && ( - - )} - {loading && ( - + )} + {loading && ( + + )} + + ) : ( + - Analyzing - - + + Sign in to start + + )} - - {showGeneratedCards && ( -
- {results.map(({ q, a }) => ( - - ))} -
- )} -
-
+ {showGeneratedCards && ( +
+ {results.map(({ q, a }) => ( + + ))} +
+ )} +
+
+ ); } diff --git a/service.ts/request.ts b/service.ts/request.ts new file mode 100644 index 0000000..f47fc06 --- /dev/null +++ b/service.ts/request.ts @@ -0,0 +1,47 @@ +import { READ_PILOT_TOKEN } from "@/lib/constants"; +import axios, { AxiosError } from "axios"; + +export const instance = axios.create({ + baseURL: + process.env.NODE_ENV === "development" + ? "http://127.0.0.1:8000/" + : "https://api.pipe3.xyz/", + + timeout: 5000, +}); + +instance.interceptors.request.use((req) => { + if (req.url !== "/api/v1/readpilot/google/login") { + !req.headers && (req.headers = {} as any); + req.headers.Authorization = + `Bearer ${localStorage.getItem(READ_PILOT_TOKEN)}` || ""; + } + return req; +}); + +instance.interceptors.response.use( + (response) => { + return response; + }, + (error: AxiosError) => { + if (error && error.response) { + const status = error.response.status; + if (status === 401) { + localStorage.removeItem(READ_PILOT_TOKEN); + window.location.reload(); + } else { + } + } + return Promise.reject(error); + }, +); + +// ================================== User ================================ + +// Get User Info +export const getUserInfo = (params: any) => + instance.get("/api/v1/readpilot/google/info", { params }); + +// Google Login +export const googleLogin = (params: any) => + instance.post("/api/v1/readpilot/google/login", params); diff --git a/yarn.lock b/yarn.lock index fcc0719..f3298b1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -427,6 +427,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@react-oauth/google@^0.7.0": + "integrity" "sha512-KLLYlLYYpXf1zmcrsJPAxImvoq2b0Rs0Pi/6GYfdwjmTh66Pf6jkZ4fpO10tuJuUPWvEeqloPEC/i4BySgbYVQ==" + "resolved" "https://registry.npmjs.org/@react-oauth/google/-/google-0.7.0.tgz" + "version" "0.7.0" + "@resvg/resvg-wasm@2.0.0-alpha.4": "integrity" "sha512-pWIG9a/x1ky8gXKRhPH1OPKpHFoMN1ISLbJ+O+gPXQHIAKhNd5I28RlWf7q576hAOQA9JZTlo3p/M2uyLzJmmw==" "resolved" "https://registry.npmjs.org/@resvg/resvg-wasm/-/resvg-wasm-2.0.0-alpha.4.tgz" @@ -734,6 +739,11 @@ "resolved" "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz" "version" "0.0.7" +"asynckit@^0.4.0": + "integrity" "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "resolved" "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + "version" "0.4.0" + "autoprefixer@^10.4.13": "integrity" "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==" "resolved" "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz" @@ -756,6 +766,15 @@ "resolved" "https://registry.npmjs.org/axe-core/-/axe-core-4.6.2.tgz" "version" "4.6.2" +"axios@^1.3.2": + "integrity" "sha512-1M3O703bYqYuPhbHeya5bnhpYVsDDRyQSabNja04mZtboLNSuZ4YrltestrLXfHgmzua4TpUqRiVKbiQuo2epw==" + "resolved" "https://registry.npmjs.org/axios/-/axios-1.3.2.tgz" + "version" "1.3.2" + dependencies: + "follow-redirects" "^1.15.0" + "form-data" "^4.0.0" + "proxy-from-env" "^1.1.0" + "axobject-query@^3.1.1": "integrity" "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==" "resolved" "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz" @@ -890,6 +909,13 @@ "resolved" "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" "version" "1.1.4" +"combined-stream@^1.0.8": + "integrity" "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==" + "resolved" "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" + "version" "1.0.8" + dependencies: + "delayed-stream" "~1.0.0" + "comma-separated-tokens@^2.0.0": "integrity" "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==" "resolved" "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz" @@ -1047,6 +1073,11 @@ "resolved" "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz" "version" "1.0.1" +"delayed-stream@~1.0.0": + "integrity" "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + "resolved" "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + "version" "1.0.0" + "dequal@^2.0.0": "integrity" "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==" "resolved" "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz" @@ -1524,6 +1555,11 @@ dependencies: "tabbable" "^6.0.1" +"follow-redirects@^1.15.0": + "integrity" "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + "resolved" "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz" + "version" "1.15.2" + "for-each@^0.3.3": "integrity" "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==" "resolved" "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz" @@ -1531,6 +1567,15 @@ dependencies: "is-callable" "^1.1.3" +"form-data@^4.0.0": + "integrity" "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==" + "resolved" "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" + "version" "4.0.0" + dependencies: + "asynckit" "^0.4.0" + "combined-stream" "^1.0.8" + "mime-types" "^2.1.12" + "fraction.js@^4.2.0": "integrity" "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==" "resolved" "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz" @@ -2386,6 +2431,18 @@ "braces" "^3.0.2" "picomatch" "^2.3.1" +"mime-db@1.52.0": + "integrity" "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + "resolved" "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + "version" "1.52.0" + +"mime-types@^2.1.12": + "integrity" "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==" + "resolved" "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + "version" "2.1.35" + dependencies: + "mime-db" "1.52.0" + "mini-svg-data-uri@^1.2.3": "integrity" "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==" "resolved" "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz" @@ -2790,6 +2847,11 @@ "resolved" "https://registry.npmjs.org/property-information/-/property-information-6.2.0.tgz" "version" "6.2.0" +"proxy-from-env@^1.1.0": + "integrity" "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + "resolved" "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" + "version" "1.1.0" + "punycode@^2.1.0": "integrity" "sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw==" "resolved" "https://registry.npmjs.org/punycode/-/punycode-2.2.0.tgz" @@ -2877,7 +2939,7 @@ "resolved" "https://registry.npmjs.org/react-wrap-balancer/-/react-wrap-balancer-0.3.0.tgz" "version" "0.3.0" -"react@^16.5.1 || ^17.0.0 || ^18.0.0", "react@^16.8 || ^17.0 || ^18.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8||^17||^18", "react@^16.9.0 || ^17.0.0 || ^18.0.0", "react@^17.0.2 || ^18", "react@^18.0.0", "react@^18.2.0", "react@>= 16.8.0 || 17.x.x || ^18.0.0-0", "react@>=16", "react@>=16.3.0", "react@>=16.8.0", "react@18.2.0": +"react@^16.5.1 || ^17.0.0 || ^18.0.0", "react@^16.8 || ^17.0 || ^18.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8||^17||^18", "react@^16.9.0 || ^17.0.0 || ^18.0.0", "react@^17.0.2 || ^18", "react@^18.0.0", "react@^18.2.0", "react@>= 16.8.0 || 17.x.x || ^18.0.0-0", "react@>=16", "react@>=16.3.0", "react@>=16.8", "react@>=16.8.0", "react@18.2.0": "integrity" "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==" "resolved" "https://registry.npmjs.org/react/-/react-18.2.0.tgz" "version" "18.2.0" @@ -3431,6 +3493,11 @@ "detect-node-es" "^1.1.0" "tslib" "^2.0.0" +"use-sync-external-store@1.2.0": + "integrity" "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==" + "resolved" "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz" + "version" "1.2.0" + "util-deprecate@^1.0.2": "integrity" "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" "resolved" "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" @@ -3575,3 +3642,10 @@ "integrity" "sha512-rD3L4jyMlO1m+RWU60lNwZQK5zmzglCV5fI1gTRikmpv3YzmNIZQbjyfE6cMNb9Xaly/C1SwemYGbsiOekMvnQ==" "resolved" "https://registry.npmjs.org/yoga-wasm-web/-/yoga-wasm-web-0.3.0.tgz" "version" "0.3.0" + +"zustand@^4.3.2": + "integrity" "sha512-rd4haDmlwMTVWVqwvgy00ny8rtti/klRoZjFbL/MAcDnmD5qSw/RZc+Vddstdv90M5Lv6RPgWvm1Hivyn0QgJw==" + "resolved" "https://registry.npmjs.org/zustand/-/zustand-4.3.2.tgz" + "version" "4.3.2" + dependencies: + "use-sync-external-store" "1.2.0"