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/BottomBar.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/BottomBar.tsx')
-rw-r--r-- | src/view/shell/BottomBar.tsx | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/src/view/shell/BottomBar.tsx b/src/view/shell/BottomBar.tsx new file mode 100644 index 000000000..18b06968f --- /dev/null +++ b/src/view/shell/BottomBar.tsx @@ -0,0 +1,255 @@ +import React from 'react' +import { + Animated, + GestureResponderEvent, + StyleSheet, + TouchableOpacity, + View, +} from 'react-native' +import {StackActions, useNavigationState} from '@react-navigation/native' +import {BottomTabBarProps} from '@react-navigation/bottom-tabs' +import {useSafeAreaInsets} from 'react-native-safe-area-context' +import {observer} from 'mobx-react-lite' +import {Text} from 'view/com/util/text/Text' +import {useStores} from 'state/index' +import {useAnalytics} from 'lib/analytics' +import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' +import {clamp} from 'lib/numbers' +import { + HomeIcon, + HomeIconSolid, + MagnifyingGlassIcon2, + MagnifyingGlassIcon2Solid, + BellIcon, + BellIconSolid, + UserIcon, +} from 'lib/icons' +import {colors} from 'lib/styles' +import {usePalette} from 'lib/hooks/usePalette' +import {getTabState, TabState} from 'lib/routes/helpers' + +export const BottomBar = observer(({navigation}: BottomTabBarProps) => { + const store = useStores() + const pal = usePalette('default') + const minimalShellInterp = useAnimatedValue(0) + const safeAreaInsets = useSafeAreaInsets() + const {track} = useAnalytics() + const {isAtHome, isAtSearch, isAtNotifications} = useNavigationState( + state => { + return { + isAtHome: getTabState(state, 'Home') !== TabState.Outside, + isAtSearch: getTabState(state, 'Search') !== TabState.Outside, + isAtNotifications: + getTabState(state, 'Notifications') !== TabState.Outside, + } + }, + ) + + React.useEffect(() => { + if (store.shell.minimalShellMode) { + Animated.timing(minimalShellInterp, { + toValue: 1, + duration: 100, + useNativeDriver: true, + isInteraction: false, + }).start() + } else { + Animated.timing(minimalShellInterp, { + toValue: 0, + duration: 100, + useNativeDriver: true, + isInteraction: false, + }).start() + } + }, [minimalShellInterp, store.shell.minimalShellMode]) + const footerMinimalShellTransform = { + transform: [{translateY: Animated.multiply(minimalShellInterp, 100)}], + } + + const onPressTab = React.useCallback( + (tab: string) => { + track(`MobileShell:${tab}ButtonPressed`) + const state = navigation.getState() + const tabState = getTabState(state, tab) + if (tabState === TabState.InsideAtRoot) { + store.emitScreenSoftReset() + } else if (tabState === TabState.Inside) { + navigation.dispatch(StackActions.popToTop()) + } else { + 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('MobileShell:ProfileButtonPressed') + navigation.navigate('Profile', {name: store.me.handle}) + }, [navigation, track, store.me.handle]) + + return ( + <Animated.View + style={[ + styles.bottomBar, + pal.view, + pal.border, + {paddingBottom: clamp(safeAreaInsets.bottom, 15, 30)}, + footerMinimalShellTransform, + ]}> + <Btn + icon={ + isAtHome ? ( + <HomeIconSolid + strokeWidth={4} + size={24} + style={[styles.ctrlIcon, pal.text, styles.homeIcon]} + /> + ) : ( + <HomeIcon + strokeWidth={4} + size={24} + style={[styles.ctrlIcon, pal.text, styles.homeIcon]} + /> + ) + } + onPress={onPressHome} + /> + <Btn + icon={ + isAtSearch ? ( + <MagnifyingGlassIcon2Solid + size={25} + style={[styles.ctrlIcon, pal.text, styles.searchIcon]} + strokeWidth={1.8} + /> + ) : ( + <MagnifyingGlassIcon2 + size={25} + style={[styles.ctrlIcon, pal.text, styles.searchIcon]} + strokeWidth={1.8} + /> + ) + } + onPress={onPressSearch} + /> + <Btn + icon={ + isAtNotifications ? ( + <BellIconSolid + size={24} + strokeWidth={1.9} + style={[styles.ctrlIcon, pal.text, styles.bellIcon]} + /> + ) : ( + <BellIcon + size={24} + strokeWidth={1.9} + style={[styles.ctrlIcon, pal.text, styles.bellIcon]} + /> + ) + } + onPress={onPressNotifications} + notificationCount={store.me.notifications.unreadCount} + /> + <Btn + icon={ + <View style={styles.ctrlIconSizingWrapper}> + <UserIcon + size={28} + strokeWidth={1.5} + style={[styles.ctrlIcon, pal.text, styles.profileIcon]} + /> + </View> + } + onPress={onPressProfile} + /> + </Animated.View> + ) +}) + +function Btn({ + icon, + notificationCount, + onPress, + onLongPress, +}: { + icon: JSX.Element + notificationCount?: number + onPress?: (event: GestureResponderEvent) => void + onLongPress?: (event: GestureResponderEvent) => void +}) { + return ( + <TouchableOpacity + style={styles.ctrl} + onPress={onLongPress ? onPress : undefined} + onPressIn={onLongPress ? undefined : onPress} + onLongPress={onLongPress}> + {notificationCount ? ( + <View style={styles.notificationCount}> + <Text style={styles.notificationCountLabel}>{notificationCount}</Text> + </View> + ) : undefined} + {icon} + </TouchableOpacity> + ) +} + +const styles = StyleSheet.create({ + bottomBar: { + position: 'absolute', + bottom: 0, + left: 0, + right: 0, + flexDirection: 'row', + borderTopWidth: 1, + paddingLeft: 5, + paddingRight: 10, + }, + ctrl: { + flex: 1, + paddingTop: 13, + paddingBottom: 4, + }, + notificationCount: { + position: 'absolute', + left: '52%', + top: 10, + backgroundColor: colors.blue3, + paddingHorizontal: 4, + paddingBottom: 1, + borderRadius: 8, + zIndex: 1, + }, + notificationCountLabel: { + fontSize: 12, + fontWeight: 'bold', + color: colors.white, + }, + ctrlIcon: { + marginLeft: 'auto', + marginRight: 'auto', + }, + ctrlIconSizingWrapper: { + height: 27, + }, + homeIcon: { + top: 0, + }, + searchIcon: { + top: -2, + }, + bellIcon: { + top: -2.5, + }, + profileIcon: { + top: -4, + }, +}) |