Add auth navigations
parent
fc3b2952bb
commit
802222fe71
|
@ -24,8 +24,6 @@ Uses:
|
||||||
|
|
||||||
## TODOs
|
## TODOs
|
||||||
|
|
||||||
- Navigation
|
|
||||||
- Auth / Unauthed
|
|
||||||
- Web
|
- Web
|
||||||
- Desktop vs mobile styling
|
- Desktop vs mobile styling
|
||||||
- API
|
- API
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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,23 +56,38 @@ const tabBarScreenOptions = ({
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const Primary = () => (
|
function Primary() {
|
||||||
<PrimaryTab.Navigator
|
return (
|
||||||
screenOptions={tabBarScreenOptions}
|
<PrimaryTab.Navigator
|
||||||
initialRouteName="Home">
|
screenOptions={tabBarScreenOptions}
|
||||||
<PrimaryTab.Screen name="Home" component={Home} />
|
initialRouteName="Home">
|
||||||
<PrimaryTab.Screen name="Search" component={Search} />
|
<PrimaryTab.Screen name="Home" component={Home} />
|
||||||
<PrimaryTab.Screen name="Notifications" component={Notifications} />
|
<PrimaryTab.Screen name="Search" component={Search} />
|
||||||
<PrimaryTab.Screen name="Menu" component={Menu} />
|
<PrimaryTab.Screen name="Notifications" component={Notifications} />
|
||||||
</PrimaryTab.Navigator>
|
<PrimaryTab.Screen name="Menu" component={Menu} />
|
||||||
)
|
</PrimaryTab.Navigator>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export const Root = () => (
|
export const Root = observer(() => {
|
||||||
<NavigationContainer linking={linking} fallback={<Text>Loading...</Text>}>
|
const store = useStores()
|
||||||
<RootStack.Navigator initialRouteName="Primary">
|
return (
|
||||||
<RootStack.Screen name="Primary" component={Primary} />
|
<NavigationContainer linking={linking} fallback={<Text>Loading...</Text>}>
|
||||||
<RootStack.Screen name="Profile" component={Profile} />
|
<RootStack.Navigator
|
||||||
<RootStack.Screen name="NotFound" component={NotFound} />
|
initialRouteName={store.session.isAuthed ? 'Primary' : 'Login'}>
|
||||||
</RootStack.Navigator>
|
{store.session.isAuthed ? (
|
||||||
</NavigationContainer>
|
<>
|
||||||
)
|
<RootStack.Screen name="Primary" component={Primary} />
|
||||||
|
<RootStack.Screen name="Profile" component={Profile} />
|
||||||
|
<RootStack.Screen name="NotFound" component={NotFound} />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<RootStack.Screen name="Login" component={Login} />
|
||||||
|
<RootStack.Screen name="Signup" component={Signup} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</RootStack.Navigator>
|
||||||
|
</NavigationContainer>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
|
@ -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> =
|
||||||
|
|
|
@ -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>
|
||||||
)
|
)
|
||||||
|
|
|
@ -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>
|
||||||
|
)
|
||||||
|
}
|
|
@ -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>
|
||||||
|
)
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in New Issue