From 23e8484986acc2926d795d0406dd3c820ec83a6c Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Sun, 19 Mar 2023 18:11:33 -0500 Subject: Implement pager and tabbar for desktop web --- src/lib/styles.ts | 1 + src/view/com/util/Pager.tsx | 87 ---------------------------- src/view/com/util/TabBar.tsx | 75 ++++++++++++++++-------- src/view/com/util/pager/Pager.tsx | 87 ++++++++++++++++++++++++++++ src/view/com/util/pager/Pager.web.tsx | 65 +++++++++++++++++++++ src/view/screens/Home.tsx | 94 ++++--------------------------- src/view/screens/home/FeedsTabBar.tsx | 72 +++++++++++++++++++++++ src/view/screens/home/FeedsTabBar.web.tsx | 22 ++++++++ 8 files changed, 307 insertions(+), 196 deletions(-) delete mode 100644 src/view/com/util/Pager.tsx create mode 100644 src/view/com/util/pager/Pager.tsx create mode 100644 src/view/com/util/pager/Pager.web.tsx create mode 100644 src/view/screens/home/FeedsTabBar.tsx create mode 100644 src/view/screens/home/FeedsTabBar.web.tsx (limited to 'src') diff --git a/src/lib/styles.ts b/src/lib/styles.ts index 5d7f7f82d..aa255b21f 100644 --- a/src/lib/styles.ts +++ b/src/lib/styles.ts @@ -70,6 +70,7 @@ export const s = StyleSheet.create({ borderRight1: {borderRightWidth: 1}, borderBottom1: {borderBottomWidth: 1}, borderLeft1: {borderLeftWidth: 1}, + hidden: {display: 'none'}, // font weights fw600: {fontWeight: '600'}, diff --git a/src/view/com/util/Pager.tsx b/src/view/com/util/Pager.tsx deleted file mode 100644 index d71cb7f7f..000000000 --- a/src/view/com/util/Pager.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import React from 'react' -import {Animated, View} from 'react-native' -import PagerView, {PagerViewOnPageSelectedEvent} from 'react-native-pager-view' -import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' -import {s} from 'lib/styles' - -export type PageSelectedEvent = PagerViewOnPageSelectedEvent -const AnimatedPagerView = Animated.createAnimatedComponent(PagerView) - -export interface RenderTabBarFnProps { - selectedPage: number - position: Animated.Value - offset: Animated.Value - onSelect?: (index: number) => void -} -export type RenderTabBarFn = (props: RenderTabBarFnProps) => JSX.Element - -interface Props { - tabBarPosition?: 'top' | 'bottom' - initialPage?: number - renderTabBar: RenderTabBarFn - onPageSelected?: (e: PageSelectedEvent) => void -} -export const Pager = ({ - children, - tabBarPosition = 'top', - initialPage = 0, - renderTabBar, - onPageSelected, -}: React.PropsWithChildren) => { - const [selectedPage, setSelectedPage] = React.useState(0) - const position = useAnimatedValue(0) - const offset = useAnimatedValue(0) - const pagerView = React.useRef() - - const onPageSelectedInner = React.useCallback( - (e: PageSelectedEvent) => { - setSelectedPage(e.nativeEvent.position) - onPageSelected?.(e) - }, - [setSelectedPage, onPageSelected], - ) - - const onTabBarSelect = React.useCallback( - (index: number) => { - pagerView.current?.setPage(index) - }, - [pagerView], - ) - - return ( - - {tabBarPosition === 'top' && - renderTabBar({ - selectedPage, - position, - offset, - onSelect: onTabBarSelect, - })} - - {children} - - {tabBarPosition === 'bottom' && - renderTabBar({ - selectedPage, - position, - offset, - onSelect: onTabBarSelect, - })} - - ) -} diff --git a/src/view/com/util/TabBar.tsx b/src/view/com/util/TabBar.tsx index dd8fdcb56..4b67b8a80 100644 --- a/src/view/com/util/TabBar.tsx +++ b/src/view/com/util/TabBar.tsx @@ -7,6 +7,7 @@ import { } from 'react-native' import {Text} from './text/Text' import {usePalette} from 'lib/hooks/usePalette' +import {isDesktopWeb} from 'platform/detection' interface Layout { x: number @@ -46,8 +47,9 @@ export function TabBar({ const indicatorStyle = { backgroundColor: indicatorColor || pal.colors.link, - bottom: indicatorPosition === 'bottom' ? -1 : undefined, - top: indicatorPosition === 'top' ? -1 : undefined, + bottom: + indicatorPosition === 'bottom' ? (isDesktopWeb ? 0 : -1) : undefined, + top: indicatorPosition === 'top' ? (isDesktopWeb ? 0 : -1) : undefined, transform: [ { translateX: panX.interpolate({ @@ -112,26 +114,49 @@ export function TabBar({ ) } -const styles = StyleSheet.create({ - outer: { - flexDirection: 'row', - paddingHorizontal: 14, - }, - itemTop: { - paddingTop: 10, - paddingBottom: 10, - marginRight: 24, - }, - itemBottom: { - paddingTop: 8, - paddingBottom: 12, - marginRight: 24, - }, - indicator: { - position: 'absolute', - left: 0, - width: 1, - height: 3, - borderRadius: 4, - }, -}) +const styles = isDesktopWeb + ? StyleSheet.create({ + outer: { + flexDirection: 'row', + paddingHorizontal: 18, + }, + itemTop: { + paddingTop: 16, + paddingBottom: 14, + marginRight: 24, + }, + itemBottom: { + paddingTop: 14, + paddingBottom: 16, + marginRight: 24, + }, + indicator: { + position: 'absolute', + left: 0, + width: 1, + height: 3, + }, + }) + : StyleSheet.create({ + outer: { + flexDirection: 'row', + paddingHorizontal: 14, + }, + itemTop: { + paddingTop: 10, + paddingBottom: 10, + marginRight: 24, + }, + itemBottom: { + paddingTop: 8, + paddingBottom: 12, + marginRight: 24, + }, + indicator: { + position: 'absolute', + left: 0, + width: 1, + height: 3, + borderRadius: 4, + }, + }) diff --git a/src/view/com/util/pager/Pager.tsx b/src/view/com/util/pager/Pager.tsx new file mode 100644 index 000000000..416828a27 --- /dev/null +++ b/src/view/com/util/pager/Pager.tsx @@ -0,0 +1,87 @@ +import React from 'react' +import {Animated, View} from 'react-native' +import PagerView, {PagerViewOnPageSelectedEvent} from 'react-native-pager-view' +import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' +import {s} from 'lib/styles' + +export type PageSelectedEvent = PagerViewOnPageSelectedEvent +const AnimatedPagerView = Animated.createAnimatedComponent(PagerView) + +export interface RenderTabBarFnProps { + selectedPage: number + position: Animated.Value + offset: Animated.Value + onSelect?: (index: number) => void +} +export type RenderTabBarFn = (props: RenderTabBarFnProps) => JSX.Element + +interface Props { + tabBarPosition?: 'top' | 'bottom' + initialPage?: number + renderTabBar: RenderTabBarFn + onPageSelected?: (index: number) => void +} +export const Pager = ({ + children, + tabBarPosition = 'top', + initialPage = 0, + renderTabBar, + onPageSelected, +}: React.PropsWithChildren) => { + const [selectedPage, setSelectedPage] = React.useState(0) + const position = useAnimatedValue(0) + const offset = useAnimatedValue(0) + const pagerView = React.useRef() + + const onPageSelectedInner = React.useCallback( + (e: PageSelectedEvent) => { + setSelectedPage(e.nativeEvent.position) + onPageSelected?.(e.nativeEvent.position) + }, + [setSelectedPage, onPageSelected], + ) + + const onTabBarSelect = React.useCallback( + (index: number) => { + pagerView.current?.setPage(index) + }, + [pagerView], + ) + + return ( + + {tabBarPosition === 'top' && + renderTabBar({ + selectedPage, + position, + offset, + onSelect: onTabBarSelect, + })} + + {children} + + {tabBarPosition === 'bottom' && + renderTabBar({ + selectedPage, + position, + offset, + onSelect: onTabBarSelect, + })} + + ) +} diff --git a/src/view/com/util/pager/Pager.web.tsx b/src/view/com/util/pager/Pager.web.tsx new file mode 100644 index 000000000..d50100de9 --- /dev/null +++ b/src/view/com/util/pager/Pager.web.tsx @@ -0,0 +1,65 @@ +import React from 'react' +import {Animated, View} from 'react-native' +import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' +import {s} from 'lib/styles' + +export interface RenderTabBarFnProps { + selectedPage: number + position: Animated.Value + offset: Animated.Value + onSelect?: (index: number) => void +} +export type RenderTabBarFn = (props: RenderTabBarFnProps) => JSX.Element + +interface Props { + tabBarPosition?: 'top' | 'bottom' + initialPage?: number + renderTabBar: RenderTabBarFn + onPageSelected?: (index: number) => void +} +export const Pager = ({ + children, + tabBarPosition = 'top', + initialPage = 0, + renderTabBar, + onPageSelected, +}: React.PropsWithChildren) => { + const [selectedPage, setSelectedPage] = React.useState(initialPage) + const position = useAnimatedValue(0) + const offset = useAnimatedValue(0) + + const onTabBarSelect = React.useCallback( + (index: number) => { + setSelectedPage(index) + onPageSelected?.(index) + Animated.timing(position, { + toValue: index, + duration: 200, + useNativeDriver: true, + }).start() + }, + [setSelectedPage, onPageSelected, position], + ) + + return ( + + {tabBarPosition === 'top' && + renderTabBar({ + selectedPage, + position, + offset, + onSelect: onTabBarSelect, + })} + {children.map((child, i) => ( + {child} + ))} + {tabBarPosition === 'bottom' && + renderTabBar({ + selectedPage, + position, + offset, + onSelect: onTabBarSelect, + })} + + ) +} diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx index 822174446..4950bc0fd 100644 --- a/src/view/screens/Home.tsx +++ b/src/view/screens/Home.tsx @@ -1,11 +1,5 @@ import React from 'react' -import { - Animated, - FlatList, - StyleSheet, - View, - useWindowDimensions, -} from 'react-native' +import {FlatList, View, useWindowDimensions} from 'react-native' import {useFocusEffect, useIsFocused} from '@react-navigation/native' import {observer} from 'mobx-react-lite' import useAppState from 'react-native-appstate-hook' @@ -15,25 +9,17 @@ import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {Feed} from '../com/posts/Feed' import {FollowingEmptyState} from 'view/com/posts/FollowingEmptyState' import {LoadLatestBtn} from '../com/util/LoadLatestBtn' -import {TabBar} from 'view/com/util/TabBar' -import { - Pager, - PageSelectedEvent, - RenderTabBarFnProps, -} from 'view/com/util/Pager' +import {FeedsTabBar} from './home/FeedsTabBar' +import {Pager, RenderTabBarFnProps} from 'view/com/util/pager/Pager' import {FAB} from '../com/util/FAB' import {useStores} from 'state/index' -import {usePalette} from 'lib/hooks/usePalette' import {s} from 'lib/styles' import {useOnMainScroll} from 'lib/hooks/useOnMainScroll' -import {useSafeAreaInsets} from 'react-native-safe-area-context' -import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' import {useAnalytics} from 'lib/analytics' import {ComposeIcon2} from 'lib/icons' -import {clamp} from 'lodash' +import {isDesktopWeb} from 'platform/detection' const TAB_BAR_HEIGHT = 82 -const BOTTOM_BAR_HEIGHT = 48 type Props = NativeStackScreenProps export const HomeScreen = withAuthRequired((_opts: Props) => { @@ -56,9 +42,9 @@ export const HomeScreen = withAuthRequired((_opts: Props) => { ) const onPageSelected = React.useCallback( - (e: PageSelectedEvent) => { - setSelectedPage(e.nativeEvent.position) - store.shell.setIsDrawerSwipeDisabled(e.nativeEvent.position > 0) + (index: number) => { + setSelectedPage(index) + store.shell.setIsDrawerSwipeDisabled(index > 0) }, [store], ) @@ -69,7 +55,7 @@ export const HomeScreen = withAuthRequired((_opts: Props) => { const renderTabBar = React.useCallback( (props: RenderTabBarFnProps) => { - return + return }, [onPressSelected], ) @@ -83,7 +69,7 @@ export const HomeScreen = withAuthRequired((_opts: Props) => { { ) }) -const FloatingTabBar = observer( - (props: RenderTabBarFnProps & {onPressSelected: () => void}) => { - const store = useStores() - const safeAreaInsets = useSafeAreaInsets() - const pal = usePalette('default') - const interp = useAnimatedValue(0) - - const pad = React.useMemo( - () => ({ - paddingBottom: clamp(safeAreaInsets.bottom, 15, 20), - }), - [safeAreaInsets], - ) - - React.useEffect(() => { - Animated.timing(interp, { - toValue: store.shell.minimalShellMode ? 0 : 1, - duration: 100, - useNativeDriver: true, - isInteraction: false, - }).start() - }, [interp, store.shell.minimalShellMode]) - const transform = { - transform: [ - {translateY: Animated.multiply(interp, -1 * BOTTOM_BAR_HEIGHT)}, - ], - } - - return ( - - - - ) - }, -) - const FeedPage = observer( ({ isPageFocused, @@ -158,7 +102,7 @@ const FeedPage = observer( const isScreenFocused = useIsFocused() const winDim = useWindowDimensions() const containerStyle = React.useMemo( - () => ({height: winDim.height - TAB_BAR_HEIGHT}), + () => ({height: winDim.height - (isDesktopWeb ? 0 : TAB_BAR_HEIGHT)}), [winDim], ) @@ -249,21 +193,3 @@ const FeedPage = observer( ) }, ) - -const styles = StyleSheet.create({ - tabBar: { - position: 'absolute', - left: 0, - right: 0, - bottom: 0, - flexDirection: 'row', - alignItems: 'center', - paddingHorizontal: 10, - borderTopWidth: 1, - paddingTop: 0, - paddingBottom: 30, - }, - tabBarAvi: { - marginRight: 4, - }, -}) diff --git a/src/view/screens/home/FeedsTabBar.tsx b/src/view/screens/home/FeedsTabBar.tsx new file mode 100644 index 000000000..d34034103 --- /dev/null +++ b/src/view/screens/home/FeedsTabBar.tsx @@ -0,0 +1,72 @@ +import React from 'react' +import {Animated, StyleSheet} from 'react-native' +import {observer} from 'mobx-react-lite' +import {TabBar} from 'view/com/util/TabBar' +import {RenderTabBarFnProps} from 'view/com/util/pager/Pager' +import {useStores} from 'state/index' +import {usePalette} from 'lib/hooks/usePalette' +import {useSafeAreaInsets} from 'react-native-safe-area-context' +import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' +import {clamp} from 'lodash' + +const BOTTOM_BAR_HEIGHT = 48 + +export const FeedsTabBar = observer( + (props: RenderTabBarFnProps & {onPressSelected: () => void}) => { + const store = useStores() + const safeAreaInsets = useSafeAreaInsets() + const pal = usePalette('default') + const interp = useAnimatedValue(0) + + const pad = React.useMemo( + () => ({ + paddingBottom: clamp(safeAreaInsets.bottom, 15, 20), + }), + [safeAreaInsets], + ) + + React.useEffect(() => { + Animated.timing(interp, { + toValue: store.shell.minimalShellMode ? 0 : 1, + duration: 100, + useNativeDriver: true, + isInteraction: false, + }).start() + }, [interp, store.shell.minimalShellMode]) + const transform = { + transform: [ + {translateY: Animated.multiply(interp, -1 * BOTTOM_BAR_HEIGHT)}, + ], + } + + return ( + + + + ) + }, +) + +const styles = StyleSheet.create({ + tabBar: { + position: 'absolute', + left: 0, + right: 0, + bottom: 0, + flexDirection: 'row', + alignItems: 'center', + paddingHorizontal: 10, + borderTopWidth: 1, + paddingTop: 0, + paddingBottom: 30, + }, + tabBarAvi: { + marginRight: 4, + }, +}) diff --git a/src/view/screens/home/FeedsTabBar.web.tsx b/src/view/screens/home/FeedsTabBar.web.tsx new file mode 100644 index 000000000..59ea42988 --- /dev/null +++ b/src/view/screens/home/FeedsTabBar.web.tsx @@ -0,0 +1,22 @@ +import React from 'react' +import {observer} from 'mobx-react-lite' +import {TabBar} from 'view/com/util/TabBar' +import {CenteredView} from 'view/com/util/Views' +import {RenderTabBarFnProps} from 'view/com/util/pager/Pager' +import {usePalette} from 'lib/hooks/usePalette' + +export const FeedsTabBar = observer( + (props: RenderTabBarFnProps & {onPressSelected: () => void}) => { + const pal = usePalette('default') + return ( + + + + ) + }, +) -- cgit 1.4.1