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 {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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|||||||
Reference in New Issue
Block a user