-
Notifications
You must be signed in to change notification settings - Fork 1
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] created AuthContext #20
base: main
Are you sure you want to change the base?
Changes from all commits
d2c8b96
3e7cacf
10cc0fc
b02515d
d76ae9e
1e13530
088ed77
0d4d431
d6f162a
594ad58
3a7501c
e578a4d
958de99
1beb0f8
993d3c8
5050894
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,50 +2,32 @@ | |
|
||
import { useState } from 'react'; | ||
import { useRouter } from 'next/navigation'; | ||
import supabase from '../../../api/supabase/createClient'; | ||
import { AuthProvider, useAuth } from '../../utils/AuthProvider'; | ||
|
||
export default function Login() { | ||
export default function LoginLayout() { | ||
return ( | ||
<AuthProvider> | ||
<Login /> | ||
</AuthProvider> | ||
); | ||
} | ||
Comment on lines
+9
to
+13
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @rachaelch3n follow up on these comments about moving AuthProvider to |
||
|
||
function Login() { | ||
const { signIn } = useAuth(); // Use `signIn` function from AuthProvider | ||
const [email, setEmail] = useState(''); | ||
const [password, setPassword] = useState(''); | ||
const { push } = useRouter(); | ||
|
||
const handleSignUp = async () => { | ||
const { data, error } = await supabase.auth.signUp({ | ||
email, | ||
password, | ||
}); | ||
|
||
if (error) { | ||
// Check if the error is due to an already registered email | ||
if (error.message.includes('User already registered')) { | ||
throw new Error( | ||
'This email is already registered. Please try signing in instead.', | ||
); | ||
} else { | ||
throw new Error( | ||
`An error occurred trying to sign up: ${error.message}`, | ||
); | ||
const router = useRouter(); | ||
|
||
const handleLogin = async () => { | ||
// Define handleLogin | ||
try { | ||
await signIn(email, password); | ||
router.push('/'); // Redirect to the home page on success | ||
} catch (error) { | ||
if (error instanceof Error) { | ||
console.error('Login Error:', error.message); | ||
} | ||
} | ||
|
||
push('/'); | ||
|
||
return data; | ||
}; | ||
|
||
const handleSignInWithEmail = async () => { | ||
const { data, error } = await supabase.auth.signInWithPassword({ | ||
email, | ||
password, | ||
}); | ||
|
||
if (error) { | ||
throw new Error(`An error occurred trying to sign in: ${error}`); | ||
} | ||
|
||
push('/'); | ||
|
||
return data; | ||
}; | ||
|
||
return ( | ||
|
@@ -63,10 +45,7 @@ export default function Login() { | |
value={password} | ||
placeholder="Password" | ||
/> | ||
<button type="button" onClick={handleSignUp}> | ||
Sign up | ||
</button> | ||
<button type="button" onClick={handleSignInWithEmail}> | ||
<button type="button" onClick={handleLogin}> | ||
Sign in | ||
</button> | ||
</> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
'use client'; | ||
|
||
import { useState } from 'react'; | ||
import { useRouter } from 'next/navigation'; | ||
import { AuthProvider, useAuth } from '../../utils/AuthProvider'; | ||
|
||
export default function SignUpLayout() { | ||
return ( | ||
<AuthProvider> | ||
<SignUp /> | ||
</AuthProvider> | ||
); | ||
} | ||
|
||
function SignUp() { | ||
const { signUp } = useAuth(); | ||
const [email, setEmail] = useState(''); | ||
const [password, setPassword] = useState(''); | ||
const router = useRouter(); | ||
|
||
const handleSignUp = async () => { | ||
// Define handleSignUp | ||
try { | ||
await signUp(email, password); | ||
router.push('/'); | ||
} catch (error) { | ||
if (error instanceof Error) { | ||
console.error(error.message); | ||
} | ||
} | ||
}; | ||
|
||
return ( | ||
<> | ||
<input | ||
name="email" | ||
onChange={e => setEmail(e.target.value)} | ||
value={email} | ||
placeholder="Email" | ||
/> | ||
<input | ||
type="password" | ||
name="password" | ||
onChange={e => setPassword(e.target.value)} | ||
value={password} | ||
placeholder="Password" | ||
/> | ||
<input | ||
type="password" | ||
name="Confirm Password" | ||
onChange={e => setPassword(e.target.value)} | ||
value={password} | ||
placeholder="Confirm Password" | ||
/> | ||
<button type="button" onClick={handleSignUp}> | ||
Sign up | ||
</button>{' '} | ||
{/* Sign up button */} | ||
</> | ||
); | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. while this is a good test of using AuthProvider, we don't want a dashboard page yet, so let's delete this file. You can leave the code in this file as a block comment in AuthProvider.tsx for example usage |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/* | ||
'use client'; | ||
|
||
import { AuthProvider, useAuth } from '../utils/AuthProvider'; | ||
|
||
export default function DashboardPage() { | ||
return ( | ||
<AuthProvider> | ||
<Dashboard /> | ||
</AuthProvider> | ||
); | ||
} | ||
|
||
function Dashboard() { | ||
const { authUser, isLoggedIn, signOut } = useAuth(); | ||
|
||
if (!authUser) { | ||
return <p>Loading...</p>; | ||
} | ||
|
||
return ( | ||
<> | ||
<header> | ||
<h1>Dashboard</h1> | ||
</header> | ||
|
||
<main> | ||
<p>User is currently: {isLoggedIn ? 'Logged In' : 'Logged Out'}</p> | ||
{authUser && <p>User name: {authUser.email}</p>}{' '} | ||
<button onClick={signOut}>Log Out</button> | ||
</main> | ||
</> | ||
); | ||
} | ||
|
||
*/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
'use client'; | ||
|
||
import { | ||
createContext, | ||
ReactNode, | ||
useContext, | ||
useEffect, | ||
useState, | ||
} from 'react'; | ||
import { useRouter } from 'next/navigation'; | ||
import { Session } from '@supabase/supabase-js'; | ||
import supabase from '../../api/supabase/createClient'; | ||
|
||
interface AuthContextType { | ||
userId: string | null; | ||
email: string | null; | ||
session: Session | null; | ||
isLoggedIn: boolean; | ||
signUp: (email: string, password: string) => Promise<void>; | ||
signIn: (email: string, password: string) => Promise<void>; | ||
signOut: () => Promise<void>; | ||
loading: boolean; | ||
} | ||
|
||
// Create the AuthContext | ||
const AuthContext = createContext<AuthContextType | null>(null); | ||
|
||
// Custom hook to use AuthContext | ||
export function useAuth() { | ||
const context = useContext(AuthContext); | ||
if (!context) { | ||
throw new Error('useAuth must be used within an AuthProvider'); | ||
} | ||
return context; | ||
} | ||
|
||
// AuthProvider component that wraps around your app or specific pages | ||
export function AuthProvider({ children }: { children: ReactNode }) { | ||
const [session, setSession] = useState<Session | null>(null); | ||
const [userId, setUserId] = useState<string | null>(null); | ||
const [email, setEmail] = useState<string | null>(null); | ||
const [isLoggedIn, setIsLoggedIn] = useState(false); | ||
const [loading, setLoading] = useState(true); | ||
const { push } = useRouter(); | ||
|
||
// Sign Up function | ||
const signUp = async (email: string, password: string) => { | ||
try { | ||
const { data, error } = await supabase.auth.signUp({ email, password }); | ||
if (error) throw new Error(error.message); | ||
|
||
if (data.user) { | ||
setUserId(data.user.id ?? null); | ||
setEmail(data.user.email ?? null); | ||
setIsLoggedIn(true); | ||
} | ||
} catch (error) { | ||
if (error instanceof Error) { | ||
console.error('Sign-up Error:', error.message); | ||
} | ||
} | ||
}; | ||
|
||
// Sign In function | ||
const signIn = async (email: string, password: string) => { | ||
try { | ||
const { data, error } = await supabase.auth.signInWithPassword({ | ||
email, | ||
password, | ||
}); | ||
if (error) throw new Error(error.message); | ||
|
||
setUserId(data.user.id ?? null); | ||
setEmail(data.user.email ?? null); | ||
setIsLoggedIn(true); | ||
} catch (error) { | ||
if (error instanceof Error) { | ||
console.error('Sign-in Error:', error.message); | ||
} | ||
} | ||
}; | ||
|
||
// Sign Out function | ||
const signOut = async () => { | ||
try { | ||
const { error } = await supabase.auth.signOut(); | ||
if (error) throw new Error(error.message); | ||
setUserId(null); | ||
setEmail(null); | ||
setIsLoggedIn(false); | ||
setSession(null); | ||
// push('/login'); // Redirect to login after sign-out | ||
} catch (error) { | ||
if (error instanceof Error) { | ||
console.error('Sign-out Error:', error.message); | ||
} | ||
} | ||
}; | ||
|
||
// Fetch the currently logged-in user on mount and redirect to dashboard if signed in | ||
useEffect(() => { | ||
const getUser = async () => { | ||
setLoading(true); | ||
const { data, error } = await supabase.auth.getSession(); | ||
if (error) { | ||
console.error('Session Error:', error.message); | ||
} else if (data.session) { | ||
setSession(data.session); | ||
const { user } = data.session; | ||
if (user) { | ||
setUserId(user.id ?? null); | ||
setEmail(user.email ?? null); | ||
setIsLoggedIn(true); | ||
} | ||
} else { | ||
setUserId(null); | ||
setEmail(null); | ||
setIsLoggedIn(false); | ||
} | ||
setLoading(false); | ||
}; | ||
getUser(); | ||
|
||
// Listen for auth state changes | ||
const { | ||
data: { subscription }, | ||
} = supabase.auth.onAuthStateChange((_event, session) => { | ||
if (session) { | ||
setSession(session); | ||
const { user } = session; | ||
if (user) { | ||
setUserId(user.id ?? null); | ||
setEmail(user.email ?? null); | ||
setIsLoggedIn(true); | ||
} | ||
} else { | ||
setUserId(null); | ||
setEmail(null); | ||
setIsLoggedIn(false); | ||
setSession(null); | ||
|
||
// if (!loading && window.location.pathname !== '/signup') { | ||
// push('/login'); | ||
// } | ||
} | ||
}); | ||
|
||
return () => subscription?.unsubscribe(); | ||
}, [push, loading]); | ||
|
||
const value: AuthContextType = { | ||
userId, | ||
email, | ||
session, | ||
isLoggedIn, | ||
signUp, | ||
signIn, | ||
signOut, | ||
loading, | ||
}; | ||
|
||
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,9 +14,11 @@ | |
"pre-commit": "(pnpm run tsc || true) && (pnpm run lint || true) && pnpm run prettier" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cc @ccatherinetan, maybe we want to remove this change & also the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @pragyakallanagoudar what change are you referring to? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this comment is fully on the wrong line lol but i'm referring to the one on line 17/20 of this file There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes sg! we'll delete the |
||
}, | ||
"dependencies": { | ||
"@next/swc-darwin-arm64": "^15.0.0", | ||
"@supabase/supabase-js": "^2.45.4", | ||
"dotenv": "^16.4.5", | ||
"next": "^14.2.10", | ||
"next": "^14.2.10", | ||
"react": "^18", | ||
"react-dom": "^18", | ||
"react-multi-select-component": "^4.3.4", | ||
|
@@ -33,7 +35,8 @@ | |
"eslint-config-prettier": "^9.1.0", | ||
"husky": "^9.1.5", | ||
"prettier": "^3.3.3", | ||
"typescript": "^5", | ||
"typescript": "5.4", | ||
"typescript": "5.4", | ||
"yarnhook": "^0.6.2" | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You only need to wrap the
layout.tsx
file with theAuthProvider
to give its child screens access to context data. It seems that an Auth context would need to wrap the entire app, so look into wrapping the rootlayout.tsx
file with the auth provider.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1! See outermost
layout.tsx
in IJP's codebase