about summary refs log tree commit diff
path: root/src/view
diff options
context:
space:
mode:
Diffstat (limited to 'src/view')
-rw-r--r--src/view/com/pager/Pager.tsx70
-rw-r--r--src/view/com/pager/Pager.web.tsx20
-rw-r--r--src/view/com/pager/PagerWithHeader.tsx5
-rw-r--r--src/view/com/pager/PagerWithHeader.web.tsx5
-rw-r--r--src/view/com/pager/TabBar.tsx102
-rw-r--r--src/view/com/pager/TabBar.web.tsx192
-rw-r--r--src/view/screens/Home.tsx15
7 files changed, 216 insertions, 193 deletions
diff --git a/src/view/com/pager/Pager.tsx b/src/view/com/pager/Pager.tsx
index de0409991..f0e686b6a 100644
--- a/src/view/com/pager/Pager.tsx
+++ b/src/view/com/pager/Pager.tsx
@@ -1,21 +1,16 @@
 import React, {forwardRef} from 'react'
 import {View} from 'react-native'
 import PagerView, {
-  PagerViewOnPageScrollEvent,
   PagerViewOnPageSelectedEvent,
   PageScrollStateChangedNativeEvent,
 } from 'react-native-pager-view'
 
-import {LogEvents} from '#/lib/statsig/events'
 import {atoms as a, native} from '#/alf'
 
 export type PageSelectedEvent = PagerViewOnPageSelectedEvent
 
 export interface PagerRef {
-  setPage: (
-    index: number,
-    reason: LogEvents['home:feedDisplayed']['reason'],
-  ) => void
+  setPage: (index: number) => void
 }
 
 export interface RenderTabBarFnProps {
@@ -29,10 +24,6 @@ interface Props {
   initialPage?: number
   renderTabBar: RenderTabBarFn
   onPageSelected?: (index: number) => void
-  onPageSelecting?: (
-    index: number,
-    reason: LogEvents['home:feedDisplayed']['reason'],
-  ) => void
   onPageScrollStateChanged?: (
     scrollState: 'idle' | 'dragging' | 'settling',
   ) => void
@@ -46,24 +37,16 @@ export const Pager = forwardRef<PagerRef, React.PropsWithChildren<Props>>(
       renderTabBar,
       onPageScrollStateChanged,
       onPageSelected,
-      onPageSelecting,
       testID,
     }: React.PropsWithChildren<Props>,
     ref,
   ) {
     const [selectedPage, setSelectedPage] = React.useState(0)
-    const lastOffset = React.useRef(0)
-    const lastDirection = React.useRef(0)
-    const scrollState = React.useRef('')
     const pagerView = React.useRef<PagerView>(null)
 
     React.useImperativeHandle(ref, () => ({
-      setPage: (
-        index: number,
-        reason: LogEvents['home:feedDisplayed']['reason'],
-      ) => {
+      setPage: (index: number) => {
         pagerView.current?.setPage(index)
-        onPageSelecting?.(index, reason)
       },
     }))
 
@@ -75,60 +58,18 @@ export const Pager = forwardRef<PagerRef, React.PropsWithChildren<Props>>(
       [setSelectedPage, onPageSelected],
     )
 
-    const onPageScroll = React.useCallback(
-      (e: PagerViewOnPageScrollEvent) => {
-        const {position, offset} = e.nativeEvent
-        if (offset === 0) {
-          // offset hits 0 in some awkward spots so we ignore it
-          return
-        }
-        // NOTE
-        // we want to call `onPageSelecting` as soon as the scroll-gesture
-        // enters the "settling" phase, which means the user has released it
-        // we can't infer directionality from the scroll information, so we
-        // track the offset changes. if the offset delta is consistent with
-        // the existing direction during the settling phase, we can say for
-        // certain where it's going and can fire
-        // -prf
-        if (scrollState.current === 'settling') {
-          if (lastDirection.current === -1 && offset < lastOffset.current) {
-            onPageSelecting?.(position, 'pager-swipe')
-            setSelectedPage(position)
-            lastDirection.current = 0
-          } else if (
-            lastDirection.current === 1 &&
-            offset > lastOffset.current
-          ) {
-            onPageSelecting?.(position + 1, 'pager-swipe')
-            setSelectedPage(position + 1)
-            lastDirection.current = 0
-          }
-        } else {
-          if (offset < lastOffset.current) {
-            lastDirection.current = -1
-          } else if (offset > lastOffset.current) {
-            lastDirection.current = 1
-          }
-        }
-        lastOffset.current = offset
-      },
-      [lastOffset, lastDirection, onPageSelecting],
-    )
-
     const handlePageScrollStateChanged = React.useCallback(
       (e: PageScrollStateChangedNativeEvent) => {
-        scrollState.current = e.nativeEvent.pageScrollState
         onPageScrollStateChanged?.(e.nativeEvent.pageScrollState)
       },
-      [scrollState, onPageScrollStateChanged],
+      [onPageScrollStateChanged],
     )
 
     const onTabBarSelect = React.useCallback(
       (index: number) => {
         pagerView.current?.setPage(index)
-        onPageSelecting?.(index, 'tabbar-click')
       },
-      [pagerView, onPageSelecting],
+      [pagerView],
     )
 
     return (
@@ -142,8 +83,7 @@ export const Pager = forwardRef<PagerRef, React.PropsWithChildren<Props>>(
           style={[a.flex_1]}
           initialPage={initialPage}
           onPageScrollStateChanged={handlePageScrollStateChanged}
-          onPageSelected={onPageSelectedInner}
-          onPageScroll={onPageScroll}>
+          onPageSelected={onPageSelectedInner}>
           {children}
         </PagerView>
       </View>
diff --git a/src/view/com/pager/Pager.web.tsx b/src/view/com/pager/Pager.web.tsx
index e6909fe10..c620e73e3 100644
--- a/src/view/com/pager/Pager.web.tsx
+++ b/src/view/com/pager/Pager.web.tsx
@@ -2,7 +2,6 @@ import React from 'react'
 import {View} from 'react-native'
 import {flushSync} from 'react-dom'
 
-import {LogEvents} from '#/lib/statsig/events'
 import {s} from '#/lib/styles'
 
 export interface RenderTabBarFnProps {
@@ -16,10 +15,6 @@ interface Props {
   initialPage?: number
   renderTabBar: RenderTabBarFn
   onPageSelected?: (index: number) => void
-  onPageSelecting?: (
-    index: number,
-    reason: LogEvents['home:feedDisplayed']['reason'],
-  ) => void
 }
 export const Pager = React.forwardRef(function PagerImpl(
   {
@@ -27,7 +22,6 @@ export const Pager = React.forwardRef(function PagerImpl(
     initialPage = 0,
     renderTabBar,
     onPageSelected,
-    onPageSelecting,
   }: React.PropsWithChildren<Props>,
   ref,
 ) {
@@ -36,16 +30,13 @@ export const Pager = React.forwardRef(function PagerImpl(
   const anchorRef = React.useRef(null)
 
   React.useImperativeHandle(ref, () => ({
-    setPage: (
-      index: number,
-      reason: LogEvents['home:feedDisplayed']['reason'],
-    ) => {
-      onTabBarSelect(index, reason)
+    setPage: (index: number) => {
+      onTabBarSelect(index)
     },
   }))
 
   const onTabBarSelect = React.useCallback(
-    (index: number, reason: LogEvents['home:feedDisplayed']['reason']) => {
+    (index: number) => {
       const scrollY = window.scrollY
       // We want to determine if the tabbar is already "sticking" at the top (in which
       // case we should preserve and restore scroll), or if it is somewhere below in the
@@ -64,7 +55,6 @@ export const Pager = React.forwardRef(function PagerImpl(
       flushSync(() => {
         setSelectedPage(index)
         onPageSelected?.(index)
-        onPageSelecting?.(index, reason)
       })
       if (isSticking) {
         const restoredScrollY = scrollYs.current[index]
@@ -75,7 +65,7 @@ export const Pager = React.forwardRef(function PagerImpl(
         }
       }
     },
-    [selectedPage, setSelectedPage, onPageSelected, onPageSelecting],
+    [selectedPage, setSelectedPage, onPageSelected],
   )
 
   return (
@@ -83,7 +73,7 @@ export const Pager = React.forwardRef(function PagerImpl(
       {renderTabBar({
         selectedPage,
         tabBarAnchor: <View ref={anchorRef} />,
-        onSelect: e => onTabBarSelect(e, 'tabbar-click'),
+        onSelect: e => onTabBarSelect(e),
       })}
       {React.Children.map(children, (child, i) => (
         <View style={selectedPage === i ? s.flex1 : s.hidden} key={`page-${i}`}>
diff --git a/src/view/com/pager/PagerWithHeader.tsx b/src/view/com/pager/PagerWithHeader.tsx
index 92b98dc2e..1aa45ffba 100644
--- a/src/view/com/pager/PagerWithHeader.tsx
+++ b/src/view/com/pager/PagerWithHeader.tsx
@@ -182,17 +182,12 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
       [onPageSelected, setCurrentPage],
     )
 
-    const onPageSelecting = React.useCallback((index: number) => {
-      setCurrentPage(index)
-    }, [])
-
     return (
       <Pager
         ref={ref}
         testID={testID}
         initialPage={initialPage}
         onPageSelected={onPageSelectedInner}
-        onPageSelecting={onPageSelecting}
         renderTabBar={renderTabBar}>
         {toArray(children)
           .filter(Boolean)
diff --git a/src/view/com/pager/PagerWithHeader.web.tsx b/src/view/com/pager/PagerWithHeader.web.tsx
index e72c1f3cc..dd0026405 100644
--- a/src/view/com/pager/PagerWithHeader.web.tsx
+++ b/src/view/com/pager/PagerWithHeader.web.tsx
@@ -75,17 +75,12 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
       [onPageSelected, setCurrentPage],
     )
 
-    const onPageSelecting = React.useCallback((index: number) => {
-      setCurrentPage(index)
-    }, [])
-
     return (
       <Pager
         ref={ref}
         testID={testID}
         initialPage={initialPage}
         onPageSelected={onPageSelectedInner}
-        onPageSelecting={onPageSelecting}
         renderTabBar={renderTabBar}>
         {toArray(children)
           .filter(Boolean)
diff --git a/src/view/com/pager/TabBar.tsx b/src/view/com/pager/TabBar.tsx
index 4e8646c60..3f453971c 100644
--- a/src/view/com/pager/TabBar.tsx
+++ b/src/view/com/pager/TabBar.tsx
@@ -2,11 +2,8 @@ import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'
 import {LayoutChangeEvent, ScrollView, StyleSheet, View} from 'react-native'
 
 import {usePalette} from '#/lib/hooks/usePalette'
-import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
-import {isNative} from '#/platform/detection'
 import {PressableWithHover} from '../util/PressableWithHover'
 import {Text} from '../util/text/Text'
-import {DraggableScrollView} from './DraggableScrollView'
 
 export interface TabBarProps {
   testID?: string
@@ -31,68 +28,20 @@ export function TabBar({
 }: TabBarProps) {
   const pal = usePalette('default')
   const scrollElRef = useRef<ScrollView>(null)
-  const itemRefs = useRef<Array<Element>>([])
   const [itemXs, setItemXs] = useState<number[]>([])
   const indicatorStyle = useMemo(
     () => ({borderBottomColor: indicatorColor || pal.colors.link}),
     [indicatorColor, pal],
   )
-  const {isDesktop, isTablet} = useWebMediaQueries()
-  const styles = isDesktop || isTablet ? desktopStyles : mobileStyles
 
   useEffect(() => {
-    if (isNative) {
-      // On native, the primary interaction is swiping.
-      // We adjust the scroll little by little on every tab change.
-      // Scroll into view but keep the end of the previous item visible.
-      let x = itemXs[selectedPage] || 0
-      x = Math.max(0, x - OFFSCREEN_ITEM_WIDTH)
-      scrollElRef.current?.scrollTo({x})
-    } else {
-      // On the web, the primary interaction is tapping.
-      // Scrolling under tap feels disorienting so only adjust the scroll offset
-      // when tapping on an item out of view--and we adjust by almost an entire page.
-      const parent = scrollElRef?.current?.getScrollableNode?.()
-      if (!parent) {
-        return
-      }
-      const parentRect = parent.getBoundingClientRect()
-      if (!parentRect) {
-        return
-      }
-      const {
-        left: parentLeft,
-        right: parentRight,
-        width: parentWidth,
-      } = parentRect
-      const child = itemRefs.current[selectedPage]
-      if (!child) {
-        return
-      }
-      const childRect = child.getBoundingClientRect?.()
-      if (!childRect) {
-        return
-      }
-      const {left: childLeft, right: childRight, width: childWidth} = childRect
-      let dx = 0
-      if (childRight >= parentRight) {
-        dx += childRight - parentRight
-        dx += parentWidth - childWidth - OFFSCREEN_ITEM_WIDTH
-      } else if (childLeft <= parentLeft) {
-        dx -= parentLeft - childLeft
-        dx -= parentWidth - childWidth - OFFSCREEN_ITEM_WIDTH
-      }
-      let x = parent.scrollLeft + dx
-      x = Math.max(0, x)
-      x = Math.min(x, parent.scrollWidth - parentWidth)
-      if (dx !== 0) {
-        parent.scroll({
-          left: x,
-          behavior: 'smooth',
-        })
-      }
-    }
-  }, [scrollElRef, itemXs, selectedPage, styles])
+    // On native, the primary interaction is swiping.
+    // We adjust the scroll little by little on every tab change.
+    // Scroll into view but keep the end of the previous item visible.
+    let x = itemXs[selectedPage] || 0
+    x = Math.max(0, x - OFFSCREEN_ITEM_WIDTH)
+    scrollElRef.current?.scrollTo({x})
+  }, [scrollElRef, itemXs, selectedPage])
 
   const onPressItem = useCallback(
     (index: number) => {
@@ -122,7 +71,7 @@ export function TabBar({
       testID={testID}
       style={[pal.view, styles.outer]}
       accessibilityRole="tablist">
-      <DraggableScrollView
+      <ScrollView
         testID={`${testID}-selector`}
         horizontal={true}
         showsHorizontalScrollIndicator={false}
@@ -134,7 +83,6 @@ export function TabBar({
             <PressableWithHover
               testID={`${testID}-selector-${i}`}
               key={`${item}-${i}`}
-              ref={node => (itemRefs.current[i] = node as any)}
               onLayout={e => onItemLayout(e, i)}
               style={styles.item}
               hoverStyle={pal.viewLight}
@@ -143,7 +91,7 @@ export function TabBar({
               <View style={[styles.itemInner, selected && indicatorStyle]}>
                 <Text
                   emoji
-                  type={isDesktop || isTablet ? 'xl-bold' : 'lg-bold'}
+                  type="lg-bold"
                   testID={testID ? `${testID}-${item}` : undefined}
                   style={[
                     selected ? pal.text : pal.textLight,
@@ -155,41 +103,13 @@ export function TabBar({
             </PressableWithHover>
           )
         })}
-      </DraggableScrollView>
+      </ScrollView>
       <View style={[pal.border, styles.outerBottomBorder]} />
     </View>
   )
 }
 
-const desktopStyles = StyleSheet.create({
-  outer: {
-    flexDirection: 'row',
-    width: 598,
-  },
-  contentContainer: {
-    paddingHorizontal: 0,
-    backgroundColor: 'transparent',
-  },
-  item: {
-    paddingTop: 14,
-    paddingHorizontal: 14,
-    justifyContent: 'center',
-  },
-  itemInner: {
-    paddingBottom: 12,
-    borderBottomWidth: 3,
-    borderBottomColor: 'transparent',
-  },
-  outerBottomBorder: {
-    position: 'absolute',
-    left: 0,
-    right: 0,
-    top: '100%',
-    borderBottomWidth: StyleSheet.hairlineWidth,
-  },
-})
-
-const mobileStyles = StyleSheet.create({
+const styles = StyleSheet.create({
   outer: {
     flexDirection: 'row',
   },
diff --git a/src/view/com/pager/TabBar.web.tsx b/src/view/com/pager/TabBar.web.tsx
new file mode 100644
index 000000000..4291a053b
--- /dev/null
+++ b/src/view/com/pager/TabBar.web.tsx
@@ -0,0 +1,192 @@
+import {useCallback, useEffect, useMemo, useRef} from 'react'
+import {ScrollView, StyleSheet, View} from 'react-native'
+
+import {usePalette} from '#/lib/hooks/usePalette'
+import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
+import {PressableWithHover} from '../util/PressableWithHover'
+import {Text} from '../util/text/Text'
+import {DraggableScrollView} from './DraggableScrollView'
+
+export interface TabBarProps {
+  testID?: string
+  selectedPage: number
+  items: string[]
+  indicatorColor?: string
+  onSelect?: (index: number) => void
+  onPressSelected?: (index: number) => void
+}
+
+// How much of the previous/next item we're showing
+// to give the user a hint there's more to scroll.
+const OFFSCREEN_ITEM_WIDTH = 20
+
+export function TabBar({
+  testID,
+  selectedPage,
+  items,
+  indicatorColor,
+  onSelect,
+  onPressSelected,
+}: TabBarProps) {
+  const pal = usePalette('default')
+  const scrollElRef = useRef<ScrollView>(null)
+  const itemRefs = useRef<Array<Element>>([])
+  const indicatorStyle = useMemo(
+    () => ({borderBottomColor: indicatorColor || pal.colors.link}),
+    [indicatorColor, pal],
+  )
+  const {isDesktop, isTablet} = useWebMediaQueries()
+  const styles = isDesktop || isTablet ? desktopStyles : mobileStyles
+
+  useEffect(() => {
+    // On the web, the primary interaction is tapping.
+    // Scrolling under tap feels disorienting so only adjust the scroll offset
+    // when tapping on an item out of view--and we adjust by almost an entire page.
+    const parent = scrollElRef?.current?.getScrollableNode?.()
+    if (!parent) {
+      return
+    }
+    const parentRect = parent.getBoundingClientRect()
+    if (!parentRect) {
+      return
+    }
+    const {
+      left: parentLeft,
+      right: parentRight,
+      width: parentWidth,
+    } = parentRect
+    const child = itemRefs.current[selectedPage]
+    if (!child) {
+      return
+    }
+    const childRect = child.getBoundingClientRect?.()
+    if (!childRect) {
+      return
+    }
+    const {left: childLeft, right: childRight, width: childWidth} = childRect
+    let dx = 0
+    if (childRight >= parentRight) {
+      dx += childRight - parentRight
+      dx += parentWidth - childWidth - OFFSCREEN_ITEM_WIDTH
+    } else if (childLeft <= parentLeft) {
+      dx -= parentLeft - childLeft
+      dx -= parentWidth - childWidth - OFFSCREEN_ITEM_WIDTH
+    }
+    let x = parent.scrollLeft + dx
+    x = Math.max(0, x)
+    x = Math.min(x, parent.scrollWidth - parentWidth)
+    if (dx !== 0) {
+      parent.scroll({
+        left: x,
+        behavior: 'smooth',
+      })
+    }
+  }, [scrollElRef, selectedPage, styles])
+
+  const onPressItem = useCallback(
+    (index: number) => {
+      onSelect?.(index)
+      if (index === selectedPage) {
+        onPressSelected?.(index)
+      }
+    },
+    [onSelect, selectedPage, onPressSelected],
+  )
+
+  return (
+    <View
+      testID={testID}
+      style={[pal.view, styles.outer]}
+      accessibilityRole="tablist">
+      <DraggableScrollView
+        testID={`${testID}-selector`}
+        horizontal={true}
+        showsHorizontalScrollIndicator={false}
+        ref={scrollElRef}
+        contentContainerStyle={styles.contentContainer}>
+        {items.map((item, i) => {
+          const selected = i === selectedPage
+          return (
+            <PressableWithHover
+              testID={`${testID}-selector-${i}`}
+              key={`${item}-${i}`}
+              ref={node => (itemRefs.current[i] = node as any)}
+              style={styles.item}
+              hoverStyle={pal.viewLight}
+              onPress={() => onPressItem(i)}
+              accessibilityRole="tab">
+              <View style={[styles.itemInner, selected && indicatorStyle]}>
+                <Text
+                  emoji
+                  type={isDesktop || isTablet ? 'xl-bold' : 'lg-bold'}
+                  testID={testID ? `${testID}-${item}` : undefined}
+                  style={[
+                    selected ? pal.text : pal.textLight,
+                    {lineHeight: 20},
+                  ]}>
+                  {item}
+                </Text>
+              </View>
+            </PressableWithHover>
+          )
+        })}
+      </DraggableScrollView>
+      <View style={[pal.border, styles.outerBottomBorder]} />
+    </View>
+  )
+}
+
+const desktopStyles = StyleSheet.create({
+  outer: {
+    flexDirection: 'row',
+    width: 598,
+  },
+  contentContainer: {
+    paddingHorizontal: 0,
+    backgroundColor: 'transparent',
+  },
+  item: {
+    paddingTop: 14,
+    paddingHorizontal: 14,
+    justifyContent: 'center',
+  },
+  itemInner: {
+    paddingBottom: 12,
+    borderBottomWidth: 3,
+    borderBottomColor: 'transparent',
+  },
+  outerBottomBorder: {
+    position: 'absolute',
+    left: 0,
+    right: 0,
+    top: '100%',
+    borderBottomWidth: StyleSheet.hairlineWidth,
+  },
+})
+
+const mobileStyles = StyleSheet.create({
+  outer: {
+    flexDirection: 'row',
+  },
+  contentContainer: {
+    backgroundColor: 'transparent',
+    paddingHorizontal: 6,
+  },
+  item: {
+    paddingTop: 10,
+    paddingHorizontal: 10,
+    justifyContent: 'center',
+  },
+  itemInner: {
+    paddingBottom: 10,
+    borderBottomWidth: 3,
+    borderBottomColor: 'transparent',
+  },
+  outerBottomBorder: {
+    position: 'absolute',
+    left: 0,
+    right: 0,
+    top: '100%',
+    borderBottomWidth: StyleSheet.hairlineWidth,
+  },
+})
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
index 91c9ae69a..7bd0b6e57 100644
--- a/src/view/screens/Home.tsx
+++ b/src/view/screens/Home.tsx
@@ -11,7 +11,7 @@ import {
   HomeTabNavigatorParams,
   NativeStackScreenProps,
 } from '#/lib/routes/types'
-import {logEvent, LogEvents} from '#/lib/statsig/statsig'
+import {logEvent} from '#/lib/statsig/statsig'
 import {isWeb} from '#/platform/detection'
 import {emitSoftReset} from '#/state/events'
 import {SavedFeedSourceInfo, usePinnedFeedsInfos} from '#/state/queries/feed'
@@ -121,7 +121,7 @@ function HomeScreenReady({
     // This is supposed to only happen on the web when you use the right nav.
     if (selectedIndex !== lastPagerReportedIndexRef.current) {
       lastPagerReportedIndexRef.current = selectedIndex
-      pagerRef.current?.setPage(selectedIndex, 'desktop-sidebar-click')
+      pagerRef.current?.setPage(selectedIndex)
     }
   }, [selectedIndex])
 
@@ -158,21 +158,13 @@ function HomeScreenReady({
       const feed = allFeeds[index]
       setSelectedFeed(feed)
       lastPagerReportedIndexRef.current = index
-    },
-    [setDrawerSwipeDisabled, setSelectedFeed, setMinimalShellMode, allFeeds],
-  )
-
-  const onPageSelecting = React.useCallback(
-    (index: number, reason: LogEvents['home:feedDisplayed']['reason']) => {
-      const feed = allFeeds[index]
       logEvent('home:feedDisplayed', {
         index,
         feedType: feed.split('|')[0],
         feedUrl: feed,
-        reason,
       })
     },
-    [allFeeds],
+    [setDrawerSwipeDisabled, setSelectedFeed, setMinimalShellMode, allFeeds],
   )
 
   const onPressSelected = React.useCallback(() => {
@@ -228,7 +220,6 @@ function HomeScreenReady({
       ref={pagerRef}
       testID="homeScreen"
       initialPage={selectedIndex}
-      onPageSelecting={onPageSelecting}
       onPageSelected={onPageSelected}
       onPageScrollStateChanged={onPageScrollStateChanged}
       renderTabBar={renderTabBar}>