diff --git a/src/view/com/feeds/ProfileFeedgens.tsx b/src/view/com/feeds/ProfileFeedgens.tsx
index ff650550..8665fbfa 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={
-
- }
+ 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 a31ca479..2aef7d76 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={
-
- }
+ 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 586ad234..a2a6b065 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 (
-
- {items.length > 0 && (
- (item.uri ? item.uri : item._reactKey)}
- renderItem={renderItemInner}
- refreshControl={
-
- }
- contentContainerStyle={[s.contentContainer]}
- removeClippedSubviews={true}
- // @ts-ignore our .web version only -prf
- desktopFixedHeight
- />
- )}
-
- )
+ if (inline) {
+ return (
+
+ {items.length > 0 && (
+ (item.uri ? item.uri : item._reactKey)}
+ renderItem={renderItemInner}
+ refreshControl={
+
+ }
+ contentContainerStyle={[s.contentContainer]}
+ removeClippedSubviews={true}
+ // @ts-ignore our .web version only -prf
+ desktopFixedHeight
+ />
+ )}
+
+ )
+ } else {
+ return (
+
+ {items.length > 0 && (
+ (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
+ />
+ )}
+
+ )
+ }
}
const styles = StyleSheet.create({
diff --git a/src/view/com/lists/ProfileLists.tsx b/src/view/com/lists/ProfileLists.tsx
index e3d9bd0b..db981717 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(
data={items}
keyExtractor={(item: any) => item._reactKey}
renderItem={renderItemInner}
- refreshControl={
-
- }
+ 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 52d534c4..a99fe2c1 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={
-
- }
+ 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 245ba59e..6e013f61 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}) {
item.actor.did}
- refreshControl={
-
- }
+ 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 5cc00638..a2d3be55 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}) {
item.did}
- refreshControl={
-
- }
+ 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 91755088..6cd1f355 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={
-
- }
+ 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 8d5c11bd..3d38fbbc 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={
-
- }
+ 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 077dabe5..fd8dee17 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}) {
item.did}
- refreshControl={
-
- }
+ 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 5265ee07..091922dd 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}) {
item.did}
- refreshControl={
-
- }
+ 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 2acc3f4b..9abd7d35 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 = Omit<
FlatListProps,
- '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
const SCROLLED_DOWN_LIMIT = 200
function ListImpl(
- {onScrolledDownChange, ...props}: ListProps,
+ {
+ onScrolledDownChange,
+ refreshing,
+ onRefresh,
+ headerOffset,
+ style,
+ ...props
+ }: ListProps,
ref: React.Ref,
) {
const isScrolledDown = useSharedValue(false)
const contextScrollHandlers = useScrollHandlers()
+ const pal = usePalette('default')
function handleScrolledDownChange(didScrollDown: boolean) {
startTransition(() => {
@@ -49,12 +64,36 @@ function ListImpl(
},
})
+ let refreshControl
+ if (refreshing !== undefined || onRefresh !== undefined) {
+ refreshControl = (
+
+ )
+ }
+
+ let contentOffset
+ if (headerOffset != null) {
+ style = addStyle(style, {
+ paddingTop: headerOffset,
+ })
+ contentOffset = {x: 0, y: headerOffset * -1}
+ }
+
return (
)
diff --git a/src/view/screens/Feeds.tsx b/src/view/screens/Feeds.tsx
index 1c8bdf9f..20cdf815 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={
-
- }
+ 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 efd8507a..60fd2167 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={
-
- }
+ refreshing={isPTR}
+ onRefresh={onPullToRefresh}
onEndReached={onEndReached}
// @ts-ignore web only -prf
desktopFixedHeight