abstract into a nice hook
This commit is contained in:
@@ -7,11 +7,6 @@ import {
|
|||||||
View,
|
View,
|
||||||
type ViewStyle,
|
type ViewStyle,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {
|
|
||||||
type ScrollHandler,
|
|
||||||
useAnimatedProps,
|
|
||||||
useSharedValue,
|
|
||||||
} from 'react-native-reanimated'
|
|
||||||
import {type AppBskyGraphDefs} from '@atproto/api'
|
import {type AppBskyGraphDefs} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {msg} from '@lingui/core/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
@@ -21,20 +16,19 @@ import {useNavigation} from '@react-navigation/native'
|
|||||||
import {useGenerateStarterPackMutation} from '#/lib/generate-starterpack'
|
import {useGenerateStarterPackMutation} from '#/lib/generate-starterpack'
|
||||||
import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset'
|
import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset'
|
||||||
import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification'
|
import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification'
|
||||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
|
||||||
import {type NavigationProp} from '#/lib/routes/types'
|
import {type NavigationProp} from '#/lib/routes/types'
|
||||||
import {ScrollProvider, useScrollHandlers} from '#/lib/ScrollContext'
|
import {ScrollProvider} from '#/lib/ScrollContext'
|
||||||
import {parseStarterPackUri} from '#/lib/strings/starter-pack'
|
import {parseStarterPackUri} from '#/lib/strings/starter-pack'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {useActorStarterPacksQuery} from '#/state/queries/actor-starter-packs'
|
import {useActorStarterPacksQuery} from '#/state/queries/actor-starter-packs'
|
||||||
import {useShellLayout} from '#/state/shell/shell-layout'
|
|
||||||
import {
|
import {
|
||||||
EmptyState,
|
EmptyState,
|
||||||
type EmptyStateButtonProps,
|
type EmptyStateButtonProps,
|
||||||
} from '#/view/com/util/EmptyState'
|
} from '#/view/com/util/EmptyState'
|
||||||
import {List, type ListRef} from '#/view/com/util/List'
|
import {List, type ListRef} from '#/view/com/util/List'
|
||||||
import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
import {FeedFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
||||||
import {atoms as a, ios, useTheme} from '#/alf'
|
import {useProfileScrollbarAdjustment} from '#/screens/Profile/useProfileScrollbarAdjustment'
|
||||||
|
import {atoms as a, ios, useBreakpoints, useTheme} from '#/alf'
|
||||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||||
import {useDialogControl} from '#/components/Dialog'
|
import {useDialogControl} from '#/components/Dialog'
|
||||||
import {PlusSmall_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
|
import {PlusSmall_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
|
||||||
@@ -88,39 +82,8 @@ export function ProfileStarterPacks({
|
|||||||
const bottomBarOffset = useBottomBarOffset(100)
|
const bottomBarOffset = useBottomBarOffset(100)
|
||||||
const {height} = useWindowDimensions()
|
const {height} = useWindowDimensions()
|
||||||
const [isPTRing, setIsPTRing] = useState(false)
|
const [isPTRing, setIsPTRing] = useState(false)
|
||||||
|
const {gtMobile} = useBreakpoints()
|
||||||
|
|
||||||
const {
|
|
||||||
onBeginDrag: onBeginDragFromContext,
|
|
||||||
onEndDrag: onEndDragFromContext,
|
|
||||||
onScroll: onScrollFromContext,
|
|
||||||
onMomentumEnd: onMomentumEndFromContext,
|
|
||||||
} = useScrollHandlers()
|
|
||||||
|
|
||||||
const scrollY = useSharedValue(0)
|
|
||||||
const onScrollWorklet = useCallback<ScrollHandler<any>>(
|
|
||||||
(e, ctx) => {
|
|
||||||
'worklet'
|
|
||||||
onScrollFromContext?.(e, ctx)
|
|
||||||
scrollY.set(e.contentOffset.y)
|
|
||||||
},
|
|
||||||
[onScrollFromContext, scrollY],
|
|
||||||
)
|
|
||||||
|
|
||||||
const {footerHeight} = useShellLayout()
|
|
||||||
|
|
||||||
const animatedProps = useAnimatedProps(() => {
|
|
||||||
if (IS_IOS) {
|
|
||||||
return {
|
|
||||||
scrollIndicatorInsets: {
|
|
||||||
top: Math.max(headerOffset - scrollY.get(), collapsedHeaderHeight),
|
|
||||||
right: 1,
|
|
||||||
left: 0,
|
|
||||||
bottom: footerHeight.get(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {}
|
|
||||||
})
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
refetch,
|
refetch,
|
||||||
@@ -129,7 +92,6 @@ export function ProfileStarterPacks({
|
|||||||
isFetchingNextPage,
|
isFetchingNextPage,
|
||||||
fetchNextPage,
|
fetchNextPage,
|
||||||
} = useActorStarterPacksQuery({did, enabled})
|
} = useActorStarterPacksQuery({did, enabled})
|
||||||
const {isTabletOrDesktop} = useWebMediaQueries()
|
|
||||||
|
|
||||||
const items = data?.pages.flatMap(page => page.starterPacks)
|
const items = data?.pages.flatMap(page => page.starterPacks)
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
@@ -191,58 +153,54 @@ export function ProfileStarterPacks({
|
|||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
a.p_lg,
|
a.p_lg,
|
||||||
(isTabletOrDesktop || index !== 0) && a.border_t,
|
(gtMobile || index !== 0) && a.border_t,
|
||||||
t.atoms.border_contrast_low,
|
t.atoms.border_contrast_low,
|
||||||
]}>
|
]}>
|
||||||
<StarterPackCard starterPack={item} />
|
<StarterPackCard starterPack={item} />
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
[isTabletOrDesktop, t.atoms.border_contrast_low],
|
[gtMobile, t.atoms.border_contrast_low],
|
||||||
)
|
)
|
||||||
|
|
||||||
const list = (
|
const {scrollHandlers, animatedProps} = useProfileScrollbarAdjustment({
|
||||||
<List
|
headerOffset,
|
||||||
testID={testID ? `${testID}-flatlist` : undefined}
|
collapsedHeaderHeight,
|
||||||
ref={scrollElRef}
|
})
|
||||||
data={items}
|
|
||||||
renderItem={renderItem}
|
|
||||||
keyExtractor={keyExtractor}
|
|
||||||
refreshing={isPTRing}
|
|
||||||
headerOffset={headerOffset}
|
|
||||||
progressViewOffset={ios(0)}
|
|
||||||
contentContainerStyle={{
|
|
||||||
minHeight: height + headerOffset,
|
|
||||||
paddingBottom: bottomBarOffset,
|
|
||||||
}}
|
|
||||||
removeClippedSubviews={true}
|
|
||||||
desktopFixedHeight
|
|
||||||
onEndReached={onEndReached}
|
|
||||||
onRefresh={onRefresh}
|
|
||||||
ListEmptyComponent={
|
|
||||||
data ? (isMe ? EmptyComponent : undefined) : FeedLoadingPlaceholder
|
|
||||||
}
|
|
||||||
ListFooterComponent={
|
|
||||||
!!data && items?.length !== 0 && isMe ? CreateAnother : undefined
|
|
||||||
}
|
|
||||||
animatedProps={IS_IOS ? animatedProps : undefined}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View testID={testID} style={style}>
|
<View testID={testID} style={style}>
|
||||||
{IS_IOS ? (
|
<ScrollProvider {...scrollHandlers}>
|
||||||
<ScrollProvider
|
<List
|
||||||
onScroll={onScrollWorklet}
|
testID={testID ? `${testID}-flatlist` : undefined}
|
||||||
onBeginDrag={onBeginDragFromContext}
|
ref={scrollElRef}
|
||||||
onEndDrag={onEndDragFromContext}
|
data={items}
|
||||||
onMomentumBegin={onMomentumEndFromContext}
|
renderItem={renderItem}
|
||||||
onMomentumEnd={onMomentumEndFromContext}>
|
keyExtractor={keyExtractor}
|
||||||
{list}
|
refreshing={isPTRing}
|
||||||
</ScrollProvider>
|
headerOffset={headerOffset}
|
||||||
) : (
|
progressViewOffset={ios(0)}
|
||||||
list
|
contentContainerStyle={{
|
||||||
)}
|
minHeight: height + headerOffset,
|
||||||
|
paddingBottom: bottomBarOffset,
|
||||||
|
}}
|
||||||
|
removeClippedSubviews={true}
|
||||||
|
desktopFixedHeight
|
||||||
|
onEndReached={onEndReached}
|
||||||
|
onRefresh={onRefresh}
|
||||||
|
ListEmptyComponent={
|
||||||
|
data
|
||||||
|
? isMe
|
||||||
|
? EmptyComponent
|
||||||
|
: undefined
|
||||||
|
: FeedFeedLoadingPlaceholder
|
||||||
|
}
|
||||||
|
ListFooterComponent={
|
||||||
|
!!data && items?.length !== 0 && isMe ? CreateAnother : undefined
|
||||||
|
}
|
||||||
|
animatedProps={animatedProps}
|
||||||
|
/>
|
||||||
|
</ScrollProvider>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
import {useCallback, useEffect, useImperativeHandle, useMemo} from 'react'
|
import {useCallback, useEffect, useImperativeHandle, useMemo} from 'react'
|
||||||
import {findNodeHandle, type ListRenderItemInfo, View} from 'react-native'
|
import {findNodeHandle, type ListRenderItemInfo, View} from 'react-native'
|
||||||
import {
|
|
||||||
type ScrollHandler,
|
|
||||||
useAnimatedProps,
|
|
||||||
useSharedValue,
|
|
||||||
} from 'react-native-reanimated'
|
|
||||||
import {
|
import {
|
||||||
type AppBskyLabelerDefs,
|
type AppBskyLabelerDefs,
|
||||||
type InterpretedLabelValueDefinition,
|
type InterpretedLabelValueDefinition,
|
||||||
@@ -16,9 +11,9 @@ import {useLingui} from '@lingui/react'
|
|||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {isLabelerSubscribed, lookupLabelValueDefinition} from '#/lib/moderation'
|
import {isLabelerSubscribed, lookupLabelValueDefinition} from '#/lib/moderation'
|
||||||
import {ScrollProvider, useScrollHandlers} from '#/lib/ScrollContext'
|
import {ScrollProvider} from '#/lib/ScrollContext'
|
||||||
import {useShellLayout} from '#/state/shell/shell-layout'
|
|
||||||
import {List, type ListRef} from '#/view/com/util/List'
|
import {List, type ListRef} from '#/view/com/util/List'
|
||||||
|
import {useProfileScrollbarAdjustment} from '#/screens/Profile/useProfileScrollbarAdjustment'
|
||||||
import {atoms as a, ios, tokens, useTheme} from '#/alf'
|
import {atoms as a, ios, tokens, useTheme} from '#/alf'
|
||||||
import {Divider} from '#/components/Divider'
|
import {Divider} from '#/components/Divider'
|
||||||
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
||||||
@@ -57,39 +52,6 @@ export function ProfileLabelsSection({
|
|||||||
}: LabelsSectionProps) {
|
}: LabelsSectionProps) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
|
|
||||||
const {
|
|
||||||
onBeginDrag: onBeginDragFromContext,
|
|
||||||
onEndDrag: onEndDragFromContext,
|
|
||||||
onScroll: onScrollFromContext,
|
|
||||||
onMomentumEnd: onMomentumEndFromContext,
|
|
||||||
} = useScrollHandlers()
|
|
||||||
|
|
||||||
const scrollY = useSharedValue(0)
|
|
||||||
const onScrollWorklet = useCallback<ScrollHandler<any>>(
|
|
||||||
(e, ctx) => {
|
|
||||||
'worklet'
|
|
||||||
onScrollFromContext?.(e, ctx)
|
|
||||||
scrollY.set(e.contentOffset.y)
|
|
||||||
},
|
|
||||||
[onScrollFromContext, scrollY],
|
|
||||||
)
|
|
||||||
|
|
||||||
const {footerHeight} = useShellLayout()
|
|
||||||
|
|
||||||
const animatedProps = useAnimatedProps(() => {
|
|
||||||
if (IS_IOS) {
|
|
||||||
return {
|
|
||||||
scrollIndicatorInsets: {
|
|
||||||
top: Math.max(headerHeight - scrollY.get(), collapsedHeaderHeight),
|
|
||||||
right: 1,
|
|
||||||
left: 0,
|
|
||||||
bottom: footerHeight.get(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {}
|
|
||||||
})
|
|
||||||
|
|
||||||
const onScrollToTop = useCallback(() => {
|
const onScrollToTop = useCallback(() => {
|
||||||
scrollElRef.current?.scrollToOffset({
|
scrollElRef.current?.scrollToOffset({
|
||||||
animated: IS_NATIVE,
|
animated: IS_NATIVE,
|
||||||
@@ -159,45 +121,40 @@ export function ProfileLabelsSection({
|
|||||||
[labelerInfo, isSubscribed, numItems, t],
|
[labelerInfo, isSubscribed, numItems, t],
|
||||||
)
|
)
|
||||||
|
|
||||||
const list = (
|
const {scrollHandlers, animatedProps} = useProfileScrollbarAdjustment({
|
||||||
<List
|
headerOffset: headerHeight,
|
||||||
ref={scrollElRef}
|
collapsedHeaderHeight,
|
||||||
data={labelValues}
|
})
|
||||||
renderItem={renderItem}
|
|
||||||
keyExtractor={keyExtractor}
|
|
||||||
contentContainerStyle={a.px_xl}
|
|
||||||
headerOffset={headerHeight}
|
|
||||||
progressViewOffset={ios(0)}
|
|
||||||
ListHeaderComponent={
|
|
||||||
<LabelerListHeader
|
|
||||||
isLabelerLoading={isLabelerLoading}
|
|
||||||
labelerInfo={labelerInfo}
|
|
||||||
labelerError={labelerError}
|
|
||||||
hasValues={labelValues.length !== 0}
|
|
||||||
isSubscribed={isSubscribed}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
ListFooterComponent={
|
|
||||||
<ListFooter height={headerHeight + 180} style={a.border_transparent} />
|
|
||||||
}
|
|
||||||
animatedProps={IS_IOS ? animatedProps : undefined}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
{IS_IOS ? (
|
<ScrollProvider {...scrollHandlers}>
|
||||||
<ScrollProvider
|
<List
|
||||||
onScroll={onScrollWorklet}
|
ref={scrollElRef}
|
||||||
onBeginDrag={onBeginDragFromContext}
|
data={labelValues}
|
||||||
onEndDrag={onEndDragFromContext}
|
renderItem={renderItem}
|
||||||
onMomentumBegin={onMomentumEndFromContext}
|
keyExtractor={keyExtractor}
|
||||||
onMomentumEnd={onMomentumEndFromContext}>
|
contentContainerStyle={a.px_xl}
|
||||||
{list}
|
headerOffset={headerHeight}
|
||||||
</ScrollProvider>
|
progressViewOffset={ios(0)}
|
||||||
) : (
|
ListHeaderComponent={
|
||||||
list
|
<LabelerListHeader
|
||||||
)}
|
isLabelerLoading={isLabelerLoading}
|
||||||
|
labelerInfo={labelerInfo}
|
||||||
|
labelerError={labelerError}
|
||||||
|
hasValues={labelValues.length !== 0}
|
||||||
|
isSubscribed={isSubscribed}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
ListFooterComponent={
|
||||||
|
<ListFooter
|
||||||
|
height={headerHeight + 180}
|
||||||
|
style={a.border_transparent}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
animatedProps={animatedProps}
|
||||||
|
/>
|
||||||
|
</ScrollProvider>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
import {useCallback, useMemo} from 'react'
|
||||||
|
import {
|
||||||
|
type ScrollHandler,
|
||||||
|
useAnimatedProps,
|
||||||
|
useSharedValue,
|
||||||
|
} from 'react-native-reanimated'
|
||||||
|
|
||||||
|
import {useScrollHandlers} from '#/lib/ScrollContext'
|
||||||
|
import {useShellLayout} from '#/state/shell/shell-layout'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjusts the scroll indicator insets on iOS to account for the pager header.
|
||||||
|
* Adds another scroll offset listener, so use sparingly.
|
||||||
|
*
|
||||||
|
* HOW TO USE:
|
||||||
|
*
|
||||||
|
* ```tsx
|
||||||
|
* const { scrollHandlers, animatedProps } = useProfileScrollbarAdjustment({
|
||||||
|
* headerHeight: 600, // full size of the header
|
||||||
|
* collapsedHeaderHeight: 200, // height of the header when collapsed
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* return (
|
||||||
|
* <ScrollContext {...scrollHandlers}>
|
||||||
|
* <List animatedProps={animatedProps} />
|
||||||
|
* </ScrollContext>
|
||||||
|
* )
|
||||||
|
* ````
|
||||||
|
*/
|
||||||
|
export function useProfileScrollbarAdjustment({
|
||||||
|
enabled = true,
|
||||||
|
headerOffset,
|
||||||
|
collapsedHeaderHeight,
|
||||||
|
}: {
|
||||||
|
enabled?: boolean
|
||||||
|
headerOffset: number
|
||||||
|
collapsedHeaderHeight: number
|
||||||
|
}) {
|
||||||
|
const {onScroll: onScrollFromContext, ...otherScrollHandlers} =
|
||||||
|
useScrollHandlers()
|
||||||
|
|
||||||
|
const scrollY = useSharedValue(0)
|
||||||
|
const onScroll = useCallback<ScrollHandler<any>>(
|
||||||
|
(e, ctx) => {
|
||||||
|
'worklet'
|
||||||
|
onScrollFromContext?.(e, ctx)
|
||||||
|
if (enabled) {
|
||||||
|
scrollY.set(e.contentOffset.y)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[onScrollFromContext, scrollY, enabled],
|
||||||
|
)
|
||||||
|
|
||||||
|
const {footerHeight} = useShellLayout()
|
||||||
|
|
||||||
|
const animatedProps = useAnimatedProps(() => {
|
||||||
|
return {
|
||||||
|
scrollIndicatorInsets: {
|
||||||
|
top: enabled
|
||||||
|
? Math.max(headerOffset - scrollY.get(), collapsedHeaderHeight)
|
||||||
|
: headerOffset,
|
||||||
|
right: 1,
|
||||||
|
left: 0,
|
||||||
|
bottom: footerHeight.get(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const scrollHandlers = useMemo(
|
||||||
|
() => ({
|
||||||
|
onScroll,
|
||||||
|
...otherScrollHandlers,
|
||||||
|
}),
|
||||||
|
[onScroll, otherScrollHandlers],
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
scrollHandlers,
|
||||||
|
animatedProps,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import {useScrollHandlers} from '#/lib/ScrollContext'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjusts the scroll indicator insets on iOS to account for the pager header. Adds another scroll offset
|
||||||
|
*
|
||||||
|
* HOW TO USE:
|
||||||
|
*
|
||||||
|
* ```tsx
|
||||||
|
* const { scrollHandlers, animatedProps } = useScrollbarAdjustment({
|
||||||
|
* headerHeight: 600, // full size of the header
|
||||||
|
* collapsedHeaderHeight: 200, // height of the header when collapsed
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* return (
|
||||||
|
* <ScrollContext {...scrollHandlers}>
|
||||||
|
* <List animatedProps={animatedProps} />
|
||||||
|
* </ScrollContext>
|
||||||
|
* )
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @platform ios
|
||||||
|
*/
|
||||||
|
export function useProfileScrollbarAdjustment({}: {
|
||||||
|
enabled?: boolean
|
||||||
|
headerOffset: number
|
||||||
|
collapsedHeaderHeight: number
|
||||||
|
}): {
|
||||||
|
scrollHandlers: ReturnType<typeof useScrollHandlers>
|
||||||
|
animatedProps:
|
||||||
|
| undefined
|
||||||
|
| Partial<{
|
||||||
|
scrollIndicatorInsets: {
|
||||||
|
top: number
|
||||||
|
right: number
|
||||||
|
left: number
|
||||||
|
bottom: number
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
} {
|
||||||
|
// this is a no-op version for android/web
|
||||||
|
|
||||||
|
const scrollHandlers = useScrollHandlers()
|
||||||
|
|
||||||
|
const animatedProps = undefined
|
||||||
|
|
||||||
|
return {
|
||||||
|
scrollHandlers,
|
||||||
|
animatedProps,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,28 +13,23 @@ import {
|
|||||||
View,
|
View,
|
||||||
type ViewStyle,
|
type ViewStyle,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {
|
|
||||||
type ScrollHandler,
|
|
||||||
useAnimatedProps,
|
|
||||||
useSharedValue,
|
|
||||||
} from 'react-native-reanimated'
|
|
||||||
import {msg} from '@lingui/core/macro'
|
import {msg} from '@lingui/core/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {useNavigation} from '@react-navigation/native'
|
import {useNavigation} from '@react-navigation/native'
|
||||||
import {useQueryClient} from '@tanstack/react-query'
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {ScrollProvider, useScrollHandlers} from '#/lib/ScrollContext'
|
import {ScrollProvider} from '#/lib/ScrollContext'
|
||||||
import {cleanError} from '#/lib/strings/errors'
|
import {cleanError} from '#/lib/strings/errors'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||||
import {RQKEY, useProfileFeedgensQuery} from '#/state/queries/profile-feedgens'
|
import {RQKEY, useProfileFeedgensQuery} from '#/state/queries/profile-feedgens'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {useShellLayout} from '#/state/shell/shell-layout'
|
|
||||||
import {EmptyState} from '#/view/com/util/EmptyState'
|
import {EmptyState} from '#/view/com/util/EmptyState'
|
||||||
import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
|
import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
|
||||||
import {List, type ListRef} from '#/view/com/util/List'
|
import {List, type ListRef} from '#/view/com/util/List'
|
||||||
import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
import {FeedFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
||||||
import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn'
|
import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn'
|
||||||
|
import {useProfileScrollbarAdjustment} from '#/screens/Profile/useProfileScrollbarAdjustment'
|
||||||
import {atoms as a, ios, useTheme} from '#/alf'
|
import {atoms as a, ios, useTheme} from '#/alf'
|
||||||
import * as FeedCard from '#/components/FeedCard'
|
import * as FeedCard from '#/components/FeedCard'
|
||||||
import {HashtagWide_Stroke1_Corner0_Rounded as HashtagWideIcon} from '#/components/icons/Hashtag'
|
import {HashtagWide_Stroke1_Corner0_Rounded as HashtagWideIcon} from '#/components/icons/Hashtag'
|
||||||
@@ -77,38 +72,6 @@ export function ProfileFeedgens({
|
|||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const [isPTRing, setIsPTRing] = useState(false)
|
const [isPTRing, setIsPTRing] = useState(false)
|
||||||
|
|
||||||
const {
|
|
||||||
onBeginDrag: onBeginDragFromContext,
|
|
||||||
onEndDrag: onEndDragFromContext,
|
|
||||||
onScroll: onScrollFromContext,
|
|
||||||
onMomentumEnd: onMomentumEndFromContext,
|
|
||||||
} = useScrollHandlers()
|
|
||||||
|
|
||||||
const scrollY = useSharedValue(0)
|
|
||||||
const onScrollWorklet = useCallback<ScrollHandler<any>>(
|
|
||||||
(e, ctx) => {
|
|
||||||
'worklet'
|
|
||||||
onScrollFromContext?.(e, ctx)
|
|
||||||
scrollY.set(e.contentOffset.y)
|
|
||||||
},
|
|
||||||
[onScrollFromContext, scrollY],
|
|
||||||
)
|
|
||||||
|
|
||||||
const {footerHeight} = useShellLayout()
|
|
||||||
|
|
||||||
const animatedProps = useAnimatedProps(() => {
|
|
||||||
if (IS_IOS) {
|
|
||||||
return {
|
|
||||||
scrollIndicatorInsets: {
|
|
||||||
top: Math.max(headerOffset - scrollY.get(), collapsedHeaderHeight),
|
|
||||||
right: 1,
|
|
||||||
left: 0,
|
|
||||||
bottom: footerHeight.get(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {}
|
|
||||||
})
|
|
||||||
const {height} = useWindowDimensions()
|
const {height} = useWindowDimensions()
|
||||||
const opts = useMemo(() => ({enabled}), [enabled])
|
const opts = useMemo(() => ({enabled}), [enabled])
|
||||||
const {
|
const {
|
||||||
@@ -230,7 +193,7 @@ export function ProfileFeedgens({
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
} else if (item === LOADING) {
|
} else if (item === LOADING) {
|
||||||
return <FeedLoadingPlaceholder />
|
return <FeedFeedLoadingPlaceholder />
|
||||||
}
|
}
|
||||||
if (preferences) {
|
if (preferences) {
|
||||||
return (
|
return (
|
||||||
@@ -286,40 +249,32 @@ export function ProfileFeedgens({
|
|||||||
isEmpty,
|
isEmpty,
|
||||||
])
|
])
|
||||||
|
|
||||||
const list = (
|
const {scrollHandlers, animatedProps} = useProfileScrollbarAdjustment({
|
||||||
<List
|
headerOffset,
|
||||||
testID={testID ? `${testID}-flatlist` : undefined}
|
collapsedHeaderHeight,
|
||||||
ref={scrollElRef}
|
})
|
||||||
data={items}
|
|
||||||
keyExtractor={keyExtractor}
|
|
||||||
renderItem={renderItem}
|
|
||||||
ListFooterComponent={ProfileFeedgensFooter}
|
|
||||||
refreshing={isPTRing}
|
|
||||||
onRefresh={onRefresh}
|
|
||||||
headerOffset={headerOffset}
|
|
||||||
progressViewOffset={ios(0)}
|
|
||||||
removeClippedSubviews={true}
|
|
||||||
desktopFixedHeight
|
|
||||||
onEndReached={onEndReached}
|
|
||||||
contentContainerStyle={{minHeight: height + headerOffset}}
|
|
||||||
animatedProps={IS_IOS ? animatedProps : undefined}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View testID={testID} style={style}>
|
<View testID={testID} style={style}>
|
||||||
{IS_IOS ? (
|
<ScrollProvider {...scrollHandlers}>
|
||||||
<ScrollProvider
|
<List
|
||||||
onScroll={onScrollWorklet}
|
testID={testID ? `${testID}-flatlist` : undefined}
|
||||||
onBeginDrag={onBeginDragFromContext}
|
ref={scrollElRef}
|
||||||
onEndDrag={onEndDragFromContext}
|
data={items}
|
||||||
onMomentumBegin={onMomentumEndFromContext}
|
keyExtractor={keyExtractor}
|
||||||
onMomentumEnd={onMomentumEndFromContext}>
|
renderItem={renderItem}
|
||||||
{list}
|
ListFooterComponent={ProfileFeedgensFooter}
|
||||||
</ScrollProvider>
|
refreshing={isPTRing}
|
||||||
) : (
|
onRefresh={onRefresh}
|
||||||
list
|
headerOffset={headerOffset}
|
||||||
)}
|
progressViewOffset={ios(0)}
|
||||||
|
removeClippedSubviews={true}
|
||||||
|
desktopFixedHeight
|
||||||
|
onEndReached={onEndReached}
|
||||||
|
contentContainerStyle={{minHeight: height + headerOffset}}
|
||||||
|
animatedProps={animatedProps}
|
||||||
|
/>
|
||||||
|
</ScrollProvider>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,28 +13,23 @@ import {
|
|||||||
View,
|
View,
|
||||||
type ViewStyle,
|
type ViewStyle,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {
|
|
||||||
type ScrollHandler,
|
|
||||||
useAnimatedProps,
|
|
||||||
useSharedValue,
|
|
||||||
} from 'react-native-reanimated'
|
|
||||||
import {msg} from '@lingui/core/macro'
|
import {msg} from '@lingui/core/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {useNavigation} from '@react-navigation/native'
|
import {useNavigation} from '@react-navigation/native'
|
||||||
import {useQueryClient} from '@tanstack/react-query'
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {ScrollProvider, useScrollHandlers} from '#/lib/ScrollContext'
|
import {ScrollProvider} from '#/lib/ScrollContext'
|
||||||
import {cleanError} from '#/lib/strings/errors'
|
import {cleanError} from '#/lib/strings/errors'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||||
import {RQKEY, useProfileListsQuery} from '#/state/queries/profile-lists'
|
import {RQKEY, useProfileListsQuery} from '#/state/queries/profile-lists'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {useShellLayout} from '#/state/shell/shell-layout'
|
|
||||||
import {EmptyState} from '#/view/com/util/EmptyState'
|
import {EmptyState} from '#/view/com/util/EmptyState'
|
||||||
import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
|
import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
|
||||||
import {List, type ListRef} from '#/view/com/util/List'
|
import {List, type ListRef} from '#/view/com/util/List'
|
||||||
import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
import {FeedFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
||||||
import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn'
|
import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn'
|
||||||
|
import {useProfileScrollbarAdjustment} from '#/screens/Profile/useProfileScrollbarAdjustment'
|
||||||
import {atoms as a, ios, useTheme} from '#/alf'
|
import {atoms as a, ios, useTheme} from '#/alf'
|
||||||
import {BulletList_Stroke1_Corner0_Rounded as ListIcon} from '#/components/icons/BulletList'
|
import {BulletList_Stroke1_Corner0_Rounded as ListIcon} from '#/components/icons/BulletList'
|
||||||
import * as ListCard from '#/components/ListCard'
|
import * as ListCard from '#/components/ListCard'
|
||||||
@@ -77,38 +72,6 @@ export function ProfileLists({
|
|||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const [isPTRing, setIsPTRing] = useState(false)
|
const [isPTRing, setIsPTRing] = useState(false)
|
||||||
|
|
||||||
const {
|
|
||||||
onBeginDrag: onBeginDragFromContext,
|
|
||||||
onEndDrag: onEndDragFromContext,
|
|
||||||
onScroll: onScrollFromContext,
|
|
||||||
onMomentumEnd: onMomentumEndFromContext,
|
|
||||||
} = useScrollHandlers()
|
|
||||||
|
|
||||||
const scrollY = useSharedValue(0)
|
|
||||||
const onScrollWorklet = useCallback<ScrollHandler<any>>(
|
|
||||||
(e, ctx) => {
|
|
||||||
'worklet'
|
|
||||||
onScrollFromContext?.(e, ctx)
|
|
||||||
scrollY.set(e.contentOffset.y)
|
|
||||||
},
|
|
||||||
[onScrollFromContext, scrollY],
|
|
||||||
)
|
|
||||||
|
|
||||||
const {footerHeight} = useShellLayout()
|
|
||||||
|
|
||||||
const animatedProps = useAnimatedProps(() => {
|
|
||||||
if (IS_IOS) {
|
|
||||||
return {
|
|
||||||
scrollIndicatorInsets: {
|
|
||||||
top: Math.max(headerOffset - scrollY.get(), collapsedHeaderHeight),
|
|
||||||
right: 1,
|
|
||||||
left: 0,
|
|
||||||
bottom: footerHeight.get(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {}
|
|
||||||
})
|
|
||||||
const {height} = useWindowDimensions()
|
const {height} = useWindowDimensions()
|
||||||
const opts = useMemo(() => ({enabled}), [enabled])
|
const opts = useMemo(() => ({enabled}), [enabled])
|
||||||
const {
|
const {
|
||||||
@@ -229,7 +192,7 @@ export function ProfileLists({
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
} else if (item === LOADING) {
|
} else if (item === LOADING) {
|
||||||
return <FeedLoadingPlaceholder />
|
return <FeedFeedLoadingPlaceholder />
|
||||||
}
|
}
|
||||||
if (preferences) {
|
if (preferences) {
|
||||||
return (
|
return (
|
||||||
@@ -285,40 +248,32 @@ export function ProfileLists({
|
|||||||
isEmpty,
|
isEmpty,
|
||||||
])
|
])
|
||||||
|
|
||||||
const list = (
|
const {scrollHandlers, animatedProps} = useProfileScrollbarAdjustment({
|
||||||
<List
|
headerOffset,
|
||||||
testID={testID ? `${testID}-flatlist` : undefined}
|
collapsedHeaderHeight,
|
||||||
ref={scrollElRef}
|
})
|
||||||
data={items}
|
|
||||||
keyExtractor={keyExtractor}
|
|
||||||
renderItem={renderItem}
|
|
||||||
ListFooterComponent={ProfileListsFooter}
|
|
||||||
refreshing={isPTRing}
|
|
||||||
onRefresh={onRefresh}
|
|
||||||
headerOffset={headerOffset}
|
|
||||||
progressViewOffset={ios(0)}
|
|
||||||
removeClippedSubviews={true}
|
|
||||||
desktopFixedHeight
|
|
||||||
onEndReached={onEndReached}
|
|
||||||
contentContainerStyle={{minHeight: height + headerOffset}}
|
|
||||||
animatedProps={IS_IOS ? animatedProps : undefined}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View testID={testID} style={style}>
|
<View testID={testID} style={style}>
|
||||||
{IS_IOS ? (
|
<ScrollProvider {...scrollHandlers}>
|
||||||
<ScrollProvider
|
<List
|
||||||
onScroll={onScrollWorklet}
|
testID={testID ? `${testID}-flatlist` : undefined}
|
||||||
onBeginDrag={onBeginDragFromContext}
|
ref={scrollElRef}
|
||||||
onEndDrag={onEndDragFromContext}
|
data={items}
|
||||||
onMomentumBegin={onMomentumEndFromContext}
|
keyExtractor={keyExtractor}
|
||||||
onMomentumEnd={onMomentumEndFromContext}>
|
renderItem={renderItem}
|
||||||
{list}
|
ListFooterComponent={ProfileListsFooter}
|
||||||
</ScrollProvider>
|
refreshing={isPTRing}
|
||||||
) : (
|
onRefresh={onRefresh}
|
||||||
list
|
headerOffset={headerOffset}
|
||||||
)}
|
progressViewOffset={ios(0)}
|
||||||
|
removeClippedSubviews={true}
|
||||||
|
desktopFixedHeight
|
||||||
|
onEndReached={onEndReached}
|
||||||
|
contentContainerStyle={{minHeight: height + headerOffset}}
|
||||||
|
animatedProps={animatedProps}
|
||||||
|
/>
|
||||||
|
</ScrollProvider>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,6 @@ import {
|
|||||||
View,
|
View,
|
||||||
type ViewStyle,
|
type ViewStyle,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {
|
|
||||||
type ScrollHandler,
|
|
||||||
useAnimatedProps,
|
|
||||||
useSharedValue,
|
|
||||||
} from 'react-native-reanimated'
|
|
||||||
import {
|
import {
|
||||||
type AppBskyActorDefs,
|
type AppBskyActorDefs,
|
||||||
AppBskyEmbedVideo,
|
AppBskyEmbedVideo,
|
||||||
@@ -26,7 +21,7 @@ import {useQueryClient} from '@tanstack/react-query'
|
|||||||
import {DISCOVER_FEED_URI, KNOWN_SHUTDOWN_FEEDS} from '#/lib/constants'
|
import {DISCOVER_FEED_URI, KNOWN_SHUTDOWN_FEEDS} from '#/lib/constants'
|
||||||
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
|
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
|
||||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||||
import {ScrollProvider, useScrollHandlers} from '#/lib/ScrollContext'
|
import {ScrollProvider} from '#/lib/ScrollContext'
|
||||||
import {isNetworkError} from '#/lib/strings/errors'
|
import {isNetworkError} from '#/lib/strings/errors'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {usePostAuthorShadowFilter} from '#/state/cache/profile-shadow'
|
import {usePostAuthorShadowFilter} from '#/state/cache/profile-shadow'
|
||||||
@@ -48,10 +43,10 @@ import {truncateAndInvalidate} from '#/state/queries/util'
|
|||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {useProgressGuide} from '#/state/shell/progress-guide'
|
import {useProgressGuide} from '#/state/shell/progress-guide'
|
||||||
import {useSelectedFeed} from '#/state/shell/selected-feed'
|
import {useSelectedFeed} from '#/state/shell/selected-feed'
|
||||||
import {useShellLayout} from '#/state/shell/shell-layout'
|
|
||||||
import {List, type ListRef} from '#/view/com/util/List'
|
import {List, type ListRef} from '#/view/com/util/List'
|
||||||
import {PostFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
import {PostFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
||||||
import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn'
|
import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn'
|
||||||
|
import {useProfileScrollbarAdjustment} from '#/screens/Profile/useProfileScrollbarAdjustment'
|
||||||
import {type VideoFeedSourceContext} from '#/screens/VideoFeed/types'
|
import {type VideoFeedSourceContext} from '#/screens/VideoFeed/types'
|
||||||
import {useBreakpoints, useLayoutBreakpoints} from '#/alf'
|
import {useBreakpoints, useLayoutBreakpoints} from '#/alf'
|
||||||
import {
|
import {
|
||||||
@@ -211,7 +206,7 @@ let PostFeed = ({
|
|||||||
savedFeedConfig,
|
savedFeedConfig,
|
||||||
initialNumToRender: initialNumToRenderOverride,
|
initialNumToRender: initialNumToRenderOverride,
|
||||||
isVideoFeed = false,
|
isVideoFeed = false,
|
||||||
adjustScrollIndicators,
|
adjustScrollIndicators = false,
|
||||||
collapsedHeaderHeight = 0,
|
collapsedHeaderHeight = 0,
|
||||||
}: {
|
}: {
|
||||||
feed: FeedDescriptor
|
feed: FeedDescriptor
|
||||||
@@ -1010,88 +1005,46 @@ let PostFeed = ({
|
|||||||
[feedFeedback, feed, liveNowConfig, getPostPosition, ax],
|
[feedFeedback, feed, liveNowConfig, getPostPosition, ax],
|
||||||
)
|
)
|
||||||
|
|
||||||
const {
|
const {scrollHandlers, animatedProps} = useProfileScrollbarAdjustment({
|
||||||
onBeginDrag: onBeginDragFromContext,
|
enabled: adjustScrollIndicators,
|
||||||
onEndDrag: onEndDragFromContext,
|
headerOffset,
|
||||||
onScroll: onScrollFromContext,
|
collapsedHeaderHeight,
|
||||||
onMomentumEnd: onMomentumEndFromContext,
|
|
||||||
} = useScrollHandlers()
|
|
||||||
|
|
||||||
const scrollY = useSharedValue(0)
|
|
||||||
const onScrollWorklet = useCallback<ScrollHandler<any>>(
|
|
||||||
(e, ctx) => {
|
|
||||||
'worklet'
|
|
||||||
onScrollFromContext?.(e, ctx)
|
|
||||||
scrollY.set(e.contentOffset.y)
|
|
||||||
},
|
|
||||||
[onScrollFromContext, scrollY],
|
|
||||||
)
|
|
||||||
|
|
||||||
const {footerHeight} = useShellLayout()
|
|
||||||
|
|
||||||
const animatedProps = useAnimatedProps(() => {
|
|
||||||
if (IS_IOS) {
|
|
||||||
return {
|
|
||||||
scrollIndicatorInsets: {
|
|
||||||
top: adjustScrollIndicators
|
|
||||||
? Math.max(headerOffset - scrollY.get(), collapsedHeaderHeight)
|
|
||||||
: headerOffset,
|
|
||||||
right: 1,
|
|
||||||
left: 0,
|
|
||||||
bottom: footerHeight.get(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const list = (
|
|
||||||
<List
|
|
||||||
testID={testID ? `${testID}-flatlist` : undefined}
|
|
||||||
ref={scrollElRef}
|
|
||||||
data={feedItems}
|
|
||||||
keyExtractor={(item: FeedRow) => item.key}
|
|
||||||
renderItem={renderItem}
|
|
||||||
ListFooterComponent={FeedFooter}
|
|
||||||
ListHeaderComponent={ListHeaderComponent}
|
|
||||||
refreshing={isPTRing}
|
|
||||||
onRefresh={() => void onRefresh()}
|
|
||||||
headerOffset={headerOffset}
|
|
||||||
progressViewOffset={progressViewOffset}
|
|
||||||
contentContainerStyle={{
|
|
||||||
minHeight: Dimensions.get('window').height * 1.5,
|
|
||||||
}}
|
|
||||||
onScrolledDownChange={handleScrolledDownChange}
|
|
||||||
onEndReached={() => void onEndReached()}
|
|
||||||
onEndReachedThreshold={2} // number of posts left to trigger load more
|
|
||||||
removeClippedSubviews={true}
|
|
||||||
extraData={extraData}
|
|
||||||
desktopFixedHeight={
|
|
||||||
desktopFixedHeightOffset ? desktopFixedHeightOffset : true
|
|
||||||
}
|
|
||||||
initialNumToRender={initialNumToRenderOverride ?? initialNumToRender}
|
|
||||||
windowSize={9}
|
|
||||||
maxToRenderPerBatch={IS_IOS ? 5 : 1}
|
|
||||||
updateCellsBatchingPeriod={40}
|
|
||||||
onItemSeen={onItemSeen}
|
|
||||||
animatedProps={IS_IOS ? animatedProps : undefined}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View testID={testID} style={style}>
|
<View testID={testID} style={style}>
|
||||||
{IS_IOS ? (
|
<ScrollProvider {...scrollHandlers}>
|
||||||
<ScrollProvider
|
<List
|
||||||
onScroll={onScrollWorklet}
|
testID={testID ? `${testID}-flatlist` : undefined}
|
||||||
onBeginDrag={onBeginDragFromContext}
|
ref={scrollElRef}
|
||||||
onEndDrag={onEndDragFromContext}
|
data={feedItems}
|
||||||
onMomentumBegin={onMomentumEndFromContext}
|
keyExtractor={(item: FeedRow) => item.key}
|
||||||
onMomentumEnd={onMomentumEndFromContext}>
|
renderItem={renderItem}
|
||||||
{list}
|
ListFooterComponent={FeedFooter}
|
||||||
</ScrollProvider>
|
ListHeaderComponent={ListHeaderComponent}
|
||||||
) : (
|
refreshing={isPTRing}
|
||||||
list
|
onRefresh={() => void onRefresh()}
|
||||||
)}
|
headerOffset={headerOffset}
|
||||||
|
progressViewOffset={progressViewOffset}
|
||||||
|
contentContainerStyle={{
|
||||||
|
minHeight: Dimensions.get('window').height * 1.5,
|
||||||
|
}}
|
||||||
|
onScrolledDownChange={handleScrolledDownChange}
|
||||||
|
onEndReached={() => void onEndReached()}
|
||||||
|
onEndReachedThreshold={2} // number of posts left to trigger load more
|
||||||
|
removeClippedSubviews={true}
|
||||||
|
extraData={extraData}
|
||||||
|
desktopFixedHeight={
|
||||||
|
desktopFixedHeightOffset ? desktopFixedHeightOffset : true
|
||||||
|
}
|
||||||
|
initialNumToRender={initialNumToRenderOverride ?? initialNumToRender}
|
||||||
|
windowSize={9}
|
||||||
|
maxToRenderPerBatch={IS_IOS ? 5 : 1}
|
||||||
|
updateCellsBatchingPeriod={40}
|
||||||
|
onItemSeen={onItemSeen}
|
||||||
|
animatedProps={animatedProps}
|
||||||
|
/>
|
||||||
|
</ScrollProvider>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user