diff options
author | Paul Frazee <pfrazee@gmail.com> | 2022-07-21 16:43:47 -0500 |
---|---|---|
committer | Paul Frazee <pfrazee@gmail.com> | 2022-07-21 16:43:47 -0500 |
commit | 29ed3d2ecf1fd6de8af0f25f6d541fe2adaf61f9 (patch) | |
tree | afc0d632c981c097013a2fbb1c15decc1e78bec0 /src | |
parent | 28dbc5f5e614b3caf08d36a7edaaf19a0ee0f0bc (diff) | |
download | voidsky-29ed3d2ecf1fd6de8af0f25f6d541fe2adaf61f9.tar.zst |
Add header to PostThread view; update navigation to include stacking so that each tab maintains its own browsing history
Diffstat (limited to 'src')
-rw-r--r-- | src/assets/alice.jpg | bin | 25330 -> 0 bytes | |||
-rw-r--r-- | src/assets/bob.jpg | bin | 45720 -> 0 bytes | |||
-rw-r--r-- | src/assets/carla.jpg | bin | 12259 -> 0 bytes | |||
-rw-r--r-- | src/view/com/feed/FeedItem.tsx | 33 | ||||
-rw-r--r-- | src/view/com/post-thread/PostThreadItem.tsx | 33 | ||||
-rw-r--r-- | src/view/index.ts | 2 | ||||
-rw-r--r-- | src/view/lib/assets.native.ts | 7 | ||||
-rw-r--r-- | src/view/lib/assets.ts | 7 | ||||
-rw-r--r-- | src/view/routes/index.tsx | 79 | ||||
-rw-r--r-- | src/view/routes/types.ts | 13 | ||||
-rw-r--r-- | src/view/screens/Home.tsx | 5 | ||||
-rw-r--r-- | src/view/screens/Menu.tsx | 2 | ||||
-rw-r--r-- | src/view/screens/NotFound.tsx | 2 | ||||
-rw-r--r-- | src/view/screens/Notifications.tsx | 4 | ||||
-rw-r--r-- | src/view/screens/Search.tsx | 2 | ||||
-rw-r--r-- | src/view/screens/content/PostThread.tsx | 19 |
16 files changed, 128 insertions, 80 deletions
diff --git a/src/assets/alice.jpg b/src/assets/alice.jpg deleted file mode 100644 index 83555d962..000000000 --- a/src/assets/alice.jpg +++ /dev/null Binary files differdiff --git a/src/assets/bob.jpg b/src/assets/bob.jpg deleted file mode 100644 index 0567772b6..000000000 --- a/src/assets/bob.jpg +++ /dev/null Binary files differdiff --git a/src/assets/carla.jpg b/src/assets/carla.jpg deleted file mode 100644 index f26734280..000000000 --- a/src/assets/carla.jpg +++ /dev/null Binary files differdiff --git a/src/view/com/feed/FeedItem.tsx b/src/view/com/feed/FeedItem.tsx index 18af53dde..9cf70578c 100644 --- a/src/view/com/feed/FeedItem.tsx +++ b/src/view/com/feed/FeedItem.tsx @@ -1,25 +1,13 @@ import React from 'react' import {observer} from 'mobx-react-lite' -import { - Image, - ImageSourcePropType, - StyleSheet, - Text, - TouchableOpacity, - View, -} from 'react-native' +import {Image, StyleSheet, Text, TouchableOpacity, View} from 'react-native' import {bsky, AdxUri} from '@adxp/mock-api' import moment from 'moment' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {OnNavigateContent} from '../../routes/types' import {FeedViewItemModel} from '../../../state/models/feed-view' import {s} from '../../lib/styles' - -const IMAGES: Record<string, ImageSourcePropType> = { - 'alice.com': require('../../assets/alice.jpg'), - 'bob.com': require('../../assets/bob.jpg'), - 'carla.com': require('../../assets/carla.jpg'), -} +import {AVIS} from '../../lib/assets' export const FeedItem = observer(function FeedItem({ item, @@ -40,10 +28,7 @@ export const FeedItem = observer(function FeedItem({ <TouchableOpacity style={styles.outer} onPress={onPressOuter}> {item.repostedBy && ( <View style={styles.repostedBy}> - <FontAwesomeIcon - icon="retweet" - style={[styles.repostedByIcon, s.gray]} - /> + <FontAwesomeIcon icon="retweet" style={styles.repostedByIcon} /> <Text style={[s.gray, s.bold, s.f13]}> Reposted by {item.repostedBy.displayName} </Text> @@ -53,7 +38,7 @@ export const FeedItem = observer(function FeedItem({ <View style={styles.layoutAvi}> <Image style={styles.avi} - source={IMAGES[item.author.name] || IMAGES['alice.com']} + source={AVIS[item.author.name] || AVIS['alice.com']} /> </View> <View style={styles.layoutContent}> @@ -74,14 +59,14 @@ export const FeedItem = observer(function FeedItem({ <View style={styles.ctrls}> <View style={styles.ctrl}> <FontAwesomeIcon - style={[styles.ctrlIcon, s.gray]} + style={styles.ctrlIcon} icon={['far', 'comment']} /> <Text>{item.replyCount}</Text> </View> <View style={styles.ctrl}> <FontAwesomeIcon - style={[styles.ctrlIcon, s.gray]} + style={styles.ctrlIcon} icon="retweet" size={22} /> @@ -89,14 +74,14 @@ export const FeedItem = observer(function FeedItem({ </View> <View style={styles.ctrl}> <FontAwesomeIcon - style={[styles.ctrlIcon, s.gray]} + style={styles.ctrlIcon} icon={['far', 'heart']} /> <Text>{item.likeCount}</Text> </View> <View style={styles.ctrl}> <FontAwesomeIcon - style={[styles.ctrlIcon, s.gray]} + style={styles.ctrlIcon} icon="share-from-square" /> </View> @@ -120,6 +105,7 @@ const styles = StyleSheet.create({ }, repostedByIcon: { marginRight: 2, + color: 'gray', }, layout: { flexDirection: 'row', @@ -159,5 +145,6 @@ const styles = StyleSheet.create({ }, ctrlIcon: { marginRight: 5, + color: 'gray', }, }) diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index 2b72b7e4b..5e01ac0a6 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -1,13 +1,6 @@ import React from 'react' import {observer} from 'mobx-react-lite' -import { - Image, - ImageSourcePropType, - StyleSheet, - Text, - TouchableOpacity, - View, -} from 'react-native' +import {Image, StyleSheet, Text, TouchableOpacity, View} from 'react-native' import {bsky, AdxUri} from '@adxp/mock-api' import moment from 'moment' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' @@ -15,14 +8,9 @@ import {OnNavigateContent} from '../../routes/types' import {PostThreadViewPostModel} from '../../../state/models/post-thread-view' import {s} from '../../lib/styles' import {pluralize} from '../../lib/strings' +import {AVIS} from '../../lib/assets' -const IMAGES: Record<string, ImageSourcePropType> = { - 'alice.com': require('../../assets/alice.jpg'), - 'bob.com': require('../../assets/bob.jpg'), - 'carla.com': require('../../assets/carla.jpg'), -} - -function iter<T>(n: number, fn: (i: number) => T): Array<T> { +function iter<T>(n: number, fn: (_i: number) => T): Array<T> { const arr: T[] = [] for (let i = 0; i < n; i++) { arr.push(fn(i)) @@ -49,13 +37,13 @@ export const PostThreadItem = observer(function PostThreadItem({ return ( <TouchableOpacity style={styles.outer} onPress={onPressOuter}> <View style={styles.layout}> - {iter(Math.abs(item._depth), () => ( - <View style={styles.replyBar} /> + {iter(Math.abs(item._depth), (i: number) => ( + <View key={i} style={styles.replyBar} /> ))} <View style={styles.layoutAvi}> <Image style={styles.avi} - source={IMAGES[item.author.name] || IMAGES['alice.com']} + source={AVIS[item.author.name] || AVIS['alice.com']} /> </View> <View style={styles.layoutContent}> @@ -104,14 +92,14 @@ export const PostThreadItem = observer(function PostThreadItem({ <View style={styles.ctrls}> <View style={styles.ctrl}> <FontAwesomeIcon - style={[styles.ctrlIcon, s.gray]} + style={styles.ctrlIcon} icon={['far', 'comment']} /> <Text>{item.replyCount}</Text> </View> <View style={styles.ctrl}> <FontAwesomeIcon - style={[styles.ctrlIcon, s.gray]} + style={styles.ctrlIcon} icon="retweet" size={22} /> @@ -119,14 +107,14 @@ export const PostThreadItem = observer(function PostThreadItem({ </View> <View style={styles.ctrl}> <FontAwesomeIcon - style={[styles.ctrlIcon, s.gray]} + style={styles.ctrlIcon} icon={['far', 'heart']} /> <Text>{item.likeCount}</Text> </View> <View style={styles.ctrl}> <FontAwesomeIcon - style={[styles.ctrlIcon, s.gray]} + style={styles.ctrlIcon} icon="share-from-square" /> </View> @@ -204,5 +192,6 @@ const styles = StyleSheet.create({ }, ctrlIcon: { marginRight: 5, + color: 'gray', }, }) diff --git a/src/view/index.ts b/src/view/index.ts index d636844cc..8e3b00798 100644 --- a/src/view/index.ts +++ b/src/view/index.ts @@ -1,6 +1,7 @@ import moment from 'moment' import {library} from '@fortawesome/fontawesome-svg-core' +import {faArrowLeft} from '@fortawesome/free-solid-svg-icons/faArrowLeft' import {faBars} from '@fortawesome/free-solid-svg-icons/faBars' import {faBell} from '@fortawesome/free-solid-svg-icons/faBell' import {faComment} from '@fortawesome/free-regular-svg-icons/faComment' @@ -32,6 +33,7 @@ export function setup() { }, }) library.add( + faArrowLeft, faBars, faBell, faComment, diff --git a/src/view/lib/assets.native.ts b/src/view/lib/assets.native.ts new file mode 100644 index 000000000..af5ee2bac --- /dev/null +++ b/src/view/lib/assets.native.ts @@ -0,0 +1,7 @@ +import {ImageSourcePropType} from 'react-native' + +export const AVIS: Record<string, ImageSourcePropType> = { + 'alice.com': require('../../../public/img/alice.jpg'), + 'bob.com': require('../../../public/img/bob.jpg'), + 'carla.com': require('../../../public/img/carla.jpg'), +} diff --git a/src/view/lib/assets.ts b/src/view/lib/assets.ts new file mode 100644 index 000000000..7d0584a41 --- /dev/null +++ b/src/view/lib/assets.ts @@ -0,0 +1,7 @@ +import {ImageSourcePropType} from 'react-native' + +export const AVIS: Record<string, ImageSourcePropType> = { + 'alice.com': {uri: '/img/alice.jpg'}, + 'bob.com': {uri: '/img/bob.jpg'}, + 'carla.com': {uri: '/img/carla.jpg'}, +} diff --git a/src/view/routes/index.tsx b/src/view/routes/index.tsx index 24d47a93c..58aa0ad8b 100644 --- a/src/view/routes/index.tsx +++ b/src/view/routes/index.tsx @@ -1,5 +1,5 @@ import React, {useEffect} from 'react' -import {Text, Linking} from 'react-native' +import {Linking, Text} from 'react-native' import { NavigationContainer, LinkingOptions, @@ -32,12 +32,12 @@ const linking: LinkingOptions<RootTabsParamList> = { ], config: { screens: { - Home: '', + HomeTab: '', + SearchTab: 'search', + NotificationsTab: 'notifications', + MenuTab: 'menu', Profile: 'profile/:name', PostThread: 'profile/:name/post/:recordKey', - Search: 'search', - Notifications: 'notifications', - Menu: 'menu', Login: 'login', Signup: 'signup', NotFound: '*', @@ -46,7 +46,9 @@ const linking: LinkingOptions<RootTabsParamList> = { } export const RootTabs = createBottomTabNavigator<RootTabsParamList>() -export const PrimaryStack = createNativeStackNavigator() +export const HomeTabStack = createNativeStackNavigator() +export const SearchTabStack = createNativeStackNavigator() +export const NotificationsTabStack = createNativeStackNavigator() const tabBarScreenOptions = ({ route, @@ -56,18 +58,18 @@ const tabBarScreenOptions = ({ headerShown: false, tabBarIcon: (state: {focused: boolean; color: string; size: number}) => { switch (route.name) { - case 'Home': + case 'HomeTab': return <FontAwesomeIcon icon="house" style={{color: state.color}} /> - case 'Search': + case 'SearchTab': return ( <FontAwesomeIcon icon="magnifying-glass" style={{color: state.color}} /> ) - case 'Notifications': + case 'NotificationsTab': return <FontAwesomeIcon icon="bell" style={{color: state.color}} /> - case 'Menu': + case 'MenuTab': return <FontAwesomeIcon icon="bars" style={{color: state.color}} /> default: return <FontAwesomeIcon icon="bars" style={{color: state.color}} /> @@ -75,8 +77,46 @@ const tabBarScreenOptions = ({ }, }) +const HIDE_HEADER = {headerShown: false} const HIDE_TAB = {tabBarButton: () => null} +function HomeStackCom() { + return ( + <HomeTabStack.Navigator> + <HomeTabStack.Screen name="Home" component={Home} options={HIDE_HEADER} /> + <HomeTabStack.Screen name="Profile" component={Profile} /> + <HomeTabStack.Screen name="PostThread" component={PostThread} /> + </HomeTabStack.Navigator> + ) +} + +function SearchStackCom() { + return ( + <SearchTabStack.Navigator> + <SearchTabStack.Screen + name="Search" + component={Search} + options={HIDE_HEADER} + /> + <SearchTabStack.Screen name="Profile" component={Profile} /> + <SearchTabStack.Screen name="PostThread" component={PostThread} /> + </SearchTabStack.Navigator> + ) +} + +function NotificationsStackCom() { + return ( + <NotificationsTabStack.Navigator> + <NotificationsTabStack.Screen + name="Notifications" + component={Notifications} + /> + <NotificationsTabStack.Screen name="Profile" component={Profile} /> + <NotificationsTabStack.Screen name="PostThread" component={PostThread} /> + </NotificationsTabStack.Navigator> + ) +} + export const Root = observer(() => { const store = useStores() @@ -96,25 +136,18 @@ export const Root = observer(() => { return ( <NavigationContainer linking={linking} fallback={<Text>Loading...</Text>}> <RootTabs.Navigator - initialRouteName={store.session.isAuthed ? 'Home' : 'Login'} + initialRouteName={store.session.isAuthed ? 'HomeTab' : 'Login'} screenOptions={tabBarScreenOptions} tabBar={tabBar}> {store.session.isAuthed ? ( <> - <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} - /> + <RootTabs.Screen name="HomeTab" component={HomeStackCom} /> + <RootTabs.Screen name="SearchTab" component={SearchStackCom} /> <RootTabs.Screen - name="PostThread" - component={PostThread} - options={HIDE_TAB} + name="NotificationsTab" + component={NotificationsStackCom} /> + <RootTabs.Screen name="MenuTab" component={Menu} /> </> ) : ( <> diff --git a/src/view/routes/types.ts b/src/view/routes/types.ts index bca6f196a..0b4bbc5d7 100644 --- a/src/view/routes/types.ts +++ b/src/view/routes/types.ts @@ -1,10 +1,10 @@ import type {StackScreenProps} from '@react-navigation/stack' export type RootTabsParamList = { - Home: undefined - Search: undefined - Notifications: undefined - Menu: undefined + HomeTab: undefined + SearchTab: undefined + NotificationsTab: undefined + MenuTab: undefined Profile: {name: string} PostThread: {name: string; recordKey: string} Login: undefined @@ -14,7 +14,10 @@ export type RootTabsParamList = { export type RootTabsScreenProps<T extends keyof RootTabsParamList> = StackScreenProps<RootTabsParamList, T> -export type OnNavigateContent = (screen: string, params: Record<string, string>): void +export type OnNavigateContent = ( + screen: string, + params: Record<string, string>, +) => void /* NOTE diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx index 4a3e41a75..1b41b2d35 100644 --- a/src/view/screens/Home.tsx +++ b/src/view/screens/Home.tsx @@ -5,15 +5,18 @@ import {Feed} from '../com/feed/Feed' import type {RootTabsScreenProps} from '../routes/types' import {useStores} from '../../state' -export function Home({navigation}: RootTabsScreenProps<'Home'>) { +export function Home({navigation}: RootTabsScreenProps<'HomeTab'>) { const store = useStores() useEffect(() => { console.log('Fetching home feed') store.homeFeed.setup() }, [store.homeFeed]) + const onNavigateContent = (screen: string, props: Record<string, string>) => { + // @ts-ignore it's up to the callers to supply correct params -prf navigation.navigate(screen, props) } + return ( <Shell> <View> diff --git a/src/view/screens/Menu.tsx b/src/view/screens/Menu.tsx index 8cf93676e..d0cc0826f 100644 --- a/src/view/screens/Menu.tsx +++ b/src/view/screens/Menu.tsx @@ -3,7 +3,7 @@ import {Shell} from '../shell' import {ScrollView, Text, View} from 'react-native' import type {RootTabsScreenProps} from '../routes/types' -export const Menu = (_props: RootTabsScreenProps<'Menu'>) => { +export const Menu = (_props: RootTabsScreenProps<'MenuTab'>) => { return ( <Shell> <ScrollView contentInsetAdjustmentBehavior="automatic"> diff --git a/src/view/screens/NotFound.tsx b/src/view/screens/NotFound.tsx index 3f6dd7aa0..5357a428a 100644 --- a/src/view/screens/NotFound.tsx +++ b/src/view/screens/NotFound.tsx @@ -8,7 +8,7 @@ export const NotFound = ({navigation}: RootTabsScreenProps<'NotFound'>) => { <Shell> <View style={{justifyContent: 'center', alignItems: 'center'}}> <Text style={{fontSize: 20, fontWeight: 'bold'}}>Page not found</Text> - <Button title="Home" onPress={() => navigation.navigate('Home')} /> + <Button title="Home" onPress={() => navigation.navigate('HomeTab')} /> </View> </Shell> ) diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx index 5bade68fa..a7918f177 100644 --- a/src/view/screens/Notifications.tsx +++ b/src/view/screens/Notifications.tsx @@ -3,7 +3,9 @@ import {Shell} from '../shell' import {Text, View} from 'react-native' import type {RootTabsScreenProps} from '../routes/types' -export const Notifications = (_props: RootTabsScreenProps<'Notifications'>) => { +export const Notifications = ( + _props: RootTabsScreenProps<'NotificationsTab'>, +) => { return ( <Shell> <View style={{justifyContent: 'center', alignItems: 'center'}}> diff --git a/src/view/screens/Search.tsx b/src/view/screens/Search.tsx index 2f111cf72..26df2954c 100644 --- a/src/view/screens/Search.tsx +++ b/src/view/screens/Search.tsx @@ -3,7 +3,7 @@ import {Shell} from '../shell' import {Text, View} from 'react-native' import type {RootTabsScreenProps} from '../routes/types' -export const Search = (_props: RootTabsScreenProps<'Search'>) => { +export const Search = (_props: RootTabsScreenProps<'SearchTab'>) => { return ( <Shell> <View style={{justifyContent: 'center', alignItems: 'center'}}> diff --git a/src/view/screens/content/PostThread.tsx b/src/view/screens/content/PostThread.tsx index 5b8fa951c..e990bcc1b 100644 --- a/src/view/screens/content/PostThread.tsx +++ b/src/view/screens/content/PostThread.tsx @@ -1,4 +1,6 @@ -import React from 'react' +import React, {useLayoutEffect} from 'react' +import {TouchableOpacity} from 'react-native' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {AdxUri} from '@adxp/mock-api' import {Shell} from '../../shell' import type {RootTabsScreenProps} from '../../routes/types' @@ -16,8 +18,21 @@ export const PostThread = ({ urip.recordKey = recordKey const uri = urip.toString() + useLayoutEffect(() => { + navigation.setOptions({ + headerShown: true, + headerTitle: 'Thread', + headerLeft: () => ( + <TouchableOpacity onPress={() => navigation.goBack()}> + <FontAwesomeIcon icon="arrow-left" /> + </TouchableOpacity> + ), + }) + }, [navigation]) + const onNavigateContent = (screen: string, props: Record<string, string>) => { - navigation.navigate(screen, props) + // @ts-ignore it's up to the callers to supply correct params -prf + navigation.push(screen, props) } return ( <Shell> |