Skip to content

Commit

Permalink
feat: react native example
Browse files Browse the repository at this point in the history
  • Loading branch information
Benehiko committed Sep 11, 2023
1 parent 56cc94f commit 49ae3d5
Show file tree
Hide file tree
Showing 28 changed files with 24,814 additions and 14,806 deletions.
2 changes: 1 addition & 1 deletion examples/preact-spa/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const Dashboard = () => {
<>
<Typography size={"headline37"}>Welcome to the dashboard!</Typography>
<Typography size={"headline21"}>
{session?.identity.traits.firstName} you can logout here:{" "}
{session?.identity?.traits.firstName} you can logout here:{" "}
<a href={logoutUrl}>Logout</a> or go to your settings page here:{" "}
<a href="/settings">Settings</a>
</Typography>
Expand Down
35 changes: 35 additions & 0 deletions examples/react-native-app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files

# dependencies
node_modules/

# Expo
.expo/
dist/
web-build/

# Native
*.orig.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision

# Metro
.metro-health-check*

# debug
npm-debug.*
yarn-debug.*
yarn-error.*

# macOS
.DS_Store
*.pem

# local env files
.env*.local

# typescript
*.tsbuildinfo
2 changes: 2 additions & 0 deletions examples/react-native-app/app.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/// <reference types="nativewind/types" />

30 changes: 30 additions & 0 deletions examples/react-native-app/app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"expo": {
"name": "react-native-app",
"slug": "react-native-app",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"userInterfaceStyle": "light",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"assetBundlePatterns": [
"**/*"
],
"ios": {
"supportsTablet": true
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"
}
},
"web": {
"favicon": "./assets/favicon.png"
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/react-native-app/assets/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/react-native-app/assets/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/react-native-app/assets/splash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions examples/react-native-app/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: [
"nativewind/babel",
'@babel/plugin-proposal-export-namespace-from',
'react-native-reanimated/plugin'
],
};
};
7 changes: 7 additions & 0 deletions examples/react-native-app/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import "react-native-gesture-handler";
// Polyfill for URLSearchParams
import "react-native-url-polyfill/auto";
import { registerRootComponent } from 'expo';
import App from './src/App';

registerRootComponent(App);
47 changes: 47 additions & 0 deletions examples/react-native-app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"name": "react-native-app",
"version": "1.0.0",
"scripts": {
"start": "npx expo start",
"android": "npx expo start --android",
"ios": "npx expo start --ios",
"web": "npx expo start --web"
},
"dependencies": {
"@babel/plugin-proposal-export-namespace-from": "7.18.9",
"@expo/webpack-config": "19.0.0",
"@ory/client": "*",
"@ory/elements": "*",
"@react-native-async-storage/async-storage": "1.18.2",
"@react-native-masked-view/masked-view": "0.2.9",
"@react-navigation/native": "6.1.7",
"@react-navigation/stack": "6.3.17",
"axios": "1.5.0",
"expo": "49.0.9",
"expo-asset": "8.10.1",
"expo-random": "13.2.0",
"expo-secure-store": "12.3.1",
"expo-standard-web-crypto": "1.6.0",
"expo-status-bar": "1.6.0",
"expo-web-browser": "12.3.2",
"nativewind": "2.0.11",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-native": "0.72.4",
"react-native-gesture-handler": "2.12.0",
"react-native-reanimated": "3.3.0",
"react-native-safe-area-context": "4.6.3",
"react-native-screens": "3.22.0",
"react-native-url-polyfill": "2.0.0",
"react-native-web": "0.19.8"
},
"devDependencies": {
"@babel/core": "7.20.12",
"@types/react": "18.2.21",
"@types/react-native": "0.72.2",
"postcss-loader": "4.2.0",
"tailwindcss": "3.3.3",
"typescript": "5.1.3"
},
"private": true
}
107 changes: 107 additions & 0 deletions examples/react-native-app/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { NavigationContainer } from '@react-navigation/native';
import { StatusBar } from 'expo-status-bar';
import { KeyboardAvoidingView, Platform, View } from 'react-native';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { useLoadedAssets } from "./hooks/useLoadAssets";
import { useContext } from 'react';
import { AuthContext } from './AuthProvider';
import Home from './Home';
import Login from './Login';
import { createStackNavigator } from '@react-navigation/stack';
import { IntlProvider, ThemeProvider } from '@ory/elements';

// Ory Elements
// optional global css reset
// import "@ory/elements/assets/normalize.css"
// optional fontawesome icons

// import "@ory/elements/assets/fa-brands.min.css"
// import "@ory/elements/assets/fa-solid.min.css"
// import "@ory/elements/assets/fontawesome.min.css"
//
// // optional fonts
// import "@ory/elements/assets/inter-font.css"
// import "@ory/elements/assets/jetbrains-mono-font.css"
//
// required styles for Ory Elements
import "@ory/elements/style.css"

export type RootStackParamList = {
Home: undefined
Login: {
refresh?: boolean
aal?: "aal2"
}
Registration: undefined
Settings: {
flowId?: string
}
Callback: {
code?: string
}
Verification: {
flowId?: string
}
Recovery: undefined
}

const Stack = createStackNavigator<RootStackParamList>();

const linking = {
// This is only used for e2e testing.
prefixes: ["http://127.0.0.1:19006/"],
}

export default function App() {
const isLoadingComplete = useLoadedAssets();
const { isAuthenticated } = useContext(AuthContext)

if (!isLoadingComplete) {
return null;
} else {
return (
<ThemeProvider>
<IntlProvider>
<View className="container mx-auto h-12 justify-center bg-slate-300 items-center">

<KeyboardAvoidingView
// style={{ flex: 1 }}
behavior={Platform.OS == "ios" ? "padding" : "height"}
>
<SafeAreaProvider>

<NavigationContainer linking={linking}>

<View className="flex flex-1 items-center justify-center">
<Stack.Navigator
screenOptions={{
headerShown: isAuthenticated,
}}
initialRouteName='Home'
>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Login" component={Login} initialParams={{}} />
{/* <Stack.Screen */}
{/* name="Settings" */}
{/* component={Settings} */}
{/* options={options} */}
{/* /> */}
{/* <Stack.Screen name="Registration" component={Registration} /> */}
{/* <Stack.Screen name="Login" component={Login} initialParams={{}} /> */}
{/* <Stack.Screen name="Verification" component={Verification} /> */}
{/* <Stack.Screen name="Callback" component={Callback} /> */}
{/* <Stack.Screen name="Recovery" component={Recovery} /> */}
</Stack.Navigator>
<StatusBar />
</View>
</NavigationContainer>

</SafeAreaProvider>
</KeyboardAvoidingView>
</View>
</IntlProvider>
</ThemeProvider >

);
}
}
114 changes: 114 additions & 0 deletions examples/react-native-app/src/AuthProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { Session } from "@ory/client"
import {
createContext,
ReactNode,
useContext,
useEffect,
useState,
} from "react"
import {
getAuthenticatedSession,
killAuthenticatedSession,
SessionContext,
setAuthenticatedSession,
} from "./helpers/auth"
import { ProjectContext } from "./ProjectProvider"
import { isAxiosError } from "axios"

interface Context {
session?: Session
sessionToken?: string
setSession: (session: SessionContext) => void
syncSession: () => Promise<void>
didFetch: boolean
isAuthenticated: boolean
}

export const AuthContext = createContext<Context>({
setSession: () => Promise.resolve(),
syncSession: () => Promise.resolve(),
didFetch: false,
isAuthenticated: false,
})

interface AuthContextProps {
children: ReactNode
}

export default function AuthContextProvider({ children }: AuthContextProps) {
const { sdk } = useContext(ProjectContext)
const [sessionContext, setSessionContext] = useState<
SessionContext | undefined
>(undefined)

// Fetches the authentication session.
useEffect(() => {
getAuthenticatedSession().then(syncSession)
}, [])

const syncSession = async (auth: { session_token?: string } | null) => {
if (!auth?.session_token) {
return setAuth(null)
}

try {
const { data: session } = await sdk
// whoami() returns the session belonging to the session_token:
.toSession({ xSessionToken: auth.session_token })

// This means that the session is still valid! The user is logged in.
//
// Here you could print the user's email using e.g.:
//
// console.log(session.identity.traits.email)
setSessionContext({ session, session_token: auth.session_token })
} catch (err: unknown) {
if (isAxiosError(err) && err.response?.status === 401) {
// The user is no longer logged in (hence 401)
// console.log('Session is not authenticated:', err)
} else {
// A network or some other error occurred
console.error(err)
}

// Remove the session / log the user out.
setSessionContext(null)
}
}

const setAuth = (session: SessionContext) => {
if (!session) {
return killAuthenticatedSession().then(() => setSessionContext(session))
}

setAuthenticatedSession(session).then(() => syncSession(session))
}

if (sessionContext === undefined) {
return null
}

return (
<AuthContext.Provider
value={{
// The session information
session: sessionContext?.session,
sessionToken: sessionContext?.session_token,

// Is true when the user has a session
isAuthenticated: Boolean(sessionContext?.session_token),

// Fetches the session from the server
syncSession: () => getAuthenticatedSession().then(syncSession),

// Allows to override the session
setSession: setAuth,

// Is true if we have fetched the session.
didFetch: true,
}}
>
{children}
</AuthContext.Provider>
)
}
29 changes: 29 additions & 0 deletions examples/react-native-app/src/Callback.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { StackScreenProps } from "@react-navigation/stack"
import { RootStackParamList } from "./App"
import { useEffect } from "react"
import * as WebBrowser from "expo-web-browser"

type Props = StackScreenProps<RootStackParamList, "Callback">

const Callback = (props: Props) => {
useEffect(() => {
WebBrowser.maybeCompleteAuthSession({
skipRedirectCheck: false,
})
}, [])
return (
<div>
{props.route.params?.code ? (
<>
Hello Callback! Your code is <code>{props.route.params?.code}</code>
</>
) : (
<>
Missing query parameter <code>?code=...</code>
</>
)}
</div>
)
}

export default Callback
Loading

0 comments on commit 49ae3d5

Please sign in to comment.