about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorSamuel Newman <mozzius@protonmail.com>2024-01-24 04:22:22 +0000
committerGitHub <noreply@github.com>2024-01-23 20:22:22 -0800
commit94f39bb3eb26a6ba2ae7f3eb72d78154006e2fca (patch)
tree0d26b3ee5bb1bf420284c3405e5d3f74a63cc322 /src
parent382383ca87664e1a3f03e0d8a401c1be850a13cf (diff)
downloadvoidsky-94f39bb3eb26a6ba2ae7f3eb72d78154006e2fca.tar.zst
Scroll feed search bar to top of screen on focus (#2602)
* Scroll feed search bar to top on focus

* update comment
Diffstat (limited to 'src')
-rw-r--r--src/view/screens/Feeds.tsx37
1 files changed, 36 insertions, 1 deletions
diff --git a/src/view/screens/Feeds.tsx b/src/view/screens/Feeds.tsx
index a913364d4..9b3fd6181 100644
--- a/src/view/screens/Feeds.tsx
+++ b/src/view/screens/Feeds.tsx
@@ -1,5 +1,5 @@
 import React from 'react'
-import {ActivityIndicator, StyleSheet, View} from 'react-native'
+import {ActivityIndicator, StyleSheet, View, type FlatList} from 'react-native'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
 import {ViewHeader} from 'view/com/util/ViewHeader'
@@ -34,6 +34,7 @@ import {
 import {cleanError} from 'lib/strings/errors'
 import {useComposerControls} from '#/state/shell/composer'
 import {useSession} from '#/state/session'
+import {isNative} from '#/platform/detection'
 
 type Props = NativeStackScreenProps<FeedsTabNavigatorParams, 'Feeds'>
 
@@ -118,6 +119,7 @@ export function FeedsScreen(_props: Props) {
     error: searchError,
   } = useSearchPopularFeedsMutation()
   const {hasSession} = useSession()
+  const listRef = React.useRef<FlatList>(null)
 
   /**
    * A search query is present. We may not have search results yet.
@@ -338,6 +340,35 @@ export function FeedsScreen(_props: Props) {
     )
   }, [pal, _])
 
+  const searchBarIndex = items.findIndex(
+    item => item.type === 'popularFeedsHeader',
+  )
+
+  const onChangeSearchFocus = React.useCallback(
+    (focus: boolean) => {
+      if (focus && searchBarIndex > -1) {
+        if (isNative) {
+          // scrollToIndex scrolls the exact right amount, so use if available
+          listRef.current?.scrollToIndex({
+            index: searchBarIndex,
+            animated: true,
+          })
+        } else {
+          // web implementation only supports scrollToOffset
+          // thus, we calculate the offset based on the index
+          // pixel values are estimates, I wasn't able to get it pixel perfect :(
+          const headerHeight = isMobile ? 43 : 53
+          const feedItemHeight = isMobile ? 49 : 58
+          listRef.current?.scrollToOffset({
+            offset: searchBarIndex * feedItemHeight - headerHeight,
+            animated: true,
+          })
+        }
+      }
+    },
+    [searchBarIndex, isMobile],
+  )
+
   const renderItem = React.useCallback(
     ({item}: {item: FlatlistSlice}) => {
       if (item.type === 'error') {
@@ -415,6 +446,7 @@ export function FeedsScreen(_props: Props) {
                   onChangeQuery={onChangeQuery}
                   onPressCancelSearch={onPressCancelSearch}
                   onSubmitQuery={onSubmitQuery}
+                  setIsInputFocused={onChangeSearchFocus}
                   style={{flex: 1, maxWidth: 250}}
                 />
               )}
@@ -427,6 +459,7 @@ export function FeedsScreen(_props: Props) {
                   onChangeQuery={onChangeQuery}
                   onPressCancelSearch={onPressCancelSearch}
                   onSubmitQuery={onSubmitQuery}
+                  setIsInputFocused={onChangeSearchFocus}
                 />
               </View>
             )}
@@ -469,6 +502,7 @@ export function FeedsScreen(_props: Props) {
       onChangeQuery,
       onPressCancelSearch,
       onSubmitQuery,
+      onChangeSearchFocus,
     ],
   )
 
@@ -486,6 +520,7 @@ export function FeedsScreen(_props: Props) {
       {preferences ? <View /> : <ActivityIndicator />}
 
       <List
+        ref={listRef}
         style={[!isTabletOrDesktop && s.flex1, styles.list]}
         data={items}
         keyExtractor={item => item.key}