WIP Switch to react-navigation 2

This commit is contained in:
Paul Frazee
2023-03-09 14:54:49 -06:00
parent 34050e05be
commit bf951421ea
+169 -125
View File
@@ -5,56 +5,120 @@ import {createNativeStackNavigator} from '@react-navigation/native-stack'
import {createDrawerNavigator} from '@react-navigation/drawer' import {createDrawerNavigator} from '@react-navigation/drawer'
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs' import {createBottomTabNavigator} from '@react-navigation/bottom-tabs'
import { import {NavigationState, PartialState} from '@react-navigation/native'
getStateFromPath,
NavigationState,
PartialState,
} from '@react-navigation/native'
type StackNavigatorParamlist = { type CommonNavigatorParamlist = {
FeedList: undefined Settings: undefined
Details: { Profile: {name: string}
id: number ProfileFollowers: {name: string}
name: string ProfileFollows: {name: string}
handle: string PostThread: {name: string; rkey: string}
date: string PostUpvotedBy: {name: string; rkey: string}
content: string PostDownvotedBy: {name: string; rkey: string}
image: string PostRepostedBy: {name: string; rkey: string}
avatar: string Debug: undefined
comments: number Log: undefined
retweets: number }
hearts: number type HomeStackNavigatorParamlist = CommonNavigatorParamlist & {
} Home: undefined
}
type NotificationsStackNavigatorParamlist = CommonNavigatorParamlist & {
Notifications: undefined
}
type SearchStackNavigatorParamlist = CommonNavigatorParamlist & {
Search: undefined
} }
type State = NavigationState | Omit<PartialState<NavigationState>, 'stale'> type State = NavigationState | Omit<PartialState<NavigationState>, 'stale'>
const PATHS = { const HomeDrawer = createDrawerNavigator()
Home: '/', const HomeStack = createNativeStackNavigator<HomeStackNavigatorParamlist>()
Notifications: '/notifications', const SearchDrawer = createDrawerNavigator()
Search: '/search', const SearchStack =
Details: '/details', createNativeStackNavigator<NotificationsStackNavigatorParamlist>()
Profile: '/profile', const NotificationsDrawer = createDrawerNavigator()
const NotificationsStack =
createNativeStackNavigator<SearchStackNavigatorParamlist>()
const Tab = createBottomTabNavigator()
type RouteParams = Record<string, string>
type MatchResult = {params: RouteParams}
type Route = {
match: (path: string) => MatchResult | undefined
build: (params: RouteParams) => string
}
function r(pattern: string): Route {
let matcherReInternal = pattern.replace(
/:([\w]+)/g,
(_m, name) => `(?<${name}>[^/]+)`,
)
const matcherRe = new RegExp(`^${matcherReInternal}([?]|$)`, 'i')
return {
match(path: string) {
const res = matcherRe.exec(path)
if (res) {
return {params: res.groups}
}
return undefined
},
build(params: Record<string, string>) {
return pattern.replace(
/:([\w]+)/g,
(_m, name) => params[name] || 'undefined',
)
},
}
}
const ROUTES: Record<string, Route> = {
Home: r('/'),
Search: r('/search'),
Notifications: r('/notifications'),
Settings: r('/settings'),
Profile: r('/profile/:name'),
ProfileFollowers: r('/profile/:name/followers'),
ProfileFollows: r('/profile/:name/follows'),
PostThread: r('/profile/:name/post/:rkey'),
PostUpvotedBy: r('/profile/:name/post/:rkey/upvoted-by'),
PostDownvotedBy: r('/profile/:name/post/:rkey/downvoted-by'),
PostRepostedBy: r('/profile/:name/post/:rkey/reposted-by'),
Debug: r('/sys/debug'),
Log: r('/sys/log'),
} }
const LINKING = { const LINKING = {
prefixes: ['bsky://', 'https://bsky.app'], prefixes: ['bsky://', 'https://bsky.app'],
getPathFromState(state: State, options?: any) { getPathFromState(state: State, options?: any) {
// TODO add parameterization // find the current node in the navigation tree
let node = state.routes[state.index] let node = state.routes[state.index]
while (node.state?.routes && typeof node.state?.index === 'number') { while (node.state?.routes && typeof node.state?.index === 'number') {
node = node.state?.routes[node.state?.index] node = node.state?.routes[node.state?.index]
} }
return PATHS[node.name] || '/'
// build the path
const route = ROUTES[node.name]
if (typeof route === 'undefined') {
return '/' // default to home
}
return route.build({
/* TODO */
})
}, },
getStateFromPath(path: string, options?: any) { getStateFromPath(path: string, options?: any) {
// TODO add parameterization // match the route
let match = 'Home' // TODO should be not found let match = 'Home' // TODO should be not found
for (const [name, matcher] of Object.entries(PATHS)) { let params: Record<string, string> = {}
if (path === matcher) { for (const [name, matcher] of Object.entries(ROUTES)) {
const res = matcher.match(path)
if (res) {
match = name match = name
params = res.params
break break
} }
} }
// build the state object
// TODO params
let container = 'HomeStack' let container = 'HomeStack'
if (match === 'Notifications') { if (match === 'Notifications') {
container = 'NotificationsStack' container = 'NotificationsStack'
@@ -74,19 +138,13 @@ const LINKING = {
}, },
} }
const Drawer = createDrawerNavigator()
const HomeStack = createNativeStackNavigator<StackNavigatorParamlist>()
const NotificationsStack = createNativeStackNavigator<StackNavigatorParamlist>()
const SearchStack = createNativeStackNavigator<StackNavigatorParamlist>()
const Tab = createBottomTabNavigator()
function HomeScreen({navigation}) { function HomeScreen({navigation}) {
return ( return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}> <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Text>Home Screen</Text> <Text>Home Screen</Text>
<Button <Button
title="Go to Details" title="Go to Post"
onPress={() => navigation.push('Details')} onPress={() => navigation.push('PostThread', {name: 'bob', rkey: 123})}
/> />
<Button <Button
title="Go to profile" title="Go to profile"
@@ -101,8 +159,8 @@ function NotificationsScreen({navigation}) {
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}> <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Text>Notifications Screen</Text> <Text>Notifications Screen</Text>
<Button <Button
title="Go to Details" title="Go to Post"
onPress={() => navigation.push('Details')} onPress={() => navigation.push('PostThread', {name: 'bob', rkey: 123})}
/> />
<Button <Button
title="Go to profile" title="Go to profile"
@@ -117,8 +175,8 @@ function SearchScreen({navigation}) {
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}> <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Text>Search Screen</Text> <Text>Search Screen</Text>
<Button <Button
title="Go to Details" title="Go to Post"
onPress={() => navigation.push('Details')} onPress={() => navigation.push('PostThread', {name: 'bob', rkey: 123})}
/> />
<Button <Button
title="Go to profile" title="Go to profile"
@@ -128,41 +186,35 @@ function SearchScreen({navigation}) {
) )
} }
function DetailsScreen({navigation}) { function debugMkScreen(name: string) {
return ( return function ({navigation}) {
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}> return (
<Text>Details Screen</Text> <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Button <Text>{name} Screen</Text>
title="Go to Details... again" <Button
onPress={() => navigation.push('Details')} title="Go to Home"
/> onPress={() => navigation.navigate('Home')}
<Button title="Go to Home" onPress={() => navigation.navigate('Home')} /> />
<Button title="Go back" onPress={() => navigation.goBack()} /> <Button title="Go back" onPress={() => navigation.goBack()} />
<Button <Button
title="Go back to first screen in stack" title="Go back to first screen in stack"
onPress={() => navigation.popToTop()} onPress={() => navigation.popToTop()}
/> />
</View> </View>
) )
}
} }
function ProfileScreen({navigation}) { const SettingsScreen = debugMkScreen('Settings')
return ( const ProfileScreen = debugMkScreen('Profile')
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}> const ProfileFollowersScreen = debugMkScreen('ProfileFollowers')
<Text>Profile Screen</Text> const ProfileFollowsScreen = debugMkScreen('ProfileFollows')
<Button const PostThreadScreen = debugMkScreen('PostThread')
title="Go to Details... again" const PostUpvotedByScreen = debugMkScreen('PostUpvotedBy')
onPress={() => navigation.push('Details')} const PostDownvotedByScreen = debugMkScreen('PostDownvotedBy')
/> const PostRepostedByScreen = debugMkScreen('PostRepostedBy')
<Button title="Go to Home" onPress={() => navigation.navigate('Home')} /> const DebugScreen = debugMkScreen('Debug')
<Button title="Go back" onPress={() => navigation.goBack()} /> const LogScreen = debugMkScreen('Log')
<Button
title="Go back to first screen in stack"
onPress={() => navigation.popToTop()}
/>
</View>
)
}
function DrawerContent() { function DrawerContent() {
return ( return (
@@ -175,19 +227,30 @@ function DrawerContent() {
function commonScreens(Stack: ReturnType<typeof createNativeStackNavigator>) { function commonScreens(Stack: ReturnType<typeof createNativeStackNavigator>) {
return ( return (
<> <>
<Stack.Screen name="Details" component={DetailsScreen} /> <Stack.Screen name="Settings" component={SettingsScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} /> <Stack.Screen name="Profile" component={ProfileScreen} />
<Stack.Screen
name="ProfileFollowers"
component={ProfileFollowersScreen}
/>
<Stack.Screen name="ProfileFollows" component={ProfileFollowsScreen} />
<Stack.Screen name="PostThread" component={PostThreadScreen} />
<Stack.Screen name="PostUpvotedBy" component={PostUpvotedByScreen} />
<Stack.Screen name="PostDownvotedBy" component={PostDownvotedByScreen} />
<Stack.Screen name="PostRepostedBy" component={PostRepostedByScreen} />
<Stack.Screen name="Debug" component={DebugScreen} />
<Stack.Screen name="Log" component={LogScreen} />
</> </>
) )
} }
function HomeDrawer() { function HomeDrawerNavigator() {
return ( return (
<Drawer.Navigator <HomeDrawer.Navigator
drawerContent={DrawerContent} drawerContent={DrawerContent}
screenOptions={{swipeEdgeWidth: 300}}> screenOptions={{swipeEdgeWidth: 300}}>
<Drawer.Screen name="HomeInner" component={HomeScreen} /> <HomeDrawer.Screen name="HomeInner" component={HomeScreen} />
</Drawer.Navigator> </HomeDrawer.Navigator>
) )
} }
@@ -195,30 +258,53 @@ function HomeStackNavigator() {
return ( return (
<HomeStack.Navigator <HomeStack.Navigator
screenOptions={{gestureEnabled: true, fullScreenGestureEnabled: true}}> screenOptions={{gestureEnabled: true, fullScreenGestureEnabled: true}}>
<HomeStack.Screen name="Home" component={HomeDrawer} /> <HomeStack.Screen name="Home" component={HomeDrawerNavigator} />
{commonScreens(HomeStack)} {commonScreens(HomeStack)}
</HomeStack.Navigator> </HomeStack.Navigator>
) )
} }
function NotificationsDrawerNavigator() {
return (
<NotificationsDrawer.Navigator
drawerContent={DrawerContent}
screenOptions={{swipeEdgeWidth: 300}}>
<NotificationsDrawer.Screen
name="NotificationsInner"
component={NotificationsScreen}
/>
</NotificationsDrawer.Navigator>
)
}
function NotificationsStackNavigator() { function NotificationsStackNavigator() {
return ( return (
<NotificationsStack.Navigator <NotificationsStack.Navigator
screenOptions={{gestureEnabled: true, fullScreenGestureEnabled: true}}> screenOptions={{gestureEnabled: true, fullScreenGestureEnabled: true}}>
<NotificationsStack.Screen <NotificationsStack.Screen
name="Notifications" name="Notifications"
component={NotificationsScreen} component={NotificationsDrawerNavigator}
/> />
{commonScreens(NotificationsStack)} {commonScreens(NotificationsStack)}
</NotificationsStack.Navigator> </NotificationsStack.Navigator>
) )
} }
function SearchDrawerNavigator() {
return (
<SearchDrawer.Navigator
drawerContent={DrawerContent}
screenOptions={{swipeEdgeWidth: 300}}>
<SearchDrawer.Screen name="SearchInner" component={SearchScreen} />
</SearchDrawer.Navigator>
)
}
function SearchStackNavigator() { function SearchStackNavigator() {
return ( return (
<SearchStack.Navigator <SearchStack.Navigator
screenOptions={{gestureEnabled: true, fullScreenGestureEnabled: true}}> screenOptions={{gestureEnabled: true, fullScreenGestureEnabled: true}}>
<SearchStack.Screen name="Search" component={SearchScreen} /> <SearchStack.Screen name="Search" component={SearchDrawerNavigator} />
{commonScreens(SearchStack)} {commonScreens(SearchStack)}
</SearchStack.Navigator> </SearchStack.Navigator>
) )
@@ -246,45 +332,3 @@ export function Screens() {
</NavigationContainer> </NavigationContainer>
) )
} }
/*function TabsNavigator() {
return (
<React.Fragment>
<Tab.Navigator initialRouteName="Feed" backBehavior="initialRoute">
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Notifications" component={NotificationsScreen} />
<Tab.Screen name="Search" component={SearchScreen} />
</Tab.Navigator>
</React.Fragment>
)
}
function DrawerNavigator() {
return (
<Drawer.Navigator drawerContent={DrawerContent}>
<Drawer.Screen name="Root" component={TabsNavigator} />
</Drawer.Navigator>
)
}
function StackNavigator() {
return (
<Stack.Navigator initialRouteName="FeedList">
<Stack.Screen name="FeedList" component={DrawerNavigator} />
<Stack.Screen
name="Details"
component={DetailsScreen}
options={{headerTitle: 'Tweet'}}
/>
</Stack.Navigator>
)
}
export function Screens() {
return (
<NavigationContainer>
<StackNavigator />
</NavigationContainer>
)
}
*/