diff options
author | Paul Frazee <pfrazee@gmail.com> | 2023-03-13 16:01:43 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-13 16:01:43 -0500 |
commit | 56cf890debeb9872f791ccb992a5587f2c05fd9e (patch) | |
tree | 929453b41274a712d8b2fce441e98a0cd030d305 /src/view/shell/Drawer.tsx | |
parent | 503e03d91e1de4bfeabec1eb2d97dcdceb13fcc5 (diff) | |
download | voidsky-56cf890debeb9872f791ccb992a5587f2c05fd9e.tar.zst |
Move to expo and react-navigation (#288)
* WIP - adding expo * WIP - adding expo 2 * Fix tsc * Finish adding expo * Disable the 'require cycle' warning * Tweak plist * Modify some dependency versions to make expo happy * Fix icon fill * Get Web compiling for expo * 1.7 * Switch to react-navigation in expo2 (#287) * WIP Switch to react-navigation * WIP Switch to react-navigation 2 * WIP Switch to react-navigation 3 * Convert all screens to react navigation * Update BottomBar for react navigation * Update mobile menu to be react-native drawer * Fixes to drawer and bottombar * Factor out some helpers * Replace the navigation model with react-navigation * Restructure the shell folder and fix the header positioning * Restore the error boundary * Fix tsc * Implement not-found page * Remove react-native-gesture-handler (no longer used) * Handle notifee card presses * Handle all navigations from the state layer * Fix drawer behaviors * Fix two linking issues * Switch to our react-native-progress fork to fix an svg rendering issue * Get Web working with react-navigation * Refactor routes and navigation for a bit more clarity * Remove dead code * Rework Web shell to left/right nav to make this easier * Fix ViewHeader for desktop web * Hide profileheader back btn on desktop web * Move the compose button to the left nav * Implement reply prompt in threads for desktop web * Composer refactors * Factor out all platform-specific text input behaviors from the composer * Small fix * Update the web build to use tiptap for the composer * Tune up the mention autocomplete dropdown * Simplify the default avatar and banner * Fixes to link cards in web composer * Fix dropdowns on web * Tweak load latest on desktop * Add web beta message and feedback link * Fix up links in desktop web
Diffstat (limited to 'src/view/shell/Drawer.tsx')
-rw-r--r-- | src/view/shell/Drawer.tsx | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx new file mode 100644 index 000000000..80944e10a --- /dev/null +++ b/src/view/shell/Drawer.tsx @@ -0,0 +1,386 @@ +import React from 'react' +import { + Linking, + SafeAreaView, + StyleProp, + StyleSheet, + TouchableOpacity, + View, + ViewStyle, +} from 'react-native' +import { + useNavigation, + useNavigationState, + StackActions, +} from '@react-navigation/native' +import {observer} from 'mobx-react-lite' +import { + FontAwesomeIcon, + FontAwesomeIconStyle, +} from '@fortawesome/react-native-fontawesome' +import {s, colors} from 'lib/styles' +import {FEEDBACK_FORM_URL} from 'lib/constants' +import {useStores} from 'state/index' +import { + HomeIcon, + HomeIconSolid, + BellIcon, + BellIconSolid, + UserIcon, + CogIcon, + MagnifyingGlassIcon2, + MagnifyingGlassIcon2Solid, + MoonIcon, +} from 'lib/icons' +import {UserAvatar} from 'view/com/util/UserAvatar' +import {Text} from 'view/com/util/text/Text' +import {useTheme} from 'lib/ThemeContext' +import {usePalette} from 'lib/hooks/usePalette' +import {useAnalytics} from 'lib/analytics' +import {pluralize} from 'lib/strings/helpers' +import {getCurrentRoute, isTab, getTabState, TabState} from 'lib/routes/helpers' +import {NavigationProp} from 'lib/routes/types' + +export const DrawerContent = observer(() => { + const theme = useTheme() + const pal = usePalette('default') + const store = useStores() + const navigation = useNavigation<NavigationProp>() + const {track} = useAnalytics() + const {isAtHome, isAtSearch, isAtNotifications} = useNavigationState( + state => { + const currentRoute = state ? getCurrentRoute(state) : false + return { + isAtHome: currentRoute ? isTab(currentRoute.name, 'Home') : true, + isAtSearch: currentRoute ? isTab(currentRoute.name, 'Search') : false, + isAtNotifications: currentRoute + ? isTab(currentRoute.name, 'Notifications') + : false, + } + }, + ) + + // events + // = + + const onPressTab = React.useCallback( + (tab: string) => { + track('Menu:ItemClicked', {url: tab}) + const state = navigation.getState() + store.shell.closeDrawer() + const tabState = getTabState(state, tab) + if (tabState === TabState.InsideAtRoot) { + store.emitScreenSoftReset() + } else if (tabState === TabState.Inside) { + navigation.dispatch(StackActions.popToTop()) + } else { + // @ts-ignore must be Home, Search, or Notifications + navigation.navigate(`${tab}Tab`) + } + }, + [store, track, navigation], + ) + + const onPressHome = React.useCallback(() => onPressTab('Home'), [onPressTab]) + + const onPressSearch = React.useCallback( + () => onPressTab('Search'), + [onPressTab], + ) + + const onPressNotifications = React.useCallback( + () => onPressTab('Notifications'), + [onPressTab], + ) + + const onPressProfile = React.useCallback(() => { + track('Menu:ItemClicked', {url: 'Profile'}) + navigation.navigate('Profile', {name: store.me.handle}) + store.shell.closeDrawer() + }, [navigation, track, store.me.handle, store.shell]) + + const onPressSettings = React.useCallback(() => { + track('Menu:ItemClicked', {url: 'Settings'}) + navigation.navigate('Settings') + store.shell.closeDrawer() + }, [navigation, track, store.shell]) + + const onPressFeedback = () => { + track('Menu:FeedbackClicked') + Linking.openURL(FEEDBACK_FORM_URL) + } + + // rendering + // = + + const MenuItem = ({ + icon, + label, + count, + bold, + onPress, + }: { + icon: JSX.Element + label: string + count?: number + bold?: boolean + onPress: () => void + }) => ( + <TouchableOpacity + testID={`menuItemButton-${label}`} + style={styles.menuItem} + onPress={onPress}> + <View style={[styles.menuItemIconWrapper]}> + {icon} + {count ? ( + <View style={styles.menuItemCount}> + <Text style={styles.menuItemCountLabel}>{count}</Text> + </View> + ) : undefined} + </View> + <Text + type={bold ? '2xl-bold' : '2xl'} + style={[pal.text, s.flex1]} + numberOfLines={1}> + {label} + </Text> + </TouchableOpacity> + ) + + const onDarkmodePress = () => { + track('Menu:ItemClicked', {url: '/darkmode'}) + store.shell.setDarkMode(!store.shell.darkMode) + } + + return ( + <View + testID="menuView" + style={[ + styles.view, + theme.colorScheme === 'light' ? pal.view : styles.viewDarkMode, + ]}> + <SafeAreaView style={s.flex1}> + <TouchableOpacity testID="profileCardButton" onPress={onPressProfile}> + <UserAvatar size={80} avatar={store.me.avatar} /> + <Text + type="title-lg" + style={[pal.text, s.bold, styles.profileCardDisplayName]}> + {store.me.displayName || store.me.handle} + </Text> + <Text type="2xl" style={[pal.textLight, styles.profileCardHandle]}> + @{store.me.handle} + </Text> + <Text type="xl" style={[pal.textLight, styles.profileCardFollowers]}> + <Text type="xl-medium" style={pal.text}> + {store.me.followersCount || 0} + </Text>{' '} + {pluralize(store.me.followersCount || 0, 'follower')} ·{' '} + <Text type="xl-medium" style={pal.text}> + {store.me.followsCount || 0} + </Text>{' '} + following + </Text> + </TouchableOpacity> + <View style={s.flex1} /> + <View> + <MenuItem + icon={ + isAtSearch ? ( + <MagnifyingGlassIcon2Solid + style={pal.text as StyleProp<ViewStyle>} + size={24} + strokeWidth={1.7} + /> + ) : ( + <MagnifyingGlassIcon2 + style={pal.text as StyleProp<ViewStyle>} + size={24} + strokeWidth={1.7} + /> + ) + } + label="Search" + bold={isAtSearch} + onPress={onPressSearch} + /> + <MenuItem + icon={ + isAtHome ? ( + <HomeIconSolid + style={pal.text as StyleProp<ViewStyle>} + size="24" + strokeWidth={3.25} + /> + ) : ( + <HomeIcon + style={pal.text as StyleProp<ViewStyle>} + size="24" + strokeWidth={3.25} + /> + ) + } + label="Home" + bold={isAtHome} + onPress={onPressHome} + /> + <MenuItem + icon={ + isAtNotifications ? ( + <BellIconSolid + style={pal.text as StyleProp<ViewStyle>} + size="24" + strokeWidth={1.7} + /> + ) : ( + <BellIcon + style={pal.text as StyleProp<ViewStyle>} + size="24" + strokeWidth={1.7} + /> + ) + } + label="Notifications" + count={store.me.notifications.unreadCount} + bold={isAtNotifications} + onPress={onPressNotifications} + /> + <MenuItem + icon={ + <UserIcon + style={pal.text as StyleProp<ViewStyle>} + size="26" + strokeWidth={1.5} + /> + } + label="Profile" + onPress={onPressProfile} + /> + <MenuItem + icon={ + <CogIcon + style={pal.text as StyleProp<ViewStyle>} + size="26" + strokeWidth={1.75} + /> + } + label="Settings" + onPress={onPressSettings} + /> + </View> + <View style={s.flex1} /> + <View style={styles.footer}> + <TouchableOpacity + onPress={onDarkmodePress} + style={[ + styles.footerBtn, + theme.colorScheme === 'light' + ? pal.btn + : styles.footerBtnDarkMode, + ]}> + <MoonIcon + size={22} + style={pal.text as StyleProp<ViewStyle>} + strokeWidth={2} + /> + </TouchableOpacity> + <TouchableOpacity + onPress={onPressFeedback} + style={[ + styles.footerBtn, + styles.footerBtnFeedback, + theme.colorScheme === 'light' + ? styles.footerBtnFeedbackLight + : styles.footerBtnFeedbackDark, + ]}> + <FontAwesomeIcon + style={pal.link as FontAwesomeIconStyle} + size={19} + icon={['far', 'message']} + /> + <Text type="2xl-medium" style={[pal.link, s.pl10]}> + Feedback + </Text> + </TouchableOpacity> + </View> + </SafeAreaView> + </View> + ) +}) + +const styles = StyleSheet.create({ + view: { + flex: 1, + paddingTop: 20, + paddingBottom: 50, + paddingLeft: 20, + }, + viewDarkMode: { + backgroundColor: '#1B1919', + }, + + profileCardDisplayName: { + marginTop: 20, + paddingRight: 30, + }, + profileCardHandle: { + marginTop: 4, + paddingRight: 30, + }, + profileCardFollowers: { + marginTop: 16, + paddingRight: 30, + }, + + menuItem: { + flexDirection: 'row', + alignItems: 'center', + paddingVertical: 16, + paddingRight: 10, + }, + menuItemIconWrapper: { + width: 24, + height: 24, + alignItems: 'center', + justifyContent: 'center', + marginRight: 12, + }, + menuItemCount: { + position: 'absolute', + right: -6, + top: -2, + backgroundColor: colors.red3, + paddingHorizontal: 4, + paddingBottom: 1, + borderRadius: 6, + }, + menuItemCountLabel: { + fontSize: 12, + fontWeight: 'bold', + color: colors.white, + }, + + footer: { + flexDirection: 'row', + justifyContent: 'space-between', + paddingRight: 30, + paddingTop: 80, + }, + footerBtn: { + flexDirection: 'row', + alignItems: 'center', + padding: 10, + borderRadius: 25, + }, + footerBtnDarkMode: { + backgroundColor: colors.black, + }, + footerBtnFeedback: { + paddingHorizontal: 24, + }, + footerBtnFeedbackLight: { + backgroundColor: '#DDEFFF', + }, + footerBtnFeedbackDark: { + backgroundColor: colors.blue6, + }, +}) |