Add auth navigations

zio/stable
Paul Frazee 2022-06-09 17:13:29 -05:00
parent fc3b2952bb
commit 802222fe71
11 changed files with 129 additions and 25 deletions

View File

@ -24,8 +24,6 @@ Uses:
## TODOs ## TODOs
- Navigation
- Auth / Unauthed
- Web - Web
- Desktop vs mobile styling - Desktop vs mobile styling
- API - API

View File

@ -17,6 +17,7 @@
"@react-navigation/native-stack": "^6.6.2", "@react-navigation/native-stack": "^6.6.2",
"@react-navigation/stack": "^6.2.1", "@react-navigation/stack": "^6.2.1",
"mobx": "^6.6.0", "mobx": "^6.6.0",
"mobx-react-lite": "^3.4.0",
"mobx-state-tree": "^5.1.5", "mobx-state-tree": "^5.1.5",
"react": "17.0.2", "react": "17.0.2",
"react-dom": "17.0.2", "react-dom": "17.0.2",

View File

@ -8,12 +8,16 @@ import {
} from '@react-navigation/native' } from '@react-navigation/native'
import {createNativeStackNavigator} from '@react-navigation/native-stack' import {createNativeStackNavigator} from '@react-navigation/native-stack'
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs' import {createBottomTabNavigator} from '@react-navigation/bottom-tabs'
import {observer} from 'mobx-react-lite'
import type {RootStackParamList} from './types' import type {RootStackParamList} from './types'
import {useStores} from '../state'
import {Home} from '../screens/Home' import {Home} from '../screens/Home'
import {Search} from '../screens/Search' import {Search} from '../screens/Search'
import {Notifications} from '../screens/Notifications' import {Notifications} from '../screens/Notifications'
import {Menu} from '../screens/Menu' import {Menu} from '../screens/Menu'
import {Profile} from '../screens/Profile' import {Profile} from '../screens/Profile'
import {Login} from '../screens/Login'
import {Signup} from '../screens/Signup'
import {NotFound} from '../screens/NotFound' import {NotFound} from '../screens/NotFound'
const linking: LinkingOptions<RootStackParamList> = { const linking: LinkingOptions<RootStackParamList> = {
@ -30,6 +34,8 @@ const linking: LinkingOptions<RootStackParamList> = {
Menu: 'menu', Menu: 'menu',
}, },
}, },
Login: 'login',
Signup: 'signup',
Profile: 'profile/:name', Profile: 'profile/:name',
NotFound: '*', NotFound: '*',
}, },
@ -50,7 +56,8 @@ const tabBarScreenOptions = ({
}, },
}) })
const Primary = () => ( function Primary() {
return (
<PrimaryTab.Navigator <PrimaryTab.Navigator
screenOptions={tabBarScreenOptions} screenOptions={tabBarScreenOptions}
initialRouteName="Home"> initialRouteName="Home">
@ -59,14 +66,28 @@ const Primary = () => (
<PrimaryTab.Screen name="Notifications" component={Notifications} /> <PrimaryTab.Screen name="Notifications" component={Notifications} />
<PrimaryTab.Screen name="Menu" component={Menu} /> <PrimaryTab.Screen name="Menu" component={Menu} />
</PrimaryTab.Navigator> </PrimaryTab.Navigator>
) )
}
export const Root = () => ( export const Root = observer(() => {
const store = useStores()
return (
<NavigationContainer linking={linking} fallback={<Text>Loading...</Text>}> <NavigationContainer linking={linking} fallback={<Text>Loading...</Text>}>
<RootStack.Navigator initialRouteName="Primary"> <RootStack.Navigator
initialRouteName={store.session.isAuthed ? 'Primary' : 'Login'}>
{store.session.isAuthed ? (
<>
<RootStack.Screen name="Primary" component={Primary} /> <RootStack.Screen name="Primary" component={Primary} />
<RootStack.Screen name="Profile" component={Profile} /> <RootStack.Screen name="Profile" component={Profile} />
<RootStack.Screen name="NotFound" component={NotFound} /> <RootStack.Screen name="NotFound" component={NotFound} />
</>
) : (
<>
<RootStack.Screen name="Login" component={Login} />
<RootStack.Screen name="Signup" component={Signup} />
</>
)}
</RootStack.Navigator> </RootStack.Navigator>
</NavigationContainer> </NavigationContainer>
) )
})

View File

@ -6,6 +6,8 @@ import type {BottomTabScreenProps} from '@react-navigation/bottom-tabs'
export type RootStackParamList = { export type RootStackParamList = {
Primary: undefined Primary: undefined
Profile: {name: string} Profile: {name: string}
Login: undefined
Signup: undefined
NotFound: undefined NotFound: undefined
} }
export type RootStackScreenProps<T extends keyof RootStackParamList> = export type RootStackScreenProps<T extends keyof RootStackParamList> =

View File

@ -1,8 +1,10 @@
import React from 'react' import React from 'react'
import {Text, Button, View, SafeAreaView} from 'react-native' import {Text, Button, View, SafeAreaView} from 'react-native'
import type {PrimaryTabScreenProps} from '../routes/types' import type {PrimaryTabScreenProps} from '../routes/types'
import {useStores} from '../state'
export const Home = ({navigation}: PrimaryTabScreenProps<'Home'>) => { export function Home({navigation}: PrimaryTabScreenProps<'Home'>) {
const store = useStores()
return ( return (
<SafeAreaView style={{flex: 1}}> <SafeAreaView style={{flex: 1}}>
<View style={{flex: 1}}> <View style={{flex: 1}}>
@ -11,6 +13,7 @@ export const Home = ({navigation}: PrimaryTabScreenProps<'Home'>) => {
title="Go to Jane's profile" title="Go to Jane's profile"
onPress={() => navigation.navigate('Profile', {name: 'Jane'})} onPress={() => navigation.navigate('Profile', {name: 'Jane'})}
/> />
<Button title="Logout" onPress={() => store.session.setAuthed(false)} />
</View> </View>
</SafeAreaView> </SafeAreaView>
) )

View File

@ -0,0 +1,17 @@
import React from 'react'
import {Text, Button, View, SafeAreaView} from 'react-native'
import type {RootStackScreenProps} from '../routes/types'
import {useStores} from '../state'
export function Login({navigation}: RootStackScreenProps<'Login'>) {
const store = useStores()
return (
<SafeAreaView style={{flex: 1}}>
<View style={{flex: 1}}>
<Text>Welcome! Time to sign in</Text>
<Button title="Login" onPress={() => store.session.setAuthed(true)} />
<Button title="Sign Up" onPress={() => navigation.navigate('Signup')} />
</View>
</SafeAreaView>
)
}

View File

@ -0,0 +1,23 @@
import React from 'react'
import {Text, Button, View, SafeAreaView} from 'react-native'
import type {RootStackScreenProps} from '../routes/types'
import {useStores} from '../state'
export function Signup({navigation}: RootStackScreenProps<'Signup'>) {
const store = useStores()
return (
<SafeAreaView style={{flex: 1}}>
<View style={{flex: 1}}>
<Text>Let's create your account</Text>
<Button
title="Create new account"
onPress={() => store.session.setAuthed(true)}
/>
<Button
title="Log in to an existing account"
onPress={() => navigation.navigate('Login')}
/>
</View>
</SafeAreaView>
)
}

View File

@ -1,5 +1,9 @@
import {onSnapshot} from 'mobx-state-tree' import {onSnapshot} from 'mobx-state-tree'
import {RootStoreModel, RootStore} from './models/root-store' import {
RootStoreModel,
RootStore,
createDefaultRootStore,
} from './models/root-store'
import {Environment} from './env' import {Environment} from './env'
import * as storage from './storage' import * as storage from './storage'
@ -15,7 +19,7 @@ export async function setupState() {
rootStore = RootStoreModel.create(data, env) rootStore = RootStoreModel.create(data, env)
} catch (e) { } catch (e) {
console.error('Failed to load state from storage', e) console.error('Failed to load state from storage', e)
rootStore = RootStoreModel.create({}, env) rootStore = RootStoreModel.create(createDefaultRootStore(), env)
} }
// track changes & save to storage // track changes & save to storage

View File

@ -4,12 +4,21 @@
import {Instance, SnapshotOut, types} from 'mobx-state-tree' import {Instance, SnapshotOut, types} from 'mobx-state-tree'
import {createContext, useContext} from 'react' import {createContext, useContext} from 'react'
import {SessionModel, createDefaultSession} from './session'
export const RootStoreModel = types.model('RootStore').props({}) export const RootStoreModel = types.model('RootStore').props({
session: SessionModel,
})
export interface RootStore extends Instance<typeof RootStoreModel> {} export interface RootStore extends Instance<typeof RootStoreModel> {}
export interface RootStoreSnapshot extends SnapshotOut<typeof RootStoreModel> {} export interface RootStoreSnapshot extends SnapshotOut<typeof RootStoreModel> {}
export function createDefaultRootStore() {
return {
session: createDefaultSession(),
}
}
// react context & hook utilities // react context & hook utilities
const RootStoreContext = createContext<RootStore>({} as RootStore) const RootStoreContext = createContext<RootStore>({} as RootStore)
export const RootStoreProvider = RootStoreContext.Provider export const RootStoreProvider = RootStoreContext.Provider

View File

@ -0,0 +1,21 @@
import {Instance, SnapshotOut, types} from 'mobx-state-tree'
export const SessionModel = types
.model('Session')
.props({
isAuthed: types.boolean,
})
.actions(self => ({
setAuthed: (v: boolean) => {
self.isAuthed = v
},
}))
export interface Session extends Instance<typeof SessionModel> {}
export interface SessionSnapshot extends SnapshotOut<typeof SessionModel> {}
export function createDefaultSession() {
return {
isAuthed: false,
}
}

View File

@ -8564,6 +8564,11 @@ mkdirp@^0.5.1, mkdirp@~0.5.1:
dependencies: dependencies:
minimist "^1.2.6" minimist "^1.2.6"
mobx-react-lite@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-3.4.0.tgz#d59156a96889cdadad751e5e4dab95f28926dfff"
integrity sha512-bRuZp3C0itgLKHu/VNxi66DN/XVkQG7xtoBVWxpvC5FhAqbOCP21+nPhULjnzEqd7xBMybp6KwytdUpZKEgpIQ==
mobx-state-tree@^5.1.5: mobx-state-tree@^5.1.5:
version "5.1.5" version "5.1.5"
resolved "https://registry.yarnpkg.com/mobx-state-tree/-/mobx-state-tree-5.1.5.tgz#7344d61072705747abb98d23ad21302e38200105" resolved "https://registry.yarnpkg.com/mobx-state-tree/-/mobx-state-tree-5.1.5.tgz#7344d61072705747abb98d23ad21302e38200105"