WIP Switch to react-navigation 2
This commit is contained in:
+169
-125
@@ -5,56 +5,120 @@ import {createNativeStackNavigator} from '@react-navigation/native-stack'
|
||||
import {createDrawerNavigator} from '@react-navigation/drawer'
|
||||
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs'
|
||||
|
||||
import {
|
||||
getStateFromPath,
|
||||
NavigationState,
|
||||
PartialState,
|
||||
} from '@react-navigation/native'
|
||||
import {NavigationState, PartialState} from '@react-navigation/native'
|
||||
|
||||
type StackNavigatorParamlist = {
|
||||
FeedList: undefined
|
||||
Details: {
|
||||
id: number
|
||||
name: string
|
||||
handle: string
|
||||
date: string
|
||||
content: string
|
||||
image: string
|
||||
avatar: string
|
||||
comments: number
|
||||
retweets: number
|
||||
hearts: number
|
||||
}
|
||||
type CommonNavigatorParamlist = {
|
||||
Settings: undefined
|
||||
Profile: {name: string}
|
||||
ProfileFollowers: {name: string}
|
||||
ProfileFollows: {name: string}
|
||||
PostThread: {name: string; rkey: string}
|
||||
PostUpvotedBy: {name: string; rkey: string}
|
||||
PostDownvotedBy: {name: string; rkey: string}
|
||||
PostRepostedBy: {name: string; rkey: string}
|
||||
Debug: undefined
|
||||
Log: undefined
|
||||
}
|
||||
type HomeStackNavigatorParamlist = CommonNavigatorParamlist & {
|
||||
Home: undefined
|
||||
}
|
||||
type NotificationsStackNavigatorParamlist = CommonNavigatorParamlist & {
|
||||
Notifications: undefined
|
||||
}
|
||||
type SearchStackNavigatorParamlist = CommonNavigatorParamlist & {
|
||||
Search: undefined
|
||||
}
|
||||
type State = NavigationState | Omit<PartialState<NavigationState>, 'stale'>
|
||||
|
||||
const PATHS = {
|
||||
Home: '/',
|
||||
Notifications: '/notifications',
|
||||
Search: '/search',
|
||||
Details: '/details',
|
||||
Profile: '/profile',
|
||||
const HomeDrawer = createDrawerNavigator()
|
||||
const HomeStack = createNativeStackNavigator<HomeStackNavigatorParamlist>()
|
||||
const SearchDrawer = createDrawerNavigator()
|
||||
const SearchStack =
|
||||
createNativeStackNavigator<NotificationsStackNavigatorParamlist>()
|
||||
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 = {
|
||||
prefixes: ['bsky://', 'https://bsky.app'],
|
||||
|
||||
getPathFromState(state: State, options?: any) {
|
||||
// TODO add parameterization
|
||||
// find the current node in the navigation tree
|
||||
let node = state.routes[state.index]
|
||||
while (node.state?.routes && typeof node.state?.index === 'number') {
|
||||
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) {
|
||||
// TODO add parameterization
|
||||
// match the route
|
||||
let match = 'Home' // TODO should be not found
|
||||
for (const [name, matcher] of Object.entries(PATHS)) {
|
||||
if (path === matcher) {
|
||||
let params: Record<string, string> = {}
|
||||
for (const [name, matcher] of Object.entries(ROUTES)) {
|
||||
const res = matcher.match(path)
|
||||
if (res) {
|
||||
match = name
|
||||
params = res.params
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// build the state object
|
||||
// TODO params
|
||||
let container = 'HomeStack'
|
||||
if (match === 'Notifications') {
|
||||
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}) {
|
||||
return (
|
||||
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
|
||||
<Text>Home Screen</Text>
|
||||
<Button
|
||||
title="Go to Details"
|
||||
onPress={() => navigation.push('Details')}
|
||||
title="Go to Post"
|
||||
onPress={() => navigation.push('PostThread', {name: 'bob', rkey: 123})}
|
||||
/>
|
||||
<Button
|
||||
title="Go to profile"
|
||||
@@ -101,8 +159,8 @@ function NotificationsScreen({navigation}) {
|
||||
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
|
||||
<Text>Notifications Screen</Text>
|
||||
<Button
|
||||
title="Go to Details"
|
||||
onPress={() => navigation.push('Details')}
|
||||
title="Go to Post"
|
||||
onPress={() => navigation.push('PostThread', {name: 'bob', rkey: 123})}
|
||||
/>
|
||||
<Button
|
||||
title="Go to profile"
|
||||
@@ -117,8 +175,8 @@ function SearchScreen({navigation}) {
|
||||
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
|
||||
<Text>Search Screen</Text>
|
||||
<Button
|
||||
title="Go to Details"
|
||||
onPress={() => navigation.push('Details')}
|
||||
title="Go to Post"
|
||||
onPress={() => navigation.push('PostThread', {name: 'bob', rkey: 123})}
|
||||
/>
|
||||
<Button
|
||||
title="Go to profile"
|
||||
@@ -128,41 +186,35 @@ function SearchScreen({navigation}) {
|
||||
)
|
||||
}
|
||||
|
||||
function DetailsScreen({navigation}) {
|
||||
return (
|
||||
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
|
||||
<Text>Details Screen</Text>
|
||||
<Button
|
||||
title="Go to Details... again"
|
||||
onPress={() => navigation.push('Details')}
|
||||
/>
|
||||
<Button title="Go to Home" onPress={() => navigation.navigate('Home')} />
|
||||
<Button title="Go back" onPress={() => navigation.goBack()} />
|
||||
<Button
|
||||
title="Go back to first screen in stack"
|
||||
onPress={() => navigation.popToTop()}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
function debugMkScreen(name: string) {
|
||||
return function ({navigation}) {
|
||||
return (
|
||||
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
|
||||
<Text>{name} Screen</Text>
|
||||
<Button
|
||||
title="Go to Home"
|
||||
onPress={() => navigation.navigate('Home')}
|
||||
/>
|
||||
<Button title="Go back" onPress={() => navigation.goBack()} />
|
||||
<Button
|
||||
title="Go back to first screen in stack"
|
||||
onPress={() => navigation.popToTop()}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function ProfileScreen({navigation}) {
|
||||
return (
|
||||
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
|
||||
<Text>Profile Screen</Text>
|
||||
<Button
|
||||
title="Go to Details... again"
|
||||
onPress={() => navigation.push('Details')}
|
||||
/>
|
||||
<Button title="Go to Home" onPress={() => navigation.navigate('Home')} />
|
||||
<Button title="Go back" onPress={() => navigation.goBack()} />
|
||||
<Button
|
||||
title="Go back to first screen in stack"
|
||||
onPress={() => navigation.popToTop()}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
const SettingsScreen = debugMkScreen('Settings')
|
||||
const ProfileScreen = debugMkScreen('Profile')
|
||||
const ProfileFollowersScreen = debugMkScreen('ProfileFollowers')
|
||||
const ProfileFollowsScreen = debugMkScreen('ProfileFollows')
|
||||
const PostThreadScreen = debugMkScreen('PostThread')
|
||||
const PostUpvotedByScreen = debugMkScreen('PostUpvotedBy')
|
||||
const PostDownvotedByScreen = debugMkScreen('PostDownvotedBy')
|
||||
const PostRepostedByScreen = debugMkScreen('PostRepostedBy')
|
||||
const DebugScreen = debugMkScreen('Debug')
|
||||
const LogScreen = debugMkScreen('Log')
|
||||
|
||||
function DrawerContent() {
|
||||
return (
|
||||
@@ -175,19 +227,30 @@ function DrawerContent() {
|
||||
function commonScreens(Stack: ReturnType<typeof createNativeStackNavigator>) {
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen name="Details" component={DetailsScreen} />
|
||||
<Stack.Screen name="Settings" component={SettingsScreen} />
|
||||
<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 (
|
||||
<Drawer.Navigator
|
||||
<HomeDrawer.Navigator
|
||||
drawerContent={DrawerContent}
|
||||
screenOptions={{swipeEdgeWidth: 300}}>
|
||||
<Drawer.Screen name="HomeInner" component={HomeScreen} />
|
||||
</Drawer.Navigator>
|
||||
<HomeDrawer.Screen name="HomeInner" component={HomeScreen} />
|
||||
</HomeDrawer.Navigator>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -195,30 +258,53 @@ function HomeStackNavigator() {
|
||||
return (
|
||||
<HomeStack.Navigator
|
||||
screenOptions={{gestureEnabled: true, fullScreenGestureEnabled: true}}>
|
||||
<HomeStack.Screen name="Home" component={HomeDrawer} />
|
||||
<HomeStack.Screen name="Home" component={HomeDrawerNavigator} />
|
||||
{commonScreens(HomeStack)}
|
||||
</HomeStack.Navigator>
|
||||
)
|
||||
}
|
||||
|
||||
function NotificationsDrawerNavigator() {
|
||||
return (
|
||||
<NotificationsDrawer.Navigator
|
||||
drawerContent={DrawerContent}
|
||||
screenOptions={{swipeEdgeWidth: 300}}>
|
||||
<NotificationsDrawer.Screen
|
||||
name="NotificationsInner"
|
||||
component={NotificationsScreen}
|
||||
/>
|
||||
</NotificationsDrawer.Navigator>
|
||||
)
|
||||
}
|
||||
|
||||
function NotificationsStackNavigator() {
|
||||
return (
|
||||
<NotificationsStack.Navigator
|
||||
screenOptions={{gestureEnabled: true, fullScreenGestureEnabled: true}}>
|
||||
<NotificationsStack.Screen
|
||||
name="Notifications"
|
||||
component={NotificationsScreen}
|
||||
component={NotificationsDrawerNavigator}
|
||||
/>
|
||||
{commonScreens(NotificationsStack)}
|
||||
</NotificationsStack.Navigator>
|
||||
)
|
||||
}
|
||||
|
||||
function SearchDrawerNavigator() {
|
||||
return (
|
||||
<SearchDrawer.Navigator
|
||||
drawerContent={DrawerContent}
|
||||
screenOptions={{swipeEdgeWidth: 300}}>
|
||||
<SearchDrawer.Screen name="SearchInner" component={SearchScreen} />
|
||||
</SearchDrawer.Navigator>
|
||||
)
|
||||
}
|
||||
|
||||
function SearchStackNavigator() {
|
||||
return (
|
||||
<SearchStack.Navigator
|
||||
screenOptions={{gestureEnabled: true, fullScreenGestureEnabled: true}}>
|
||||
<SearchStack.Screen name="Search" component={SearchScreen} />
|
||||
<SearchStack.Screen name="Search" component={SearchDrawerNavigator} />
|
||||
{commonScreens(SearchStack)}
|
||||
</SearchStack.Navigator>
|
||||
)
|
||||
@@ -246,45 +332,3 @@ export function Screens() {
|
||||
</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>
|
||||
)
|
||||
}
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user