about summary refs log tree commit diff
diff options
context:
space:
mode:
authordan <dan.abramov@gmail.com>2023-12-21 22:56:45 +0000
committerGitHub <noreply@github.com>2023-12-21 14:56:45 -0800
commitbc31da47fdcb4c1704c96b0083f8e5429475da4e (patch)
treedef266d74be5554614b53bf097e8d2ce24687615
parent987c543727dd4d816987148ec3ccdb4337d601ac (diff)
downloadvoidsky-bc31da47fdcb4c1704c96b0083f8e5429475da4e.tar.zst
Consolidate List props a bit (#2216)
-rw-r--r--src/view/com/feeds/ProfileFeedgens.tsx23
-rw-r--r--src/view/com/lists/ListMembers.tsx17
-rw-r--r--src/view/com/lists/MyLists.tsx70
-rw-r--r--src/view/com/lists/ProfileLists.tsx23
-rw-r--r--src/view/com/notifications/Feed.tsx14
-rw-r--r--src/view/com/post-thread/PostLikedBy.tsx14
-rw-r--r--src/view/com/post-thread/PostRepostedBy.tsx14
-rw-r--r--src/view/com/post-thread/PostThread.tsx11
-rw-r--r--src/view/com/posts/Feed.tsx17
-rw-r--r--src/view/com/profile/ProfileFollowers.tsx14
-rw-r--r--src/view/com/profile/ProfileFollows.tsx14
-rw-r--r--src/view/com/util/List.tsx45
-rw-r--r--src/view/screens/Feeds.tsx12
-rw-r--r--src/view/screens/Search/Search.tsx12
14 files changed, 123 insertions, 177 deletions
diff --git a/src/view/com/feeds/ProfileFeedgens.tsx b/src/view/com/feeds/ProfileFeedgens.tsx
index ff6505501..8665fbfac 100644
--- a/src/view/com/feeds/ProfileFeedgens.tsx
+++ b/src/view/com/feeds/ProfileFeedgens.tsx
@@ -1,12 +1,5 @@
 import React from 'react'
-import {
-  Dimensions,
-  RefreshControl,
-  StyleProp,
-  StyleSheet,
-  View,
-  ViewStyle,
-} from 'react-native'
+import {Dimensions, StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
 import {useQueryClient} from '@tanstack/react-query'
 import {List, ListRef} from '../util/List'
 import {FeedSourceCardLoaded} from './FeedSourceCard'
@@ -180,22 +173,14 @@ export const ProfileFeedgens = React.forwardRef<
         data={items}
         keyExtractor={(item: any) => item._reactKey || item.uri}
         renderItem={renderItemInner}
-        refreshControl={
-          <RefreshControl
-            refreshing={isPTRing}
-            onRefresh={onRefresh}
-            tintColor={pal.colors.text}
-            titleColor={pal.colors.text}
-            progressViewOffset={headerOffset}
-          />
-        }
+        refreshing={isPTRing}
+        onRefresh={onRefresh}
+        headerOffset={headerOffset}
         contentContainerStyle={{
           minHeight: Dimensions.get('window').height * 1.5,
         }}
-        style={{paddingTop: headerOffset}}
         indicatorStyle={theme.colorScheme === 'dark' ? 'white' : 'black'}
         removeClippedSubviews={true}
-        contentOffset={{x: 0, y: headerOffset * -1}}
         // @ts-ignore our .web version only -prf
         desktopFixedHeight
         onEndReached={onEndReached}
diff --git a/src/view/com/lists/ListMembers.tsx b/src/view/com/lists/ListMembers.tsx
index a31ca4793..2aef7d766 100644
--- a/src/view/com/lists/ListMembers.tsx
+++ b/src/view/com/lists/ListMembers.tsx
@@ -2,7 +2,6 @@ import React from 'react'
 import {
   ActivityIndicator,
   Dimensions,
-  RefreshControl,
   StyleProp,
   View,
   ViewStyle,
@@ -15,7 +14,6 @@ import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
 import {ProfileCard} from '../profile/ProfileCard'
 import {Button} from '../util/forms/Button'
 import {useAnalytics} from 'lib/analytics/analytics'
-import {usePalette} from 'lib/hooks/usePalette'
 import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
 import {useListMembersQuery} from '#/state/queries/list-members'
 import {logger} from '#/logger'
@@ -51,7 +49,6 @@ export function ListMembers({
   headerOffset?: number
   desktopFixedHeightOffset?: number
 }) {
-  const pal = usePalette('default')
   const {track} = useAnalytics()
   const [isRefreshing, setIsRefreshing] = React.useState(false)
   const {isMobile} = useWebMediaQueries()
@@ -215,24 +212,16 @@ export function ListMembers({
         renderItem={renderItem}
         ListHeaderComponent={renderHeader}
         ListFooterComponent={Footer}
-        refreshControl={
-          <RefreshControl
-            refreshing={isRefreshing}
-            onRefresh={onRefresh}
-            tintColor={pal.colors.text}
-            titleColor={pal.colors.text}
-            progressViewOffset={headerOffset}
-          />
-        }
+        refreshing={isRefreshing}
+        onRefresh={onRefresh}
+        headerOffset={headerOffset}
         contentContainerStyle={{
           minHeight: Dimensions.get('window').height * 1.5,
         }}
-        style={{paddingTop: headerOffset}}
         onScrolledDownChange={onScrolledDownChange}
         onEndReached={onEndReached}
         onEndReachedThreshold={0.6}
         removeClippedSubviews={true}
-        contentOffset={{x: 0, y: headerOffset * -1}}
         // @ts-ignore our .web version only -prf
         desktopFixedHeight={desktopFixedHeightOffset || true}
       />
diff --git a/src/view/com/lists/MyLists.tsx b/src/view/com/lists/MyLists.tsx
index 586ad234e..a2a6b0651 100644
--- a/src/view/com/lists/MyLists.tsx
+++ b/src/view/com/lists/MyLists.tsx
@@ -119,31 +119,51 @@ export function MyLists({
     [error, onRefresh, renderItem, pal],
   )
 
-  const FlatListCom = inline ? RNFlatList : List
-  return (
-    <View testID={testID} style={style}>
-      {items.length > 0 && (
-        <FlatListCom
-          testID={testID ? `${testID}-flatlist` : undefined}
-          data={items}
-          keyExtractor={item => (item.uri ? item.uri : item._reactKey)}
-          renderItem={renderItemInner}
-          refreshControl={
-            <RefreshControl
-              refreshing={isPTRing}
-              onRefresh={onRefresh}
-              tintColor={pal.colors.text}
-              titleColor={pal.colors.text}
-            />
-          }
-          contentContainerStyle={[s.contentContainer]}
-          removeClippedSubviews={true}
-          // @ts-ignore our .web version only -prf
-          desktopFixedHeight
-        />
-      )}
-    </View>
-  )
+  if (inline) {
+    return (
+      <View testID={testID} style={style}>
+        {items.length > 0 && (
+          <RNFlatList
+            testID={testID ? `${testID}-flatlist` : undefined}
+            data={items}
+            keyExtractor={item => (item.uri ? item.uri : item._reactKey)}
+            renderItem={renderItemInner}
+            refreshControl={
+              <RefreshControl
+                refreshing={isPTRing}
+                onRefresh={onRefresh}
+                tintColor={pal.colors.text}
+                titleColor={pal.colors.text}
+              />
+            }
+            contentContainerStyle={[s.contentContainer]}
+            removeClippedSubviews={true}
+            // @ts-ignore our .web version only -prf
+            desktopFixedHeight
+          />
+        )}
+      </View>
+    )
+  } else {
+    return (
+      <View testID={testID} style={style}>
+        {items.length > 0 && (
+          <List
+            testID={testID ? `${testID}-flatlist` : undefined}
+            data={items}
+            keyExtractor={item => (item.uri ? item.uri : item._reactKey)}
+            renderItem={renderItemInner}
+            refreshing={isPTRing}
+            onRefresh={onRefresh}
+            contentContainerStyle={[s.contentContainer]}
+            removeClippedSubviews={true}
+            // @ts-ignore our .web version only -prf
+            desktopFixedHeight
+          />
+        )}
+      </View>
+    )
+  }
 }
 
 const styles = StyleSheet.create({
diff --git a/src/view/com/lists/ProfileLists.tsx b/src/view/com/lists/ProfileLists.tsx
index e3d9bd0b4..db981717f 100644
--- a/src/view/com/lists/ProfileLists.tsx
+++ b/src/view/com/lists/ProfileLists.tsx
@@ -1,12 +1,5 @@
 import React from 'react'
-import {
-  Dimensions,
-  RefreshControl,
-  StyleProp,
-  StyleSheet,
-  View,
-  ViewStyle,
-} from 'react-native'
+import {Dimensions, StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
 import {useQueryClient} from '@tanstack/react-query'
 import {List, ListRef} from '../util/List'
 import {ListCard} from './ListCard'
@@ -182,22 +175,14 @@ export const ProfileLists = React.forwardRef<SectionRef, ProfileListsProps>(
           data={items}
           keyExtractor={(item: any) => item._reactKey}
           renderItem={renderItemInner}
-          refreshControl={
-            <RefreshControl
-              refreshing={isPTRing}
-              onRefresh={onRefresh}
-              tintColor={pal.colors.text}
-              titleColor={pal.colors.text}
-              progressViewOffset={headerOffset}
-            />
-          }
+          refreshing={isPTRing}
+          onRefresh={onRefresh}
+          headerOffset={headerOffset}
           contentContainerStyle={{
             minHeight: Dimensions.get('window').height * 1.5,
           }}
-          style={{paddingTop: headerOffset}}
           indicatorStyle={theme.colorScheme === 'dark' ? 'white' : 'black'}
           removeClippedSubviews={true}
-          contentOffset={{x: 0, y: headerOffset * -1}}
           // @ts-ignore our .web version only -prf
           desktopFixedHeight
           onEndReached={onEndReached}
diff --git a/src/view/com/notifications/Feed.tsx b/src/view/com/notifications/Feed.tsx
index 52d534c4f..a99fe2c1d 100644
--- a/src/view/com/notifications/Feed.tsx
+++ b/src/view/com/notifications/Feed.tsx
@@ -1,13 +1,12 @@
 import React from 'react'
 import {CenteredView} from '../util/Views'
-import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
+import {ActivityIndicator, StyleSheet, View} from 'react-native'
 import {FeedItem} from './FeedItem'
 import {NotificationFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
 import {ErrorMessage} from '../util/error/ErrorMessage'
 import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
 import {EmptyState} from '../util/EmptyState'
 import {s} from 'lib/styles'
-import {usePalette} from 'lib/hooks/usePalette'
 import {useNotificationFeedQuery} from '#/state/queries/notifications/feed'
 import {useUnreadNotificationsApi} from '#/state/queries/notifications/unread'
 import {logger} from '#/logger'
@@ -30,7 +29,6 @@ export function Feed({
   onScrolledDownChange: (isScrolledDown: boolean) => void
   ListHeaderComponent?: () => JSX.Element
 }) {
-  const pal = usePalette('default')
   const [isPTRing, setIsPTRing] = React.useState(false)
 
   const moderationOpts = useModerationOpts()
@@ -152,14 +150,8 @@ export function Feed({
         renderItem={renderItem}
         ListHeaderComponent={ListHeaderComponent}
         ListFooterComponent={FeedFooter}
-        refreshControl={
-          <RefreshControl
-            refreshing={isPTRing}
-            onRefresh={onRefresh}
-            tintColor={pal.colors.text}
-            titleColor={pal.colors.text}
-          />
-        }
+        refreshing={isPTRing}
+        onRefresh={onRefresh}
         onEndReached={onEndReached}
         onEndReachedThreshold={0.6}
         onScrolledDownChange={onScrolledDownChange}
diff --git a/src/view/com/post-thread/PostLikedBy.tsx b/src/view/com/post-thread/PostLikedBy.tsx
index 245ba59e8..6e013f611 100644
--- a/src/view/com/post-thread/PostLikedBy.tsx
+++ b/src/view/com/post-thread/PostLikedBy.tsx
@@ -1,18 +1,16 @@
 import React, {useCallback, useMemo, useState} from 'react'
-import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
+import {ActivityIndicator, StyleSheet, View} from 'react-native'
 import {AppBskyFeedGetLikes as GetLikes} from '@atproto/api'
 import {CenteredView} from '../util/Views'
 import {List} from '../util/List'
 import {ErrorMessage} from '../util/error/ErrorMessage'
 import {ProfileCardWithFollowBtn} from '../profile/ProfileCard'
-import {usePalette} from 'lib/hooks/usePalette'
 import {logger} from '#/logger'
 import {useResolveUriQuery} from '#/state/queries/resolve-uri'
 import {usePostLikedByQuery} from '#/state/queries/post-liked-by'
 import {cleanError} from '#/lib/strings/errors'
 
 export function PostLikedBy({uri}: {uri: string}) {
-  const pal = usePalette('default')
   const [isPTRing, setIsPTRing] = useState(false)
   const {
     data: resolvedUri,
@@ -88,14 +86,8 @@ export function PostLikedBy({uri}: {uri: string}) {
     <List
       data={likes}
       keyExtractor={item => item.actor.did}
-      refreshControl={
-        <RefreshControl
-          refreshing={isPTRing}
-          onRefresh={onRefresh}
-          tintColor={pal.colors.text}
-          titleColor={pal.colors.text}
-        />
-      }
+      refreshing={isPTRing}
+      onRefresh={onRefresh}
       onEndReached={onEndReached}
       renderItem={renderItem}
       initialNumToRender={15}
diff --git a/src/view/com/post-thread/PostRepostedBy.tsx b/src/view/com/post-thread/PostRepostedBy.tsx
index 5cc006388..a2d3be558 100644
--- a/src/view/com/post-thread/PostRepostedBy.tsx
+++ b/src/view/com/post-thread/PostRepostedBy.tsx
@@ -1,18 +1,16 @@
 import React, {useMemo, useCallback, useState} from 'react'
-import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
+import {ActivityIndicator, StyleSheet, View} from 'react-native'
 import {AppBskyActorDefs as ActorDefs} from '@atproto/api'
 import {CenteredView} from '../util/Views'
 import {List} from '../util/List'
 import {ProfileCardWithFollowBtn} from '../profile/ProfileCard'
 import {ErrorMessage} from '../util/error/ErrorMessage'
-import {usePalette} from 'lib/hooks/usePalette'
 import {logger} from '#/logger'
 import {useResolveUriQuery} from '#/state/queries/resolve-uri'
 import {usePostRepostedByQuery} from '#/state/queries/post-reposted-by'
 import {cleanError} from '#/lib/strings/errors'
 
 export function PostRepostedBy({uri}: {uri: string}) {
-  const pal = usePalette('default')
   const [isPTRing, setIsPTRing] = useState(false)
   const {
     data: resolvedUri,
@@ -89,14 +87,8 @@ export function PostRepostedBy({uri}: {uri: string}) {
     <List
       data={repostedBy}
       keyExtractor={item => item.did}
-      refreshControl={
-        <RefreshControl
-          refreshing={isPTRing}
-          onRefresh={onRefresh}
-          tintColor={pal.colors.text}
-          titleColor={pal.colors.text}
-        />
-      }
+      refreshing={isPTRing}
+      onRefresh={onRefresh}
       onEndReached={onEndReached}
       renderItem={renderItem}
       initialNumToRender={15}
diff --git a/src/view/com/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx
index 917550884..6cd1f3551 100644
--- a/src/view/com/post-thread/PostThread.tsx
+++ b/src/view/com/post-thread/PostThread.tsx
@@ -2,7 +2,6 @@ import React, {useEffect, useRef} from 'react'
 import {
   ActivityIndicator,
   Pressable,
-  RefreshControl,
   StyleSheet,
   TouchableOpacity,
   View,
@@ -349,14 +348,8 @@ function PostThreadLoaded({
       }
       keyExtractor={item => item._reactKey}
       renderItem={renderItem}
-      refreshControl={
-        <RefreshControl
-          refreshing={isPTRing}
-          onRefresh={onPTR}
-          tintColor={pal.colors.text}
-          titleColor={pal.colors.text}
-        />
-      }
+      refreshing={isPTRing}
+      onRefresh={onPTR}
       onContentSizeChange={onContentSizeChange}
       style={s.hContentRegion}
       // @ts-ignore our .web version only -prf
diff --git a/src/view/com/posts/Feed.tsx b/src/view/com/posts/Feed.tsx
index 8d5c11bda..3d38fbbc5 100644
--- a/src/view/com/posts/Feed.tsx
+++ b/src/view/com/posts/Feed.tsx
@@ -3,7 +3,6 @@ import {
   ActivityIndicator,
   AppState,
   Dimensions,
-  RefreshControl,
   StyleProp,
   StyleSheet,
   View,
@@ -16,7 +15,6 @@ import {FeedErrorMessage} from './FeedErrorMessage'
 import {FeedSlice} from './FeedSlice'
 import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
 import {useAnalytics} from 'lib/analytics/analytics'
-import {usePalette} from 'lib/hooks/usePalette'
 import {useTheme} from 'lib/ThemeContext'
 import {logger} from '#/logger'
 import {
@@ -74,7 +72,6 @@ let Feed = ({
   ListHeaderComponent?: () => JSX.Element
   extraData?: any
 }): React.ReactNode => {
-  const pal = usePalette('default')
   const theme = useTheme()
   const {track} = useAnalytics()
   const queryClient = useQueryClient()
@@ -294,25 +291,17 @@ let Feed = ({
         renderItem={renderItem}
         ListFooterComponent={FeedFooter}
         ListHeaderComponent={ListHeaderComponent}
-        refreshControl={
-          <RefreshControl
-            refreshing={isPTRing}
-            onRefresh={onRefresh}
-            tintColor={pal.colors.text}
-            titleColor={pal.colors.text}
-            progressViewOffset={headerOffset}
-          />
-        }
+        refreshing={isPTRing}
+        onRefresh={onRefresh}
+        headerOffset={headerOffset}
         contentContainerStyle={{
           minHeight: Dimensions.get('window').height * 1.5,
         }}
-        style={{paddingTop: headerOffset}}
         onScrolledDownChange={onScrolledDownChange}
         indicatorStyle={theme.colorScheme === 'dark' ? 'white' : 'black'}
         onEndReached={onEndReached}
         onEndReachedThreshold={2} // number of posts left to trigger load more
         removeClippedSubviews={true}
-        contentOffset={{x: 0, y: headerOffset * -1}}
         extraData={extraData}
         // @ts-ignore our .web version only -prf
         desktopFixedHeight={
diff --git a/src/view/com/profile/ProfileFollowers.tsx b/src/view/com/profile/ProfileFollowers.tsx
index 077dabe53..fd8dee173 100644
--- a/src/view/com/profile/ProfileFollowers.tsx
+++ b/src/view/com/profile/ProfileFollowers.tsx
@@ -1,18 +1,16 @@
 import React from 'react'
-import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
+import {ActivityIndicator, StyleSheet, View} from 'react-native'
 import {AppBskyActorDefs as ActorDefs} from '@atproto/api'
 import {CenteredView} from '../util/Views'
 import {List} from '../util/List'
 import {ErrorMessage} from '../util/error/ErrorMessage'
 import {ProfileCardWithFollowBtn} from './ProfileCard'
-import {usePalette} from 'lib/hooks/usePalette'
 import {useProfileFollowersQuery} from '#/state/queries/profile-followers'
 import {useResolveDidQuery} from '#/state/queries/resolve-uri'
 import {logger} from '#/logger'
 import {cleanError} from '#/lib/strings/errors'
 
 export function ProfileFollowers({name}: {name: string}) {
-  const pal = usePalette('default')
   const [isPTRing, setIsPTRing] = React.useState(false)
   const {
     data: resolvedDid,
@@ -90,14 +88,8 @@ export function ProfileFollowers({name}: {name: string}) {
     <List
       data={followers}
       keyExtractor={item => item.did}
-      refreshControl={
-        <RefreshControl
-          refreshing={isPTRing}
-          onRefresh={onRefresh}
-          tintColor={pal.colors.text}
-          titleColor={pal.colors.text}
-        />
-      }
+      refreshing={isPTRing}
+      onRefresh={onRefresh}
       onEndReached={onEndReached}
       renderItem={renderItem}
       initialNumToRender={15}
diff --git a/src/view/com/profile/ProfileFollows.tsx b/src/view/com/profile/ProfileFollows.tsx
index 5265ee07e..091922dd9 100644
--- a/src/view/com/profile/ProfileFollows.tsx
+++ b/src/view/com/profile/ProfileFollows.tsx
@@ -1,18 +1,16 @@
 import React from 'react'
-import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
+import {ActivityIndicator, StyleSheet, View} from 'react-native'
 import {AppBskyActorDefs as ActorDefs} from '@atproto/api'
 import {CenteredView} from '../util/Views'
 import {List} from '../util/List'
 import {ErrorMessage} from '../util/error/ErrorMessage'
 import {ProfileCardWithFollowBtn} from './ProfileCard'
-import {usePalette} from 'lib/hooks/usePalette'
 import {useProfileFollowsQuery} from '#/state/queries/profile-follows'
 import {useResolveDidQuery} from '#/state/queries/resolve-uri'
 import {logger} from '#/logger'
 import {cleanError} from '#/lib/strings/errors'
 
 export function ProfileFollows({name}: {name: string}) {
-  const pal = usePalette('default')
   const [isPTRing, setIsPTRing] = React.useState(false)
   const {
     data: resolvedDid,
@@ -90,14 +88,8 @@ export function ProfileFollows({name}: {name: string}) {
     <List
       data={follows}
       keyExtractor={item => item.did}
-      refreshControl={
-        <RefreshControl
-          refreshing={isPTRing}
-          onRefresh={onRefresh}
-          tintColor={pal.colors.text}
-          titleColor={pal.colors.text}
-        />
-      }
+      refreshing={isPTRing}
+      onRefresh={onRefresh}
       onEndReached={onEndReached}
       renderItem={renderItem}
       initialNumToRender={15}
diff --git a/src/view/com/util/List.tsx b/src/view/com/util/List.tsx
index 2acc3f4b3..9abd7d35a 100644
--- a/src/view/com/util/List.tsx
+++ b/src/view/com/util/List.tsx
@@ -1,27 +1,42 @@
 import React, {memo, startTransition} from 'react'
-import {FlatListProps} from 'react-native'
+import {FlatListProps, RefreshControl} from 'react-native'
 import {FlatList_INTERNAL} from './Views'
+import {addStyle} from 'lib/styles'
 import {useScrollHandlers} from '#/lib/ScrollContext'
 import {runOnJS, useSharedValue} from 'react-native-reanimated'
 import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED'
+import {usePalette} from '#/lib/hooks/usePalette'
 
 export type ListMethods = FlatList_INTERNAL
 export type ListProps<ItemT> = Omit<
   FlatListProps<ItemT>,
-  'onScroll' // Use ScrollContext instead.
+  | 'onScroll' // Use ScrollContext instead.
+  | 'refreshControl' // Pass refreshing and/or onRefresh instead.
+  | 'contentOffset' // Pass headerOffset instead.
 > & {
   onScrolledDownChange?: (isScrolledDown: boolean) => void
+  headerOffset?: number
+  refreshing?: boolean
+  onRefresh?: () => void
 }
 export type ListRef = React.MutableRefObject<FlatList_INTERNAL | null>
 
 const SCROLLED_DOWN_LIMIT = 200
 
 function ListImpl<ItemT>(
-  {onScrolledDownChange, ...props}: ListProps<ItemT>,
+  {
+    onScrolledDownChange,
+    refreshing,
+    onRefresh,
+    headerOffset,
+    style,
+    ...props
+  }: ListProps<ItemT>,
   ref: React.Ref<ListMethods>,
 ) {
   const isScrolledDown = useSharedValue(false)
   const contextScrollHandlers = useScrollHandlers()
+  const pal = usePalette('default')
 
   function handleScrolledDownChange(didScrollDown: boolean) {
     startTransition(() => {
@@ -49,12 +64,36 @@ function ListImpl<ItemT>(
     },
   })
 
+  let refreshControl
+  if (refreshing !== undefined || onRefresh !== undefined) {
+    refreshControl = (
+      <RefreshControl
+        refreshing={refreshing ?? false}
+        onRefresh={onRefresh}
+        tintColor={pal.colors.text}
+        titleColor={pal.colors.text}
+        progressViewOffset={headerOffset}
+      />
+    )
+  }
+
+  let contentOffset
+  if (headerOffset != null) {
+    style = addStyle(style, {
+      paddingTop: headerOffset,
+    })
+    contentOffset = {x: 0, y: headerOffset * -1}
+  }
+
   return (
     <FlatList_INTERNAL
       {...props}
       scrollIndicatorInsets={{right: 1}}
+      contentOffset={contentOffset}
+      refreshControl={refreshControl}
       onScroll={scrollHandler}
       scrollEventThrottle={1}
+      style={style}
       ref={ref}
     />
   )
diff --git a/src/view/screens/Feeds.tsx b/src/view/screens/Feeds.tsx
index 1c8bdf9f0..20cdf815a 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, RefreshControl} from 'react-native'
+import {ActivityIndicator, StyleSheet, View} from 'react-native'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
 import {ViewHeader} from 'view/com/util/ViewHeader'
@@ -487,14 +487,8 @@ export function FeedsScreen(_props: Props) {
         keyExtractor={item => item.key}
         contentContainerStyle={styles.contentContainer}
         renderItem={renderItem}
-        refreshControl={
-          <RefreshControl
-            refreshing={isPTR}
-            onRefresh={isUserSearching ? undefined : onPullToRefresh}
-            tintColor={pal.colors.text}
-            titleColor={pal.colors.text}
-          />
-        }
+        refreshing={isPTR}
+        onRefresh={isUserSearching ? undefined : onPullToRefresh}
         initialNumToRender={10}
         onEndReached={onEndReached}
         // @ts-ignore our .web version only -prf
diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx
index efd8507a7..60fd21674 100644
--- a/src/view/screens/Search/Search.tsx
+++ b/src/view/screens/Search/Search.tsx
@@ -3,7 +3,6 @@ import {
   View,
   StyleSheet,
   ActivityIndicator,
-  RefreshControl,
   TextInput,
   Pressable,
   Platform,
@@ -185,7 +184,6 @@ type SearchResultSlice =
 
 function SearchScreenPostResults({query}: {query: string}) {
   const {_} = useLingui()
-  const pal = usePalette('default')
   const [isPTR, setIsPTR] = React.useState(false)
   const {
     isFetched,
@@ -254,14 +252,8 @@ function SearchScreenPostResults({query}: {query: string}) {
                 }
               }}
               keyExtractor={item => item.key}
-              refreshControl={
-                <RefreshControl
-                  refreshing={isPTR}
-                  onRefresh={onPullToRefresh}
-                  tintColor={pal.colors.text}
-                  titleColor={pal.colors.text}
-                />
-              }
+              refreshing={isPTR}
+              onRefresh={onPullToRefresh}
               onEndReached={onEndReached}
               // @ts-ignore web only -prf
               desktopFixedHeight