about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorPaul Frazee <pfrazee@gmail.com>2023-03-17 14:03:16 -0500
committerPaul Frazee <pfrazee@gmail.com>2023-03-17 14:03:16 -0500
commitc3ed0dc44cf36d1f2275735f7c69ac010c5ecff8 (patch)
tree67b5c93c0f5a6852232b6d6cd76426ccc3585bd5 /src
parent244b06c19d57901b1fee04a742396f6c360339d9 (diff)
downloadvoidsky-c3ed0dc44cf36d1f2275735f7c69ac010c5ecff8.tar.zst
Move the feed selector to the footer
Diffstat (limited to 'src')
-rw-r--r--src/view/com/profile/FollowButton.tsx6
-rw-r--r--src/view/com/util/Pager.tsx17
-rw-r--r--src/view/com/util/PostMeta.tsx1
-rw-r--r--src/view/com/util/TabBar.tsx28
-rw-r--r--src/view/screens/Home.tsx87
-rw-r--r--src/view/shell/BottomBar.tsx22
6 files changed, 121 insertions, 40 deletions
diff --git a/src/view/com/profile/FollowButton.tsx b/src/view/com/profile/FollowButton.tsx
index 7a194cee9..5204f5a40 100644
--- a/src/view/com/profile/FollowButton.tsx
+++ b/src/view/com/profile/FollowButton.tsx
@@ -1,16 +1,18 @@
 import React from 'react'
 import {observer} from 'mobx-react-lite'
-import {Button} from '../util/forms/Button'
+import {Button, ButtonType} from '../util/forms/Button'
 import {useStores} from 'state/index'
 import * as apilib from 'lib/api/index'
 import * as Toast from '../util/Toast'
 
 const FollowButton = observer(
   ({
+    type = 'inverted',
     did,
     declarationCid,
     onToggleFollow,
   }: {
+    type?: ButtonType
     did: string
     declarationCid: string
     onToggleFollow?: (v: boolean) => void
@@ -42,7 +44,7 @@ const FollowButton = observer(
 
     return (
       <Button
-        type={isFollowing ? 'default' : 'inverted'}
+        type={isFollowing ? 'default' : type}
         onPress={onToggleFollowInner}
         label={isFollowing ? 'Unfollow' : 'Follow'}
       />
diff --git a/src/view/com/util/Pager.tsx b/src/view/com/util/Pager.tsx
index 9ce5006cd..89ba59e85 100644
--- a/src/view/com/util/Pager.tsx
+++ b/src/view/com/util/Pager.tsx
@@ -15,11 +15,13 @@ export interface TabBarProps {
 }
 
 interface Props {
+  tabBarPosition?: 'top' | 'bottom'
   renderTabBar: (props: TabBarProps) => JSX.Element
   onPageSelected?: (e: PageSelectedEvent) => void
 }
 export const Pager = ({
   children,
+  tabBarPosition = 'top',
   renderTabBar,
   onPageSelected,
 }: React.PropsWithChildren<Props>) => {
@@ -45,7 +47,13 @@ export const Pager = ({
 
   return (
     <View>
-      {renderTabBar({selectedPage, position, offset, onSelect: onTabBarSelect})}
+      {tabBarPosition === 'top' &&
+        renderTabBar({
+          selectedPage,
+          position,
+          offset,
+          onSelect: onTabBarSelect,
+        })}
       <AnimatedPagerView
         ref={pagerView}
         style={s.h100pct}
@@ -64,6 +72,13 @@ export const Pager = ({
         )}>
         {children}
       </AnimatedPagerView>
+      {tabBarPosition === 'bottom' &&
+        renderTabBar({
+          selectedPage,
+          position,
+          offset,
+          onSelect: onTabBarSelect,
+        })}
     </View>
   )
 }
diff --git a/src/view/com/util/PostMeta.tsx b/src/view/com/util/PostMeta.tsx
index 1a36a72e8..c53de5c1f 100644
--- a/src/view/com/util/PostMeta.tsx
+++ b/src/view/com/util/PostMeta.tsx
@@ -77,6 +77,7 @@ export const PostMeta = observer(function (opts: PostMetaOpts) {
 
         <View>
           <FollowButton
+            type="default"
             did={opts.did}
             declarationCid={opts.declarationCid}
             onToggleFollow={onToggleFollow}
diff --git a/src/view/com/util/TabBar.tsx b/src/view/com/util/TabBar.tsx
index 666ad5811..ac1814685 100644
--- a/src/view/com/util/TabBar.tsx
+++ b/src/view/com/util/TabBar.tsx
@@ -18,12 +18,16 @@ export function TabBar({
   items,
   position,
   offset,
+  indicatorPosition = 'bottom',
+  indicatorColor,
   onSelect,
 }: {
   selectedPage: number
   items: string[]
   position: Animated.Value
   offset: Animated.Value
+  indicatorPosition?: 'top' | 'bottom'
+  indicatorColor?: string
   onSelect?: (index: number) => void
 }) {
   const pal = usePalette('default')
@@ -36,8 +40,10 @@ export function TabBar({
   )
   const panX = Animated.add(position, offset)
 
-  const underlineStyle = {
-    backgroundColor: pal.colors.link,
+  const indicatorStyle = {
+    backgroundColor: indicatorColor || pal.colors.link,
+    bottom: indicatorPosition === 'bottom' ? -1 : undefined,
+    top: indicatorPosition === 'top' ? -1 : undefined,
     left: panX.interpolate({
       inputRange: items.map((_item, i) => i),
       outputRange: itemLayouts.map(l => l.x),
@@ -72,12 +78,16 @@ export function TabBar({
 
   return (
     <View style={[pal.view, styles.outer]} onLayout={onLayout}>
-      <Animated.View style={[styles.underline, underlineStyle]} />
+      <Animated.View style={[styles.indicator, indicatorStyle]} />
       {items.map((item, i) => {
         const selected = i === selectedPage
         return (
           <TouchableWithoutFeedback key={i} onPress={() => onPressItem(i)}>
-            <View style={styles.item} ref={itemRefs[i]}>
+            <View
+              style={
+                indicatorPosition === 'top' ? styles.itemTop : styles.itemBottom
+              }
+              ref={itemRefs[i]}>
               <Text type="xl-bold" style={selected ? pal.text : pal.textLight}>
                 {item}
               </Text>
@@ -94,15 +104,19 @@ const styles = StyleSheet.create({
     flexDirection: 'row',
     paddingHorizontal: 14,
   },
-  item: {
+  itemTop: {
+    paddingTop: 10,
+    paddingBottom: 10,
+    marginRight: 24,
+  },
+  itemBottom: {
     paddingTop: 8,
     paddingBottom: 12,
     marginRight: 24,
   },
-  underline: {
+  indicator: {
     position: 'absolute',
     height: 3,
-    bottom: -1,
     borderRadius: 4,
   },
 })
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
index b38e1cc36..390746eb3 100644
--- a/src/view/screens/Home.tsx
+++ b/src/view/screens/Home.tsx
@@ -1,8 +1,8 @@
 import React from 'react'
 import {
+  Animated,
   FlatList,
   StyleSheet,
-  TouchableOpacity,
   View,
   useWindowDimensions,
 } from 'react-native'
@@ -15,7 +15,6 @@ import {withAuthRequired} from 'view/com/auth/withAuthRequired'
 import {Feed} from '../com/posts/Feed'
 import {LoadLatestBtn} from '../com/util/LoadLatestBtn'
 import {WelcomeBanner} from '../com/util/WelcomeBanner'
-import {UserAvatar} from 'view/com/util/UserAvatar'
 import {TabBar} from 'view/com/util/TabBar'
 import {Pager, PageSelectedEvent, TabBarProps} from 'view/com/util/Pager'
 import {FAB} from '../com/util/FAB'
@@ -23,15 +22,18 @@ 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'
 
 const TAB_BAR_HEIGHT = 82
+const BOTTOM_BAR_HEIGHT = 48
 
 type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'>
 export const HomeScreen = withAuthRequired((_opts: Props) => {
   const store = useStores()
-  const pal = usePalette('default')
   const [selectedPage, setSelectedPage] = React.useState(0)
 
   useFocusEffect(
@@ -51,26 +53,15 @@ export const HomeScreen = withAuthRequired((_opts: Props) => {
     [store],
   )
 
-  const onPressAvi = React.useCallback(() => {
-    store.shell.openDrawer()
-  }, [store])
-
-  const renderTabBar = React.useCallback(
-    (props: TabBarProps) => {
-      return (
-        <View style={[pal.view, pal.border, styles.tabBar]}>
-          <TouchableOpacity style={styles.tabBarAvi} onPress={onPressAvi}>
-            <UserAvatar avatar={store.me.avatar} size={32} />
-          </TouchableOpacity>
-          <TabBar items={['Suggested', 'Following']} {...props} />
-        </View>
-      )
-    },
-    [store.me.avatar, pal, onPressAvi],
-  )
+  const renderTabBar = React.useCallback((props: TabBarProps) => {
+    return <FloatingTabBar {...props} />
+  }, [])
 
   return (
-    <Pager onPageSelected={onPageSelected} renderTabBar={renderTabBar}>
+    <Pager
+      onPageSelected={onPageSelected}
+      renderTabBar={renderTabBar}
+      tabBarPosition="bottom">
       <AlgoView key="1" />
       <View key="2">
         <FollowingView />
@@ -79,6 +70,46 @@ export const HomeScreen = withAuthRequired((_opts: Props) => {
   )
 })
 
+const FloatingTabBar = observer((props: TabBarProps) => {
+  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 (
+    <Animated.View
+      style={[pal.view, pal.border, styles.tabBar, pad, transform]}>
+      <TabBar
+        items={['Suggested', 'Following']}
+        {...props}
+        indicatorPosition="top"
+        indicatorColor={pal.colors.link}
+      />
+    </Animated.View>
+  )
+})
+
 const AlgoView = observer(() => {
   const store = useStores()
   const onMainScroll = useOnMainScroll(store)
@@ -270,13 +301,19 @@ const FollowingView = observer(() => {
 
 const styles = StyleSheet.create({
   tabBar: {
+    position: 'absolute',
+    left: 0,
+    right: 0,
+    bottom: 0,
     flexDirection: 'row',
     alignItems: 'center',
-    paddingHorizontal: 18,
-    borderBottomWidth: 1,
+    paddingHorizontal: 8,
+    borderTopWidth: 1,
+    paddingTop: 0,
+    paddingBottom: 30,
+    // height: 100,
   },
   tabBarAvi: {
-    marginRight: 16,
-    paddingBottom: 2,
+    marginRight: 4,
   },
 })
diff --git a/src/view/shell/BottomBar.tsx b/src/view/shell/BottomBar.tsx
index c59d8c675..1cbf2de86 100644
--- a/src/view/shell/BottomBar.tsx
+++ b/src/view/shell/BottomBar.tsx
@@ -34,16 +34,24 @@ export const BottomBar = observer(({navigation}: BottomTabBarProps) => {
   const minimalShellInterp = useAnimatedValue(0)
   const safeAreaInsets = useSafeAreaInsets()
   const {track} = useAnalytics()
-  const {isAtHome, isAtSearch, isAtNotifications} = useNavigationState(
-    state => {
-      return {
+  const {isAtHome, isAtSearch, isAtNotifications, noBorder} =
+    useNavigationState(state => {
+      const res = {
         isAtHome: getTabState(state, 'Home') !== TabState.Outside,
         isAtSearch: getTabState(state, 'Search') !== TabState.Outside,
         isAtNotifications:
           getTabState(state, 'Notifications') !== TabState.Outside,
+        noBorder: getTabState(state, 'Home') === TabState.InsideAtRoot,
       }
-    },
-  )
+      if (!res.isAtHome && !res.isAtNotifications && !res.isAtSearch) {
+        // HACK for some reason useNavigationState will give us pre-hydration results
+        //      and not update after, so we force isAtHome if all came back false
+        //      -prf
+        res.isAtHome = true
+        res.noBorder = true
+      }
+      return res
+    })
 
   React.useEffect(() => {
     if (store.shell.minimalShellMode) {
@@ -99,6 +107,7 @@ export const BottomBar = observer(({navigation}: BottomTabBarProps) => {
     <Animated.View
       style={[
         styles.bottomBar,
+        noBorder && styles.noBorder,
         pal.view,
         pal.border,
         {paddingBottom: clamp(safeAreaInsets.bottom, 15, 30)},
@@ -213,6 +222,9 @@ const styles = StyleSheet.create({
     paddingLeft: 5,
     paddingRight: 10,
   },
+  noBorder: {
+    borderTopWidth: 0,
+  },
   ctrl: {
     flex: 1,
     paddingTop: 13,