mirror of
https://github.com/SrIzan10/featheroom.git
synced 2026-06-06 00:56:49 +00:00
feat: google auth welcome page
This commit is contained in:
113
app/(app)/_layout.tsx
Normal file
113
app/(app)/_layout.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons'
|
||||
import {
|
||||
useFonts,
|
||||
JetBrainsMono_400Regular,
|
||||
} from '@expo-google-fonts/jetbrains-mono'
|
||||
import { NotoSans_400Regular } from '@expo-google-fonts/noto-sans'
|
||||
import { Redirect, SplashScreen, Stack, useRootNavigationState } from 'expo-router'
|
||||
import * as SecureStore from 'expo-secure-store'
|
||||
import React from 'react'
|
||||
import { useColorScheme } from 'react-native'
|
||||
import { PaperProvider } from 'react-native-paper'
|
||||
|
||||
import { useAuth } from '@/lib/providers/auth'
|
||||
import { Setting } from '@/lib/types'
|
||||
import { StackHeader, Themes } from '@/lib/ui'
|
||||
|
||||
export {
|
||||
// Catch any errors thrown by the Layout component.
|
||||
ErrorBoundary,
|
||||
} from 'expo-router'
|
||||
|
||||
export const unstable_settings = {
|
||||
// Ensure that reloading on `/modal` keeps a back button present.
|
||||
initialRouteName: '(root)/login',
|
||||
}
|
||||
|
||||
// Prevent the splash screen from auto-hiding before asset loading is complete.
|
||||
SplashScreen.preventAutoHideAsync()
|
||||
|
||||
const RootLayout = () => {
|
||||
const { user } = useAuth()
|
||||
const [loaded, error] = useFonts({
|
||||
NotoSans_400Regular,
|
||||
JetBrainsMono_400Regular,
|
||||
...MaterialCommunityIcons.font,
|
||||
})
|
||||
|
||||
// Get authentication state
|
||||
const rootNavigationState = useRootNavigationState()
|
||||
|
||||
// Expo Router uses Error Boundaries to catch errors in the navigation tree.
|
||||
React.useEffect(() => {
|
||||
if (error) throw error
|
||||
}, [error])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (loaded) {
|
||||
SplashScreen.hideAsync()
|
||||
}
|
||||
}, [loaded])
|
||||
// Make sure we have the navigation state before showing content
|
||||
if (!loaded || !rootNavigationState) {
|
||||
return null
|
||||
}
|
||||
if (!user) {
|
||||
console.log('redirecting inside the root')
|
||||
return <Redirect href="/login" />
|
||||
}
|
||||
|
||||
return <RootLayoutNav />
|
||||
}
|
||||
|
||||
const RootLayoutNav = () => {
|
||||
const colorScheme = useColorScheme()
|
||||
const [settings, setSettings] = React.useState<Setting>({
|
||||
theme: 'auto',
|
||||
color: 'default',
|
||||
})
|
||||
|
||||
// Load settings from the device
|
||||
React.useEffect(() => {
|
||||
SecureStore.getItemAsync('settings').then((result) => {
|
||||
if (result === null) {
|
||||
SecureStore.setItemAsync('settings', JSON.stringify(settings)).then(
|
||||
(res) => console.log(res),
|
||||
)
|
||||
}
|
||||
|
||||
setSettings(JSON.parse(result ?? JSON.stringify(settings)))
|
||||
})
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<PaperProvider
|
||||
theme={
|
||||
Themes[
|
||||
settings.theme === 'auto' ? (colorScheme ?? 'dark') : settings.theme
|
||||
][settings.color]
|
||||
}
|
||||
>
|
||||
<Stack
|
||||
screenOptions={{
|
||||
animation: 'slide_from_bottom',
|
||||
header: (props) => (
|
||||
<StackHeader navProps={props} children={undefined} />
|
||||
),
|
||||
}}
|
||||
>
|
||||
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="drawer" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="search" options={{ title: 'Search' }} />
|
||||
<Stack.Screen
|
||||
name="modal"
|
||||
options={{ title: 'Modal', presentation: 'modal' }}
|
||||
/>
|
||||
</Stack>
|
||||
</PaperProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default RootLayout
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Text } from "react-native-paper";
|
||||
|
||||
export default function Login() {
|
||||
return (
|
||||
<Text>Login please</Text>
|
||||
)
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import { ScrollViewStyleReset } from 'expo-router/html'
|
||||
import React from 'react'
|
||||
|
||||
// This file is web-only and used to configure the root HTML for every
|
||||
// web page during static rendering.
|
||||
// The contents of this function only run in Node.js environments and
|
||||
// do not have access to the DOM or browser APIs.
|
||||
export default function Root({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, shrink-to-fit=no"
|
||||
/>
|
||||
|
||||
{/*
|
||||
Disable body scrolling on web. This makes ScrollView components work closer to how they do on native.
|
||||
However, body scrolling is often nice to have for mobile web. If you want to enable it, remove this line.
|
||||
*/}
|
||||
<ScrollViewStyleReset />
|
||||
|
||||
{/* Using raw CSS styles as an escape-hatch to ensure the background color never flickers in dark-mode. */}
|
||||
<style dangerouslySetInnerHTML={{ __html: responsiveBackground }} />
|
||||
{/* Add any additional <head> elements that you want globally available on web... */}
|
||||
</head>
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
|
||||
const responsiveBackground = `
|
||||
body {
|
||||
background-color: #fff;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: #000;
|
||||
}
|
||||
}`
|
||||
106
app/_layout.tsx
106
app/_layout.tsx
@@ -1,111 +1,11 @@
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons'
|
||||
import {
|
||||
useFonts,
|
||||
JetBrainsMono_400Regular,
|
||||
} from '@expo-google-fonts/jetbrains-mono'
|
||||
import { NotoSans_400Regular } from '@expo-google-fonts/noto-sans'
|
||||
import { Redirect, SplashScreen, Stack, useRootNavigationState, useRouter } from 'expo-router'
|
||||
import * as SecureStore from 'expo-secure-store'
|
||||
import React from 'react'
|
||||
import { Platform, useColorScheme } from 'react-native'
|
||||
import { PaperProvider } from 'react-native-paper'
|
||||
|
||||
import { AuthProvider, useAuth } from '@/lib/providers/auth'
|
||||
import { Setting } from '@/lib/types'
|
||||
import { StackHeader, Themes } from '@/lib/ui'
|
||||
|
||||
export {
|
||||
// Catch any errors thrown by the Layout component.
|
||||
ErrorBoundary,
|
||||
} from 'expo-router'
|
||||
|
||||
export const unstable_settings = {
|
||||
// Ensure that reloading on `/modal` keeps a back button present.
|
||||
initialRouteName: '(tabs)',
|
||||
}
|
||||
|
||||
// Prevent the splash screen from auto-hiding before asset loading is complete.
|
||||
SplashScreen.preventAutoHideAsync()
|
||||
|
||||
const RootLayout = () => {
|
||||
const [loaded, error] = useFonts({
|
||||
NotoSans_400Regular,
|
||||
JetBrainsMono_400Regular,
|
||||
...MaterialCommunityIcons.font,
|
||||
})
|
||||
|
||||
// Get authentication state
|
||||
const rootNavigationState = useRootNavigationState()
|
||||
|
||||
// Expo Router uses Error Boundaries to catch errors in the navigation tree.
|
||||
React.useEffect(() => {
|
||||
if (error) throw error
|
||||
}, [error])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (loaded) {
|
||||
SplashScreen.hideAsync()
|
||||
}
|
||||
}, [loaded])
|
||||
|
||||
// Make sure we have the navigation state before showing content
|
||||
if (!loaded || !rootNavigationState) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <RootLayoutNav />
|
||||
}
|
||||
|
||||
const RootLayoutNav = () => {
|
||||
const colorScheme = useColorScheme()
|
||||
const [settings, setSettings] = React.useState<Setting>({
|
||||
theme: 'auto',
|
||||
color: 'default',
|
||||
})
|
||||
|
||||
// Load settings from the device
|
||||
React.useEffect(() => {
|
||||
SecureStore.getItemAsync('settings').then((result) => {
|
||||
if (result === null) {
|
||||
SecureStore.setItemAsync('settings', JSON.stringify(settings)).then(
|
||||
(res) => console.log(res),
|
||||
)
|
||||
}
|
||||
|
||||
setSettings(JSON.parse(result ?? JSON.stringify(settings)))
|
||||
})
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
import { Redirect, Slot } from 'expo-router'
|
||||
|
||||
export default function Root() {
|
||||
return (
|
||||
<AuthProvider>
|
||||
<PaperProvider
|
||||
theme={
|
||||
Themes[
|
||||
settings.theme === 'auto' ? (colorScheme ?? 'dark') : settings.theme
|
||||
][settings.color]
|
||||
}
|
||||
>
|
||||
<Stack
|
||||
screenOptions={{
|
||||
animation: 'slide_from_bottom',
|
||||
header: (props) => (
|
||||
<StackHeader navProps={props} children={undefined} />
|
||||
),
|
||||
}}
|
||||
>
|
||||
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="drawer" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="search" options={{ title: 'Search' }} />
|
||||
<Stack.Screen
|
||||
name="modal"
|
||||
options={{ title: 'Modal', presentation: 'modal' }}
|
||||
/>
|
||||
</Stack>
|
||||
</PaperProvider>
|
||||
<Slot />
|
||||
</AuthProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default RootLayout
|
||||
|
||||
38
app/login.tsx
Normal file
38
app/login.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import { useAuth } from '@/lib/providers/auth'
|
||||
import { useRouter } from 'expo-router'
|
||||
import { useEffect } from 'react'
|
||||
import { View } from 'react-native'
|
||||
import { Button, Text } from 'react-native-paper'
|
||||
|
||||
function Login() {
|
||||
const { signIn, user } = useAuth()
|
||||
const router = useRouter()
|
||||
|
||||
useEffect(() => {
|
||||
if (user) {
|
||||
console.log('redicrecting')
|
||||
router.push('/')
|
||||
}
|
||||
}, [user])
|
||||
useEffect(() => {
|
||||
console.log('login page')
|
||||
}, [])
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
gap: 16,
|
||||
padding: 16,
|
||||
}}
|
||||
>
|
||||
<Text variant="headlineMedium">Welcome</Text>
|
||||
<Button mode="contained" icon="google" onPress={async () => await signIn()}>
|
||||
Login with Google
|
||||
</Button>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default Login
|
||||
@@ -7,6 +7,8 @@ import {
|
||||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
useCallback,
|
||||
useMemo,
|
||||
type ReactNode,
|
||||
} from 'react'
|
||||
|
||||
@@ -17,21 +19,21 @@ interface AuthProviderProps {
|
||||
export function AuthProvider({ children }: AuthProviderProps) {
|
||||
const [user, setUser] = useState<User | null>(null)
|
||||
|
||||
GoogleSignin.configure({
|
||||
scopes: [
|
||||
'https://www.googleapis.com/auth/classroom.courses.readonly',
|
||||
'https://www.googleapis.com/auth/classroom.coursework.me',
|
||||
'https://www.googleapis.com/auth/classroom.coursework.students',
|
||||
'https://www.googleapis.com/auth/classroom.coursework.students.readonly',
|
||||
'https://www.googleapis.com/auth/classroom.coursework.me.readonly',
|
||||
],
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
GoogleSignin.configure({
|
||||
scopes: [
|
||||
'https://www.googleapis.com/auth/classroom.courses.readonly',
|
||||
'https://www.googleapis.com/auth/classroom.coursework.me',
|
||||
'https://www.googleapis.com/auth/classroom.coursework.students',
|
||||
'https://www.googleapis.com/auth/classroom.coursework.students.readonly',
|
||||
'https://www.googleapis.com/auth/classroom.coursework.me.readonly',
|
||||
],
|
||||
})
|
||||
|
||||
setUser(GoogleSignin.getCurrentUser())
|
||||
}, [])
|
||||
|
||||
const signIn = async () => {
|
||||
const signIn = useCallback(async () => {
|
||||
try {
|
||||
await GoogleSignin.hasPlayServices()
|
||||
const userInfo = await GoogleSignin.signIn()
|
||||
@@ -40,19 +42,24 @@ export function AuthProvider({ children }: AuthProviderProps) {
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
const signOut = async () => {
|
||||
const signOut = useCallback(async () => {
|
||||
try {
|
||||
await GoogleSignin.signOut()
|
||||
setUser(null)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
const authContextValue = useMemo(
|
||||
() => ({ user, signIn, signOut }),
|
||||
[user, signIn, signOut],
|
||||
)
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{ user, signIn, signOut }}>
|
||||
<AuthContext.Provider value={authContextValue}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
)
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"]
|
||||
"include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts", "app/(app)/plushtml.old"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user