Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Google Auth #13

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
45 changes: 34 additions & 11 deletions components/layout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
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";
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,
Expand All @@ -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 (
<>
Expand All @@ -46,17 +60,26 @@ export default function Layout({
></Image>
<p>Read Pilot</p>
</Link>
<div>
<div className="flex items-center">
<AnimatePresence>
{!session && status !== "loading" ? (
<motion.a
<motion.a
className="mr-2 rounded-full border border-black bg-black p-1.5 px-4 text-sm text-white transition-all hover:bg-white hover:text-black"
href="https://twitter.com/Tisoga"
target="_blank"
{...FADE_IN_ANIMATION_SETTINGS}
>
Subscribe
</motion.a>
{!authToken ? (
<motion.button
className="rounded-full border border-black bg-black p-1.5 px-4 text-sm text-white transition-all hover:bg-white hover:text-black"
href="https://twitter.com/Tisoga"
target="_blank"
{...FADE_IN_ANIMATION_SETTINGS}
onClick={() => {
login();
}}
>
Subscribe
</motion.a>
Sign in
</motion.button>
) : (
<UserDropdown />
)}
Expand Down
34 changes: 12 additions & 22 deletions components/layout/user-dropdown.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<motion.div
Expand All @@ -21,23 +20,19 @@ export default function UserDropdown() {
<Popover
content={
<div className="w-full rounded-md bg-white p-2 sm:w-56">
{/* <Link
className="flex items-center justify-start space-x-2 relative w-full rounded-md p-2 text-left text-sm transition-all duration-75 hover:bg-gray-100"
href="/dashboard"
>
<LayoutDashboard className="h-4 w-4" />
<p className="text-sm">Dashboard</p>
</Link> */}
<button
{/* <button
className="relative flex w-full cursor-not-allowed items-center justify-start space-x-2 rounded-md p-2 text-left text-sm transition-all duration-75 hover:bg-gray-100"
disabled
>
<LayoutDashboard className="h-4 w-4" />
<p className="text-sm">Dashboard</p>
</button>
</button> */}
<button
className="relative flex w-full items-center justify-start space-x-2 rounded-md p-2 text-left text-sm transition-all duration-75 hover:bg-gray-100"
onClick={() => signOut({ redirect: false })}
onClick={() => {
localStorage.removeItem(READ_PILOT_TOKEN);
setAuthToken();
}}
>
<LogOut className="h-4 w-4" />
<p className="text-sm">Logout</p>
Expand All @@ -52,12 +47,7 @@ export default function UserDropdown() {
onClick={() => setOpenPopover(!openPopover)}
className="flex h-8 w-8 items-center justify-center overflow-hidden rounded-full border border-gray-300 transition-all duration-75 focus:outline-none active:scale-95 sm:h-9 sm:w-9"
>
<Image
alt={email}
src={image || `https://avatars.dicebear.com/api/micah/${email}.svg`}
width={40}
height={40}
/>
<Image alt={"avatar"} src={userInfo.avatar} width={40} height={40} />
</button>
</Popover>
</motion.div>
Expand Down
5 changes: 5 additions & 0 deletions lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
5 changes: 5 additions & 0 deletions lib/interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface UserInfo {
avatar: string;
email: string;
id: string;
}
37 changes: 37 additions & 0 deletions lib/store/index.ts
Original file line number Diff line number Diff line change
@@ -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<void>;
}

export const usePilotStore = create<PilotStoreInterface>()((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) {}
},
}));
Loading