diff options
author | Paul Frazee <pfrazee@gmail.com> | 2022-06-09 21:34:43 -0500 |
---|---|---|
committer | Paul Frazee <pfrazee@gmail.com> | 2022-06-09 21:34:43 -0500 |
commit | 967f9fc474f2903dd2c12ef4f662ead1592ea26c (patch) | |
tree | af90380121af16ce2382d725fee3d50cd6332598 /src | |
parent | 802222fe7181303d710607129e1c74427f07c97c (diff) | |
download | voidsky-967f9fc474f2903dd2c12ef4f662ead1592ea26c.tar.zst |
Add desktop shell
Diffstat (limited to 'src')
-rw-r--r-- | src/platform/desktop-web/left-column.tsx | 57 | ||||
-rw-r--r-- | src/platform/desktop-web/right-column.tsx | 19 | ||||
-rw-r--r-- | src/platform/desktop-web/shell.tsx | 35 | ||||
-rw-r--r-- | src/platform/detection.ts | 11 | ||||
-rw-r--r-- | src/platform/shell.tsx | 12 | ||||
-rw-r--r-- | src/routes/index.tsx | 70 | ||||
-rw-r--r-- | src/routes/types.ts | 40 | ||||
-rw-r--r-- | src/screens/Home.tsx | 15 | ||||
-rw-r--r-- | src/screens/Login.tsx | 15 | ||||
-rw-r--r-- | src/screens/Menu.tsx | 19 | ||||
-rw-r--r-- | src/screens/NotFound.tsx | 32 | ||||
-rw-r--r-- | src/screens/Notifications.tsx | 25 | ||||
-rw-r--r-- | src/screens/Profile.tsx | 17 | ||||
-rw-r--r-- | src/screens/Search.tsx | 23 | ||||
-rw-r--r-- | src/screens/Signup.tsx | 15 |
15 files changed, 269 insertions, 136 deletions
diff --git a/src/platform/desktop-web/left-column.tsx b/src/platform/desktop-web/left-column.tsx new file mode 100644 index 000000000..082231ec9 --- /dev/null +++ b/src/platform/desktop-web/left-column.tsx @@ -0,0 +1,57 @@ +import React from 'react' +import {Pressable, View, StyleSheet} from 'react-native' +import {Link} from '@react-navigation/native' +import {useRoute} from '@react-navigation/native' + +export const NavItem: React.FC<{label: string; screen: string}> = ({ + label, + screen, +}) => { + const route = useRoute() + return ( + <View> + <Pressable + style={state => [ + // @ts-ignore it does exist! (react-native-web) -prf + state.hovered && styles.navItemHovered, + ]}> + <Link + style={[ + styles.navItemLink, + route.name === screen && styles.navItemLinkSelected, + ]} + to={{screen, params: {}}}> + {label} + </Link> + </Pressable> + </View> + ) +} + +export const DesktopLeftColumn: React.FC = () => { + return ( + <View style={styles.container}> + <NavItem screen="Home" label="Home" /> + <NavItem screen="Search" label="Search" /> + <NavItem screen="Notifications" label="Notifications" /> + </View> + ) +} + +const styles = StyleSheet.create({ + container: { + position: 'absolute', + left: 'calc(50vw - 500px)', + width: '200px', + height: '100%', + }, + navItemHovered: { + backgroundColor: 'gray', + }, + navItemLink: { + padding: '1rem', + }, + navItemLinkSelected: { + color: 'blue', + }, +}) diff --git a/src/platform/desktop-web/right-column.tsx b/src/platform/desktop-web/right-column.tsx new file mode 100644 index 000000000..5fe65cac8 --- /dev/null +++ b/src/platform/desktop-web/right-column.tsx @@ -0,0 +1,19 @@ +import React from 'react' +import {Text, View, StyleSheet} from 'react-native' + +export const DesktopRightColumn: React.FC = () => { + return ( + <View style={styles.container}> + <Text>Right Column</Text> + </View> + ) +} + +const styles = StyleSheet.create({ + container: { + position: 'absolute', + right: 'calc(50vw - 500px)', + width: '200px', + height: '100%', + }, +}) diff --git a/src/platform/desktop-web/shell.tsx b/src/platform/desktop-web/shell.tsx new file mode 100644 index 000000000..ef880306b --- /dev/null +++ b/src/platform/desktop-web/shell.tsx @@ -0,0 +1,35 @@ +import React from 'react' +import {observer} from 'mobx-react-lite' +import {View, StyleSheet} from 'react-native' +import {DesktopLeftColumn} from './left-column' +import {DesktopRightColumn} from './right-column' +import {useStores} from '../../state' + +export const DesktopWebShell: React.FC = observer(({children}) => { + const store = useStores() + return ( + <View style={styles.outerContainer}> + {store.session.isAuthed ? ( + <> + <DesktopLeftColumn /> + <View style={styles.innerContainer}>{children}</View> + <DesktopRightColumn /> + </> + ) : ( + <View style={styles.innerContainer}>{children}</View> + )} + </View> + ) +}) + +const styles = StyleSheet.create({ + outerContainer: { + height: '100%', + }, + innerContainer: { + marginLeft: 'auto', + marginRight: 'auto', + width: '600px', + height: '100%', + }, +}) diff --git a/src/platform/detection.ts b/src/platform/detection.ts new file mode 100644 index 000000000..5d2ffcb22 --- /dev/null +++ b/src/platform/detection.ts @@ -0,0 +1,11 @@ +import {Platform} from 'react-native' + +export const isIOS = Platform.OS === 'ios' +export const isAndroid = Platform.OS === 'android' +export const isNative = isIOS || isAndroid +export const isWeb = !isNative +export const isMobileWeb = + isWeb && + // @ts-ignore we know window exists -prf + global.window.matchMedia('only screen and (max-width: 1000px)')?.matches +export const isDesktopWeb = isWeb && !isMobileWeb diff --git a/src/platform/shell.tsx b/src/platform/shell.tsx new file mode 100644 index 000000000..ec8d51e1f --- /dev/null +++ b/src/platform/shell.tsx @@ -0,0 +1,12 @@ +import React from 'react' +import {SafeAreaView} from 'react-native' +import {isDesktopWeb} from './detection' +import {DesktopWebShell} from './desktop-web/shell' + +export const Shell: React.FC = ({children}) => { + return isDesktopWeb ? ( + <DesktopWebShell>{children}</DesktopWebShell> + ) : ( + <SafeAreaView>{children}</SafeAreaView> + ) +} diff --git a/src/routes/index.tsx b/src/routes/index.tsx index fa035e3c9..a8a92e54f 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -9,8 +9,9 @@ import { import {createNativeStackNavigator} from '@react-navigation/native-stack' import {createBottomTabNavigator} from '@react-navigation/bottom-tabs' import {observer} from 'mobx-react-lite' -import type {RootStackParamList} from './types' +import type {RootTabsParamList} from './types' import {useStores} from '../state' +import * as platform from '../platform/detection' import {Home} from '../screens/Home' import {Search} from '../screens/Search' import {Notifications} from '../screens/Notifications' @@ -20,74 +21,77 @@ import {Login} from '../screens/Login' import {Signup} from '../screens/Signup' import {NotFound} from '../screens/NotFound' -const linking: LinkingOptions<RootStackParamList> = { +const linking: LinkingOptions<RootTabsParamList> = { prefixes: [ 'http://localhost:3000', // local dev ], config: { screens: { - Primary: { - screens: { - Home: '', - Search: 'search', - Notifications: 'notifications', - Menu: 'menu', - }, - }, + Home: '', + Profile: 'profile/:name', + Search: 'search', + Notifications: 'notifications', + Menu: 'menu', Login: 'login', Signup: 'signup', - Profile: 'profile/:name', NotFound: '*', }, }, } -export const RootStack = createNativeStackNavigator() -export const PrimaryTab = createBottomTabNavigator() +export const RootTabs = createBottomTabNavigator() +export const PrimaryStack = createNativeStackNavigator() const tabBarScreenOptions = ({ route, }: { route: RouteProp<ParamListBase, string> }) => ({ + headerShown: false, tabBarIcon: (_state: {focused: boolean; color: string; size: number}) => { // TODO: icons return <Text>{route.name.at(0)}</Text> }, }) -function Primary() { - return ( - <PrimaryTab.Navigator - screenOptions={tabBarScreenOptions} - initialRouteName="Home"> - <PrimaryTab.Screen name="Home" component={Home} /> - <PrimaryTab.Screen name="Search" component={Search} /> - <PrimaryTab.Screen name="Notifications" component={Notifications} /> - <PrimaryTab.Screen name="Menu" component={Menu} /> - </PrimaryTab.Navigator> - ) -} +const HIDE_TAB = {tabBarButton: () => null} export const Root = observer(() => { const store = useStores() + + // hide the tabbar on desktop web + const tabBar = platform.isDesktopWeb ? () => null : undefined + return ( <NavigationContainer linking={linking} fallback={<Text>Loading...</Text>}> - <RootStack.Navigator - initialRouteName={store.session.isAuthed ? 'Primary' : 'Login'}> + <RootTabs.Navigator + initialRouteName={store.session.isAuthed ? 'Home' : 'Login'} + screenOptions={tabBarScreenOptions} + tabBar={tabBar}> {store.session.isAuthed ? ( <> - <RootStack.Screen name="Primary" component={Primary} /> - <RootStack.Screen name="Profile" component={Profile} /> - <RootStack.Screen name="NotFound" component={NotFound} /> + <RootTabs.Screen name="Home" component={Home} /> + <RootTabs.Screen name="Search" component={Search} /> + <RootTabs.Screen name="Notifications" component={Notifications} /> + <RootTabs.Screen name="Menu" component={Menu} /> + <RootTabs.Screen + name="Profile" + component={Profile} + options={HIDE_TAB} + /> </> ) : ( <> - <RootStack.Screen name="Login" component={Login} /> - <RootStack.Screen name="Signup" component={Signup} /> + <RootTabs.Screen name="Login" component={Login} /> + <RootTabs.Screen name="Signup" component={Signup} /> </> )} - </RootStack.Navigator> + <RootTabs.Screen + name="NotFound" + component={NotFound} + options={HIDE_TAB} + /> + </RootTabs.Navigator> </NavigationContainer> ) }) diff --git a/src/routes/types.ts b/src/routes/types.ts index 88148fd4d..d92594bbe 100644 --- a/src/routes/types.ts +++ b/src/routes/types.ts @@ -1,26 +1,36 @@ -import type {NavigatorScreenParams} from '@react-navigation/native' -import type {CompositeScreenProps} from '@react-navigation/native' import type {StackScreenProps} from '@react-navigation/stack' -import type {BottomTabScreenProps} from '@react-navigation/bottom-tabs' -export type RootStackParamList = { - Primary: undefined +export type RootTabsParamList = { + Home: undefined + Search: undefined + Notifications: undefined + Menu: undefined Profile: {name: string} Login: undefined Signup: undefined NotFound: undefined } -export type RootStackScreenProps<T extends keyof RootStackParamList> = - StackScreenProps<RootStackParamList, T> +export type RootTabsScreenProps<T extends keyof RootTabsParamList> = + StackScreenProps<RootTabsParamList, T> -export type PrimaryTabParamList = { - Home: NavigatorScreenParams<RootStackParamList> - Search: undefined - Notifications: undefined - Menu: undefined +/* +NOTE +this is leftover from a nested nav implementation +keeping it around for future reference +-prf + +import type {NavigatorScreenParams} from '@react-navigation/native' +import type {CompositeScreenProps} from '@react-navigation/native' +import type {BottomTabScreenProps} from '@react-navigation/bottom-tabs' + +Container: NavigatorScreenParams<PrimaryStacksParamList> +export type PrimaryStacksParamList = { + Home: undefined + Profile: {name: string} } -export type PrimaryTabScreenProps<T extends keyof PrimaryTabParamList> = +export type PrimaryStacksScreenProps<T extends keyof PrimaryStacksParamList> = CompositeScreenProps< - BottomTabScreenProps<PrimaryTabParamList, T>, - RootStackScreenProps<keyof RootStackParamList> + BottomTabScreenProps<PrimaryStacksParamList, T>, + RootTabsScreenProps<keyof RootTabsParamList> > +*/ diff --git a/src/screens/Home.tsx b/src/screens/Home.tsx index a2d013abe..90c9262f3 100644 --- a/src/screens/Home.tsx +++ b/src/screens/Home.tsx @@ -1,20 +1,21 @@ import React from 'react' -import {Text, Button, View, SafeAreaView} from 'react-native' -import type {PrimaryTabScreenProps} from '../routes/types' +import {Text, Button, View} from 'react-native' +import {Shell} from '../platform/shell' +import type {RootTabsScreenProps} from '../routes/types' import {useStores} from '../state' -export function Home({navigation}: PrimaryTabScreenProps<'Home'>) { +export function Home({navigation}: RootTabsScreenProps<'Home'>) { const store = useStores() return ( - <SafeAreaView style={{flex: 1}}> - <View style={{flex: 1}}> - <Text>Hello world</Text> + <Shell> + <View style={{alignItems: 'center'}}> + <Text style={{fontSize: 20, fontWeight: 'bold'}}>Home</Text> <Button title="Go to Jane's profile" onPress={() => navigation.navigate('Profile', {name: 'Jane'})} /> <Button title="Logout" onPress={() => store.session.setAuthed(false)} /> </View> - </SafeAreaView> + </Shell> ) } diff --git a/src/screens/Login.tsx b/src/screens/Login.tsx index 77580748c..0eea085d5 100644 --- a/src/screens/Login.tsx +++ b/src/screens/Login.tsx @@ -1,17 +1,18 @@ import React from 'react' -import {Text, Button, View, SafeAreaView} from 'react-native' -import type {RootStackScreenProps} from '../routes/types' +import {Text, Button, View} from 'react-native' +import {Shell} from '../platform/shell' +import type {RootTabsScreenProps} from '../routes/types' import {useStores} from '../state' -export function Login({navigation}: RootStackScreenProps<'Login'>) { +export function Login({navigation}: RootTabsScreenProps<'Login'>) { const store = useStores() return ( - <SafeAreaView style={{flex: 1}}> - <View style={{flex: 1}}> - <Text>Welcome! Time to sign in</Text> + <Shell> + <View style={{justifyContent: 'center', alignItems: 'center'}}> + <Text style={{fontSize: 20, fontWeight: 'bold'}}>Sign In</Text> <Button title="Login" onPress={() => store.session.setAuthed(true)} /> <Button title="Sign Up" onPress={() => navigation.navigate('Signup')} /> </View> - </SafeAreaView> + </Shell> ) } diff --git a/src/screens/Menu.tsx b/src/screens/Menu.tsx index 2e0376a49..9cdda4f2a 100644 --- a/src/screens/Menu.tsx +++ b/src/screens/Menu.tsx @@ -1,19 +1,16 @@ import React from 'react' -import {SafeAreaView, ScrollView, Text, Button, View} from 'react-native' -import type {PrimaryTabScreenProps} from '../routes/types' +import {Shell} from '../platform/shell' +import {ScrollView, Text, View} from 'react-native' +import type {RootTabsScreenProps} from '../routes/types' -export const Menu = ({navigation}: PrimaryTabScreenProps<'Menu'>) => { +export const Menu = (_props: RootTabsScreenProps<'Menu'>) => { return ( - <SafeAreaView> + <Shell> <ScrollView contentInsetAdjustmentBehavior="automatic"> - <View> - <Text>Hello world</Text> - <Button - title="Go to Jane's profile" - onPress={() => navigation.navigate('Profile', {name: 'Jane'})} - /> + <View style={{justifyContent: 'center', alignItems: 'center'}}> + <Text style={{fontSize: 20, fontWeight: 'bold'}}>Menu</Text> </View> </ScrollView> - </SafeAreaView> + </Shell> ) } diff --git a/src/screens/NotFound.tsx b/src/screens/NotFound.tsx index afb91b402..f4d9d510c 100644 --- a/src/screens/NotFound.tsx +++ b/src/screens/NotFound.tsx @@ -1,27 +1,15 @@ import React from 'react' -import { - SafeAreaView, - ScrollView, - StatusBar, - Text, - Button, - useColorScheme, - View, -} from 'react-native' -import type {RootStackScreenProps} from '../routes/types' - -export const NotFound = ({navigation}: RootStackScreenProps<'NotFound'>) => { - const isDarkMode = useColorScheme() === 'dark' +import {Shell} from '../platform/shell' +import {Text, Button, View} from 'react-native' +import type {RootTabsScreenProps} from '../routes/types' +export const NotFound = ({navigation}: RootTabsScreenProps<'NotFound'>) => { return ( - <SafeAreaView> - <StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} /> - <ScrollView contentInsetAdjustmentBehavior="automatic"> - <View> - <Text>Page not found</Text> - <Button title="Home" onPress={() => navigation.navigate('Primary')} /> - </View> - </ScrollView> - </SafeAreaView> + <Shell> + <View style={{justifyContent: 'center', alignItems: 'center'}}> + <Text style={{fontSize: 20, fontWeight: 'bold'}}>Page not found</Text> + <Button title="Home" onPress={() => navigation.navigate('Home')} /> + </View> + </Shell> ) } diff --git a/src/screens/Notifications.tsx b/src/screens/Notifications.tsx index 8ffb5bb87..292f4593f 100644 --- a/src/screens/Notifications.tsx +++ b/src/screens/Notifications.tsx @@ -1,21 +1,14 @@ import React from 'react' -import {SafeAreaView, ScrollView, Text, Button, View} from 'react-native' -import type {PrimaryTabScreenProps} from '../routes/types' +import {Shell} from '../platform/shell' +import {Text, View} from 'react-native' +import type {RootTabsScreenProps} from '../routes/types' -export const Notifications = ({ - navigation, -}: PrimaryTabScreenProps<'Notifications'>) => { +export const Notifications = (_props: RootTabsScreenProps<'Notifications'>) => { return ( - <SafeAreaView> - <ScrollView contentInsetAdjustmentBehavior="automatic"> - <View> - <Text>Hello world</Text> - <Button - title="Go to Jane's profile" - onPress={() => navigation.navigate('Profile', {name: 'Jane'})} - /> - </View> - </ScrollView> - </SafeAreaView> + <Shell> + <View style={{justifyContent: 'center', alignItems: 'center'}}> + <Text style={{fontSize: 20, fontWeight: 'bold'}}>Notifications</Text> + </View> + </Shell> ) } diff --git a/src/screens/Profile.tsx b/src/screens/Profile.tsx index ffd30a499..76915b48f 100644 --- a/src/screens/Profile.tsx +++ b/src/screens/Profile.tsx @@ -1,7 +1,16 @@ import React from 'react' -import {Text} from 'react-native' -import type {RootStackScreenProps} from '../routes/types' +import {Shell} from '../platform/shell' +import {View, Text} from 'react-native' +import type {RootTabsScreenProps} from '../routes/types' -export const Profile = ({route}: RootStackScreenProps<'Profile'>) => { - return <Text>This is {route.params.name}'s profile</Text> +export const Profile = ({route}: RootTabsScreenProps<'Profile'>) => { + return ( + <Shell> + <View style={{justifyContent: 'center', alignItems: 'center'}}> + <Text style={{fontSize: 20, fontWeight: 'bold'}}> + {route.params?.name}'s profile + </Text> + </View> + </Shell> + ) } diff --git a/src/screens/Search.tsx b/src/screens/Search.tsx index 85943190a..d456cd196 100644 --- a/src/screens/Search.tsx +++ b/src/screens/Search.tsx @@ -1,19 +1,14 @@ import React from 'react' -import {SafeAreaView, ScrollView, Text, Button, View} from 'react-native' -import type {PrimaryTabScreenProps} from '../routes/types' +import {Shell} from '../platform/shell' +import {Text, View} from 'react-native' +import type {RootTabsScreenProps} from '../routes/types' -export const Search = ({navigation}: PrimaryTabScreenProps<'Search'>) => { +export const Search = (_props: RootTabsScreenProps<'Search'>) => { return ( - <SafeAreaView> - <ScrollView contentInsetAdjustmentBehavior="automatic"> - <View> - <Text>Hello world</Text> - <Button - title="Go to Jane's profile" - onPress={() => navigation.navigate('Profile', {name: 'Jane'})} - /> - </View> - </ScrollView> - </SafeAreaView> + <Shell> + <View style={{justifyContent: 'center', alignItems: 'center'}}> + <Text style={{fontSize: 20, fontWeight: 'bold'}}>Search</Text> + </View> + </Shell> ) } diff --git a/src/screens/Signup.tsx b/src/screens/Signup.tsx index bf7f8f2f8..bab7abc1c 100644 --- a/src/screens/Signup.tsx +++ b/src/screens/Signup.tsx @@ -1,14 +1,15 @@ import React from 'react' -import {Text, Button, View, SafeAreaView} from 'react-native' -import type {RootStackScreenProps} from '../routes/types' +import {Shell} from '../platform/shell' +import {Text, Button, View} from 'react-native' +import type {RootTabsScreenProps} from '../routes/types' import {useStores} from '../state' -export function Signup({navigation}: RootStackScreenProps<'Signup'>) { +export function Signup({navigation}: RootTabsScreenProps<'Signup'>) { const store = useStores() return ( - <SafeAreaView style={{flex: 1}}> - <View style={{flex: 1}}> - <Text>Let's create your account</Text> + <Shell> + <View style={{justifyContent: 'center', alignItems: 'center'}}> + <Text style={{fontSize: 20, fontWeight: 'bold'}}>Create Account</Text> <Button title="Create new account" onPress={() => store.session.setAuthed(true)} @@ -18,6 +19,6 @@ export function Signup({navigation}: RootStackScreenProps<'Signup'>) { onPress={() => navigation.navigate('Login')} /> </View> - </SafeAreaView> + </Shell> ) } |