Skip to content

Commit

Permalink
[auth] Add auth context and useSession hook (#17)
Browse files Browse the repository at this point in the history
* Add AuthContext

* Add useSession hook in authentication components

* Run formatter

* Fix tsc errors

---------

Co-authored-by: Aditya Pawar <[email protected]>
  • Loading branch information
adityapawar1 and adityapawar1 authored Oct 8, 2023
1 parent e30c936 commit e24a4f3
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 57 deletions.
13 changes: 8 additions & 5 deletions src/app/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Stack } from 'expo-router';
import { AuthContextProvider } from '../utils/AuthContext';

function StackLayout() {
return (
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="settings" options={{ headerShown: false }} />
<Stack.Screen name="auth" options={{ headerShown: false }} />
</Stack>
<AuthContextProvider>
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="settings" options={{ headerShown: false }} />
<Stack.Screen name="auth" options={{ headerShown: false }} />
</Stack>
</AuthContextProvider>
);
}

Expand Down
22 changes: 3 additions & 19 deletions src/app/auth/onboarding.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,14 @@
import { useState, useEffect } from 'react';
import { View } from 'react-native';
import { Session } from '@supabase/supabase-js';
import supabase from '../../utils/supabase';
import Login from '../../components/Login';
import Account from '../../components/Account';
import { useSession } from '../../utils/AuthContext';

function OnboardingScreen() {
const [session, setSession] = useState<Session | null>(null);

useEffect(() => {
supabase.auth.getSession().then(({ data: { session: newSession } }) => {
setSession(newSession);
});

supabase.auth.onAuthStateChange((_event, newSession) => {
setSession(newSession);
});
}, []);
const { session } = useSession();

return (
<View>
{session && session.user ? (
<Account key={session.user.id} session={session} />
) : (
<Login />
)}
{session && session.user ? <Account key={session.user.id} /> : <Login />}
</View>
);
}
Expand Down
22 changes: 3 additions & 19 deletions src/app/auth/signup.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,14 @@
import { Session } from '@supabase/supabase-js';
import { useEffect, useState } from 'react';
import { View } from 'react-native';
import Account from '../../components/Account';
import Login from '../../components/Login';
import supabase from '../../utils/supabase';
import { useSession } from '../../utils/AuthContext';

function SignUpScreen() {
const [session, setSession] = useState<Session | null>(null);

useEffect(() => {
supabase.auth.getSession().then(({ data: { session: newSession } }) => {
setSession(newSession);
});

supabase.auth.onAuthStateChange((_event, newSession) => {
setSession(newSession);
});
}, []);
const { session } = useSession();

return (
<View>
{session && session.user ? (
<Account key={session.user.id} session={session} />
) : (
<Login />
)}
{session && session.user ? <Account key={session.user.id} /> : <Login />}
</View>
);
}
Expand Down
8 changes: 5 additions & 3 deletions src/components/Account.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useState, useEffect } from 'react';
import { StyleSheet, View, Alert, ScrollView, Platform } from 'react-native';
import { Button, Input } from 'react-native-elements';
import { Session } from '@supabase/supabase-js';
import DateTimePicker from '@react-native-community/datetimepicker';
import supabase from '../utils/supabase';
import UserStringInput from './UserStringInput';
import { useSession } from '../utils/AuthContext';

const styles = StyleSheet.create({
container: {
Expand All @@ -21,7 +21,9 @@ const styles = StyleSheet.create({
},
});

export default function Account({ session }: { session: Session }) {
export default function Account() {
const { session, signOut } = useSession();

const [loading, setLoading] = useState(true);
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
Expand Down Expand Up @@ -160,7 +162,7 @@ export default function Account({ session }: { session: Session }) {
/>
</View>
<View style={styles.verticallySpaced}>
<Button title="Sign Out" onPress={() => supabase.auth.signOut()} />
<Button title="Sign Out" onPress={signOut} />
</View>
</ScrollView>
);
Expand Down
23 changes: 12 additions & 11 deletions src/components/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { Alert, StyleSheet, View } from 'react-native';
import { Button, Input } from 'react-native-elements';
import supabase from '../utils/supabase';
import { useSession } from '../utils/AuthContext';

const styles = StyleSheet.create({
container: {
Expand All @@ -19,30 +19,31 @@ const styles = StyleSheet.create({
});

export default function Login() {
const sessionHandler = useSession();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [loading, setLoading] = useState(false);

const signInWithEmail = async () => {
setLoading(true);
const { error } = await supabase.auth.signInWithPassword({
email,
password,
});
const { error } = await sessionHandler.signInWithEmail(email, password);

if (error) Alert.alert(error.message);
setLoading(false);
};

const signUpWithEmail = async () => {
setLoading(true);
const { error } = await supabase.auth.signUp({
email,
password,
});
const { error } = await sessionHandler.signUp(email, password);

if (error) Alert.alert(error.message);
console.error(error);
if (error) {
Alert.alert(error.message);
console.error(error);
} else {
Alert.alert(
'Please follow the directions in the confirmation email to activate your account.',
);
}
setLoading(false);
};

Expand Down
86 changes: 86 additions & 0 deletions src/utils/AuthContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { AuthResponse, Session } from '@supabase/supabase-js';
import React, {
createContext,
useContext,
useEffect,
useMemo,
useState,
} from 'react';
import supabase from './supabase';

export interface AuthState {
session: Session | null;
signIn: (newSession: Session | null) => void;
signUp: (email: string, password: string) => Promise<AuthResponse>;
signInWithEmail: (email: string, password: string) => Promise<AuthResponse>;
signOut: () => void;
}

const AuthContext = createContext({} as AuthState);

export function useSession() {
const value = useContext(AuthContext);
if (process.env.NODE_ENV !== 'production') {
if (!value) {
throw new Error(
'useSession must be wrapped in a <AuthContextProvider />',
);
}
}

return value;
}

export function AuthContextProvider({
children,
}: {
children: React.ReactNode;
}) {
const [session, setSession] = useState<Session | null>(null);

useEffect(() => {
supabase.auth.getSession().then(({ data: { session: newSession } }) => {
setSession(newSession);
});

supabase.auth.onAuthStateChange((_event, newSession) => {
setSession(newSession);
});
}, []);

const signIn = (newSession: Session | null) => {
setSession(newSession);
};

const signInWithEmail = async (email: string, password: string) =>
supabase.auth.signInWithPassword({
email,
password,
}); // will trigger the use effect to update the session
const signUp = async (email: string, password: string) =>
supabase.auth.signUp({
email,
password,
}); // will trigger the use effect to update the session
const signOut = () => {
supabase.auth.signOut();
setSession(null);
};

const authContextValue = useMemo(
() => ({
session,
signUp,
signIn,
signInWithEmail,
signOut,
}),
[session],
);

return (
<AuthContext.Provider value={authContextValue}>
{children}
</AuthContext.Provider>
);
}

0 comments on commit e24a4f3

Please sign in to comment.