diff --git a/src/view/com/feeds/FeedPage.tsx b/src/view/com/feeds/FeedPage.tsx index 31ebc75a19..a64809ca70 100644 --- a/src/view/com/feeds/FeedPage.tsx +++ b/src/view/com/feeds/FeedPage.tsx @@ -13,7 +13,8 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {FeedDescriptor, FeedParams} from '#/state/queries/post-feed' import {ComposeIcon2} from 'lib/icons' import {colors, s} from 'lib/styles' -import {FlatList, View, useWindowDimensions} from 'react-native' +import {View, useWindowDimensions} from 'react-native' +import {ListMethods} from '../util/List' import {Feed} from '../posts/Feed' import {TextLink} from '../util/Link' import {FAB} from '../util/fab/FAB' @@ -54,7 +55,7 @@ export function FeedPage({ const [onMainScroll, isScrolledDown, resetMainScroll] = useOnMainScroll() const {screen, track} = useAnalytics() const headerOffset = useHeaderOffset() - const scrollElRef = React.useRef(null) + const scrollElRef = React.useRef(null) const [hasNew, setHasNew] = React.useState(false) const scrollToTop = React.useCallback(() => { diff --git a/src/view/com/feeds/ProfileFeedgens.tsx b/src/view/com/feeds/ProfileFeedgens.tsx index 43b170ced4..73e8ee3f85 100644 --- a/src/view/com/feeds/ProfileFeedgens.tsx +++ b/src/view/com/feeds/ProfileFeedgens.tsx @@ -1,4 +1,4 @@ -import React, {MutableRefObject} from 'react' +import React from 'react' import { Dimensions, RefreshControl, @@ -8,7 +8,7 @@ import { ViewStyle, } from 'react-native' import {useQueryClient} from '@tanstack/react-query' -import {FlatList} from '../util/Views' +import {List, ListRef} from '../util/List' import {FeedSourceCardLoaded} from './FeedSourceCard' import {ErrorMessage} from '../util/error/ErrorMessage' import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn' @@ -37,7 +37,7 @@ interface SectionRef { interface ProfileFeedgensProps { did: string - scrollElRef: MutableRefObject | null> + scrollElRef: ListRef onScroll?: OnScrollHandler scrollEventThrottle?: number headerOffset: number @@ -188,7 +188,7 @@ export const ProfileFeedgens = React.forwardRef< const scrollHandler = useAnimatedScrollHandler(onScroll || {}) return ( - - scrollElRef?: MutableRefObject | null> + scrollElRef?: ListRef onScroll: OnScrollHandler onPressTryAgain?: () => void renderHeader: () => JSX.Element @@ -212,7 +212,7 @@ export function ListMembers({ const scrollHandler = useAnimatedScrollHandler(onScroll) return ( - {items.length > 0 && ( diff --git a/src/view/com/lists/ProfileLists.tsx b/src/view/com/lists/ProfileLists.tsx index 1bd9d188ff..d1ed91980a 100644 --- a/src/view/com/lists/ProfileLists.tsx +++ b/src/view/com/lists/ProfileLists.tsx @@ -1,4 +1,4 @@ -import React, {MutableRefObject} from 'react' +import React from 'react' import { Dimensions, RefreshControl, @@ -8,7 +8,7 @@ import { ViewStyle, } from 'react-native' import {useQueryClient} from '@tanstack/react-query' -import {FlatList} from '../util/Views' +import {List, ListRef} from '../util/List' import {ListCard} from './ListCard' import {ErrorMessage} from '../util/error/ErrorMessage' import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn' @@ -36,7 +36,7 @@ interface SectionRef { interface ProfileListsProps { did: string - scrollElRef: MutableRefObject | null> + scrollElRef: ListRef onScroll?: OnScrollHandler scrollEventThrottle?: number headerOffset: number @@ -190,7 +190,7 @@ export const ProfileLists = React.forwardRef( const scrollHandler = useAnimatedScrollHandler(onScroll || {}) return ( - | null> + scrollElRef?: ListRef onPressTryAgain?: () => void onScroll?: OnScrollHandler ListHeaderComponent?: () => JSX.Element @@ -146,7 +147,7 @@ export function Feed({ /> )} - | ScrollView | null> + scrollElRef: React.MutableRefObject } export interface PagerWithHeaderProps { @@ -331,7 +331,7 @@ function PagerItem({ isScrolledDown, onScroll: scrollHandler, scrollElRef: scrollElRef as React.MutableRefObject< - FlatList | ScrollView | null + ListMethods | ScrollView | null >, }) } diff --git a/src/view/com/post-thread/PostLikedBy.tsx b/src/view/com/post-thread/PostLikedBy.tsx index 60afe1f9c6..245ba59e8c 100644 --- a/src/view/com/post-thread/PostLikedBy.tsx +++ b/src/view/com/post-thread/PostLikedBy.tsx @@ -1,7 +1,8 @@ import React, {useCallback, useMemo, useState} from 'react' import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native' import {AppBskyFeedGetLikes as GetLikes} from '@atproto/api' -import {CenteredView, FlatList} from '../util/Views' +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' @@ -84,7 +85,7 @@ export function PostLikedBy({uri}: {uri: string}) { // loaded // = return ( - item.actor.did} refreshControl={ diff --git a/src/view/com/post-thread/PostRepostedBy.tsx b/src/view/com/post-thread/PostRepostedBy.tsx index 1162fec403..5cc0063885 100644 --- a/src/view/com/post-thread/PostRepostedBy.tsx +++ b/src/view/com/post-thread/PostRepostedBy.tsx @@ -1,7 +1,8 @@ import React, {useMemo, useCallback, useState} from 'react' import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native' import {AppBskyActorDefs as ActorDefs} from '@atproto/api' -import {CenteredView, FlatList} from '../util/Views' +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' @@ -85,7 +86,7 @@ export function PostRepostedBy({uri}: {uri: string}) { // loaded // = return ( - item.did} refreshControl={ diff --git a/src/view/com/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx index 051bc7849f..f27da331f9 100644 --- a/src/view/com/post-thread/PostThread.tsx +++ b/src/view/com/post-thread/PostThread.tsx @@ -8,7 +8,8 @@ import { View, } from 'react-native' import {AppBskyFeedDefs} from '@atproto/api' -import {CenteredView, FlatList} from '../util/Views' +import {CenteredView} from '../util/Views' +import {List, ListMethods} from '../util/List' import { FontAwesomeIcon, FontAwesomeIconStyle, @@ -140,7 +141,7 @@ function PostThreadLoaded({ const {_} = useLingui() const pal = usePalette('default') const {isTablet, isDesktop} = useWebMediaQueries() - const ref = useRef(null) + const ref = useRef(null) const highlightedPostRef = useRef(null) const needsScrollAdjustment = useRef( !isNative || // web always uses scroll adjustment @@ -335,7 +336,7 @@ function PostThreadLoaded({ ) return ( - enabled?: boolean pollInterval?: number - scrollElRef?: MutableRefObject | null> + scrollElRef?: ListRef onHasNew?: (v: boolean) => void onScroll?: OnScrollHandler scrollEventThrottle?: number @@ -273,7 +273,7 @@ let Feed = ({ const scrollHandler = useAnimatedScrollHandler(onScroll || {}) return ( - item.did} refreshControl={ diff --git a/src/view/com/profile/ProfileFollows.tsx b/src/view/com/profile/ProfileFollows.tsx index 890c13eb23..5265ee07ea 100644 --- a/src/view/com/profile/ProfileFollows.tsx +++ b/src/view/com/profile/ProfileFollows.tsx @@ -1,7 +1,8 @@ import React from 'react' import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native' import {AppBskyActorDefs as ActorDefs} from '@atproto/api' -import {CenteredView, FlatList} from '../util/Views' +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' @@ -86,7 +87,7 @@ export function ProfileFollows({name}: {name: string}) { // loaded // = return ( - item.did} refreshControl={ diff --git a/src/view/com/util/List.tsx b/src/view/com/util/List.tsx new file mode 100644 index 0000000000..19b43d5455 --- /dev/null +++ b/src/view/com/util/List.tsx @@ -0,0 +1,15 @@ +import React from 'react' +import {FlatListProps} from 'react-native' +import {FlatList_INTERNAL} from './Views' + +export type ListMethods = FlatList_INTERNAL +export type ListProps = FlatListProps +export type ListRef = React.MutableRefObject + +function ListImpl(props: ListProps, ref: React.Ref) { + return +} + +export const List = React.forwardRef(ListImpl) as ( + props: ListProps & {ref?: React.Ref}, +) => React.ReactElement diff --git a/src/view/com/util/ViewSelector.tsx b/src/view/com/util/ViewSelector.tsx index 935d93033b..79f91be5ef 100644 --- a/src/view/com/util/ViewSelector.tsx +++ b/src/view/com/util/ViewSelector.tsx @@ -6,7 +6,7 @@ import { View, ScrollView, } from 'react-native' -import {FlatList} from './Views' +import {FlatList_INTERNAL} from './Views' import {OnScrollCb} from 'lib/hooks/useOnMainScroll' import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle' import {Text} from './text/Text' @@ -110,7 +110,7 @@ export const ViewSelector = React.forwardRef< [items], ) return ( - diff --git a/src/view/com/util/Views.web.tsx b/src/view/com/util/Views.web.tsx index 5a4f266fd5..db3b9de0d8 100644 --- a/src/view/com/util/Views.web.tsx +++ b/src/view/com/util/Views.web.tsx @@ -49,7 +49,7 @@ export function CenteredView({ return } -export const FlatList = React.forwardRef(function FlatListImpl( +export const FlatList_INTERNAL = React.forwardRef(function FlatListImpl( { contentContainerStyle, style, diff --git a/src/view/screens/Feeds.tsx b/src/view/screens/Feeds.tsx index f319fbc390..d2d0aafb8f 100644 --- a/src/view/screens/Feeds.tsx +++ b/src/view/screens/Feeds.tsx @@ -19,7 +19,7 @@ import { import {ErrorMessage} from 'view/com/util/error/ErrorMessage' import debounce from 'lodash.debounce' import {Text} from 'view/com/util/text/Text' -import {FlatList} from 'view/com/util/Views' +import {List} from 'view/com/util/List' import {useFocusEffect} from '@react-navigation/native' import {FeedSourceCard} from 'view/com/feeds/FeedSourceCard' import {Trans, msg} from '@lingui/macro' @@ -481,7 +481,7 @@ export function FeedsScreen(_props: Props) { {preferences ? : } - item.key} diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx index fceaa60c29..09cdfcd0aa 100644 --- a/src/view/screens/Notifications.tsx +++ b/src/view/screens/Notifications.tsx @@ -1,5 +1,5 @@ import React from 'react' -import {FlatList, View} from 'react-native' +import {View} from 'react-native' import {useFocusEffect} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' import { @@ -9,6 +9,7 @@ import { import {ViewHeader} from '../com/util/ViewHeader' import {Feed} from '../com/notifications/Feed' import {TextLink} from 'view/com/util/Link' +import {ListMethods} from 'view/com/util/List' import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn' import {useOnMainScroll} from 'lib/hooks/useOnMainScroll' import {usePalette} from 'lib/hooks/usePalette' @@ -36,7 +37,7 @@ export function NotificationsScreen({}: Props) { const {_} = useLingui() const setMinimalShellMode = useSetMinimalShellMode() const [onMainScroll, isScrolledDown, resetMainScroll] = useOnMainScroll() - const scrollElRef = React.useRef(null) + const scrollElRef = React.useRef(null) const checkLatestRef = React.useRef<() => void | null>() const {screen} = useAnalytics() const pal = usePalette('default') diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index b7dac8c6de..fa15b8a149 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -5,7 +5,8 @@ import {AppBskyActorDefs, moderateProfile, ModerationOpts} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' -import {CenteredView, FlatList} from '../com/util/Views' +import {CenteredView} from '../com/util/Views' +import {ListRef} from '../com/util/List' import {ScreenHider} from 'view/com/util/moderation/ScreenHider' import {Feed} from 'view/com/posts/Feed' import {ProfileLists} from '../com/lists/ProfileLists' @@ -285,9 +286,7 @@ function ProfileScreenLoaded({ headerHeight={headerHeight} isFocused={isFocused} isScrolledDown={isScrolledDown} - scrollElRef={ - scrollElRef as React.MutableRefObject | null> - } + scrollElRef={scrollElRef as ListRef} ignoreFilterFor={profile.did} /> )} @@ -306,9 +305,7 @@ function ProfileScreenLoaded({ headerHeight={headerHeight} isFocused={isFocused} isScrolledDown={isScrolledDown} - scrollElRef={ - scrollElRef as React.MutableRefObject | null> - } + scrollElRef={scrollElRef as ListRef} ignoreFilterFor={profile.did} /> ) @@ -321,9 +318,7 @@ function ProfileScreenLoaded({ headerHeight={headerHeight} isFocused={isFocused} isScrolledDown={isScrolledDown} - scrollElRef={ - scrollElRef as React.MutableRefObject | null> - } + scrollElRef={scrollElRef as ListRef} ignoreFilterFor={profile.did} /> )} @@ -342,9 +337,7 @@ function ProfileScreenLoaded({ headerHeight={headerHeight} isFocused={isFocused} isScrolledDown={isScrolledDown} - scrollElRef={ - scrollElRef as React.MutableRefObject | null> - } + scrollElRef={scrollElRef as ListRef} ignoreFilterFor={profile.did} /> ) @@ -354,9 +347,7 @@ function ProfileScreenLoaded({ | null> - } + scrollElRef={scrollElRef as ListRef} onScroll={onScroll} scrollEventThrottle={1} headerOffset={headerHeight} @@ -369,9 +360,7 @@ function ProfileScreenLoaded({ | null> - } + scrollElRef={scrollElRef as ListRef} onScroll={onScroll} scrollEventThrottle={1} headerOffset={headerHeight} @@ -400,7 +389,7 @@ interface FeedSectionProps { headerHeight: number isFocused: boolean isScrolledDown: boolean - scrollElRef: React.MutableRefObject | null> + scrollElRef: ListRef ignoreFilterFor?: string } const FeedSection = React.forwardRef( diff --git a/src/view/screens/ProfileFeed.tsx b/src/view/screens/ProfileFeed.tsx index 620b1814af..73e265410c 100644 --- a/src/view/screens/ProfileFeed.tsx +++ b/src/view/screens/ProfileFeed.tsx @@ -1,11 +1,5 @@ import React, {useMemo, useCallback} from 'react' -import { - Dimensions, - StyleSheet, - View, - ActivityIndicator, - FlatList, -} from 'react-native' +import {Dimensions, StyleSheet, View, ActivityIndicator} from 'react-native' import {NativeStackScreenProps} from '@react-navigation/native-stack' import {useNavigation} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' @@ -20,6 +14,7 @@ import {PagerWithHeader} from 'view/com/pager/PagerWithHeader' import {ProfileSubpageHeader} from 'view/com/profile/ProfileSubpageHeader' import {Feed} from 'view/com/posts/Feed' import {TextLink} from 'view/com/util/Link' +import {ListRef} from 'view/com/util/List' import {Button} from 'view/com/util/forms/Button' import {Text} from 'view/com/util/text/Text' import {RichText} from 'view/com/util/text/RichText' @@ -411,9 +406,7 @@ export function ProfileFeedScreenInner({ onScroll={onScroll} headerHeight={headerHeight} isScrolledDown={isScrolledDown} - scrollElRef={ - scrollElRef as React.MutableRefObject | null> - } + scrollElRef={scrollElRef as ListRef} isFocused={isFocused} /> ) : ( @@ -500,7 +493,7 @@ interface FeedSectionProps { onScroll: OnScrollHandler headerHeight: number isScrolledDown: boolean - scrollElRef: React.MutableRefObject | null> + scrollElRef: ListRef isFocused: boolean } const FeedSection = React.forwardRef( diff --git a/src/view/screens/ProfileList.tsx b/src/view/screens/ProfileList.tsx index 3dcc7121fd..84f4137b5c 100644 --- a/src/view/screens/ProfileList.tsx +++ b/src/view/screens/ProfileList.tsx @@ -1,11 +1,5 @@ import React, {useCallback, useMemo} from 'react' -import { - ActivityIndicator, - FlatList, - Pressable, - StyleSheet, - View, -} from 'react-native' +import {ActivityIndicator, Pressable, StyleSheet, View} from 'react-native' import {useFocusEffect} from '@react-navigation/native' import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' import {useNavigation} from '@react-navigation/native' @@ -22,6 +16,7 @@ import {EmptyState} from 'view/com/util/EmptyState' import {RichText} from 'view/com/util/text/RichText' import {Button} from 'view/com/util/forms/Button' import {TextLink} from 'view/com/util/Link' +import {ListRef} from 'view/com/util/List' import * as Toast from 'view/com/util/Toast' import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn' import {FAB} from 'view/com/util/fab/FAB' @@ -175,9 +170,7 @@ function ProfileListScreenLoaded({ | null> - } + scrollElRef={scrollElRef as ListRef} onScroll={onScroll} headerHeight={headerHeight} isScrolledDown={isScrolledDown} @@ -187,9 +180,7 @@ function ProfileListScreenLoaded({ {({onScroll, headerHeight, isScrolledDown, scrollElRef}) => ( | null> - } + scrollElRef={scrollElRef as ListRef} list={list} onPressAddUser={onPressAddUser} onScroll={onScroll} @@ -224,9 +215,7 @@ function ProfileListScreenLoaded({ {({onScroll, headerHeight, isScrolledDown, scrollElRef}) => ( | null> - } + scrollElRef={scrollElRef as ListRef} onPressAddUser={onPressAddUser} onScroll={onScroll} headerHeight={headerHeight} @@ -618,7 +607,7 @@ interface FeedSectionProps { onScroll: OnScrollHandler headerHeight: number isScrolledDown: boolean - scrollElRef: React.MutableRefObject | null> + scrollElRef: ListRef isFocused: boolean } const FeedSection = React.forwardRef( @@ -677,7 +666,7 @@ interface AboutSectionProps { onScroll: OnScrollHandler headerHeight: number isScrolledDown: boolean - scrollElRef: React.MutableRefObject | null> + scrollElRef: ListRef } const AboutSection = React.forwardRef( function AboutSectionImpl( diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx index b4db270b3c..7d7b4098f8 100644 --- a/src/view/screens/Search/Search.tsx +++ b/src/view/screens/Search/Search.tsx @@ -8,7 +8,8 @@ import { Pressable, Platform, } from 'react-native' -import {FlatList, ScrollView, CenteredView} from '#/view/com/util/Views' +import {ScrollView, CenteredView} from '#/view/com/util/Views' +import {List} from '#/view/com/util/List' import {AppBskyActorDefs, AppBskyFeedDefs, moderateProfile} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' @@ -155,7 +156,7 @@ function SearchScreenSuggestedFollows() { }, [currentAccount, setSuggestions, getSuggestedFollowsByActor]) return suggestions.length ? ( - } keyExtractor={item => item.did} @@ -243,7 +244,7 @@ function SearchScreenPostResults({query}: {query: string}) { {isFetched ? ( <> {posts.length ? ( - { if (item.type === 'post') { @@ -284,7 +285,7 @@ function SearchScreenUserResults({query}: {query: string}) { return isFetched && results ? ( <> {results.length ? ( - (