Add animated scroll indicator insets to remaining profile tabs

Apply the same scroll-position-aware indicator inset pattern to Labels,
Lists, Feeds, and Starter Packs tabs. Each component now intercepts scroll
events via ScrollProvider, tracks scrollY, and uses collapsedHeaderHeight
to clamp the iOS scroll indicator top inset.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Samuel Newman
2026-02-17 11:49:57 +02:00
parent 4c374eb616
commit 8485ac65df
5 changed files with 290 additions and 85 deletions
@@ -7,6 +7,11 @@ 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'
@@ -18,9 +23,11 @@ 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 {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 {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,
@@ -47,6 +54,7 @@ interface ProfileFeedgensProps {
scrollElRef: ListRef scrollElRef: ListRef
did: string did: string
headerOffset: number headerOffset: number
collapsedHeaderHeight?: number
enabled?: boolean enabled?: boolean
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
testID?: string testID?: string
@@ -66,6 +74,7 @@ export function ProfileStarterPacks({
scrollElRef, scrollElRef,
did, did,
headerOffset, headerOffset,
collapsedHeaderHeight = 0,
enabled, enabled,
style, style,
testID, testID,
@@ -79,6 +88,39 @@ 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 {
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,
@@ -161,30 +203,38 @@ export function ProfileStarterPacks({
return ( return (
<View testID={testID} style={style}> <View testID={testID} style={style}>
<List <ScrollProvider
testID={testID ? `${testID}-flatlist` : undefined} onScroll={onScrollWorklet}
ref={scrollElRef} onBeginDrag={onBeginDragFromContext}
data={items} onEndDrag={onEndDragFromContext}
renderItem={renderItem} onMomentumBegin={onMomentumEndFromContext}
keyExtractor={keyExtractor} onMomentumEnd={onMomentumEndFromContext}>
refreshing={isPTRing} <List
headerOffset={headerOffset} testID={testID ? `${testID}-flatlist` : undefined}
progressViewOffset={ios(0)} ref={scrollElRef}
contentContainerStyle={{ data={items}
minHeight: height + headerOffset, renderItem={renderItem}
paddingBottom: bottomBarOffset, keyExtractor={keyExtractor}
}} refreshing={isPTRing}
removeClippedSubviews={true} headerOffset={headerOffset}
desktopFixedHeight progressViewOffset={ios(0)}
onEndReached={onEndReached} contentContainerStyle={{
onRefresh={onRefresh} minHeight: height + headerOffset,
ListEmptyComponent={ paddingBottom: bottomBarOffset,
data ? (isMe ? EmptyComponent : undefined) : FeedLoadingPlaceholder }}
} removeClippedSubviews={true}
ListFooterComponent={ desktopFixedHeight
!!data && items?.length !== 0 && isMe ? CreateAnother : undefined onEndReached={onEndReached}
} onRefresh={onRefresh}
/> ListEmptyComponent={
data ? (isMe ? EmptyComponent : undefined) : FeedLoadingPlaceholder
}
ListFooterComponent={
!!data && items?.length !== 0 && isMe ? CreateAnother : undefined
}
animatedProps={animatedProps}
/>
</ScrollProvider>
</View> </View>
) )
} }
+74 -24
View File
@@ -1,5 +1,10 @@
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,
@@ -11,6 +16,8 @@ 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 {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 {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'
@@ -31,6 +38,7 @@ interface LabelsSectionProps {
moderationOpts: ModerationOpts moderationOpts: ModerationOpts
scrollElRef: ListRef scrollElRef: ListRef
headerHeight: number headerHeight: number
collapsedHeaderHeight: number
isFocused: boolean isFocused: boolean
setScrollViewTag: (tag: number | null) => void setScrollViewTag: (tag: number | null) => void
} }
@@ -43,11 +51,45 @@ export function ProfileLabelsSection({
moderationOpts, moderationOpts,
scrollElRef, scrollElRef,
headerHeight, headerHeight,
collapsedHeaderHeight,
isFocused, isFocused,
setScrollViewTag, setScrollViewTag,
}: 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,
@@ -119,30 +161,38 @@ export function ProfileLabelsSection({
return ( return (
<View> <View>
<List <ScrollProvider
ref={scrollElRef} onScroll={onScrollWorklet}
data={labelValues} onBeginDrag={onBeginDragFromContext}
renderItem={renderItem} onEndDrag={onEndDragFromContext}
keyExtractor={keyExtractor} onMomentumBegin={onMomentumEndFromContext}
contentContainerStyle={a.px_xl} onMomentumEnd={onMomentumEndFromContext}>
headerOffset={headerHeight} <List
progressViewOffset={ios(0)} ref={scrollElRef}
ListHeaderComponent={ data={labelValues}
<LabelerListHeader renderItem={renderItem}
isLabelerLoading={isLabelerLoading} keyExtractor={keyExtractor}
labelerInfo={labelerInfo} contentContainerStyle={a.px_xl}
labelerError={labelerError} headerOffset={headerHeight}
hasValues={labelValues.length !== 0} progressViewOffset={ios(0)}
isSubscribed={isSubscribed} ListHeaderComponent={
/> <LabelerListHeader
} isLabelerLoading={isLabelerLoading}
ListFooterComponent={ labelerInfo={labelerInfo}
<ListFooter labelerError={labelerError}
height={headerHeight + 180} hasValues={labelValues.length !== 0}
style={a.border_transparent} isSubscribed={isSubscribed}
/> />
} }
/> ListFooterComponent={
<ListFooter
height={headerHeight + 180}
style={a.border_transparent}
/>
}
animatedProps={animatedProps}
/>
</ScrollProvider>
</View> </View>
) )
} }
+66 -16
View File
@@ -13,16 +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 {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'
@@ -48,6 +55,7 @@ interface ProfileFeedgensProps {
did: string did: string
scrollElRef: ListRef scrollElRef: ListRef
headerOffset: number headerOffset: number
collapsedHeaderHeight?: number
enabled?: boolean enabled?: boolean
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
testID?: string testID?: string
@@ -59,6 +67,7 @@ export function ProfileFeedgens({
did, did,
scrollElRef, scrollElRef,
headerOffset, headerOffset,
collapsedHeaderHeight = 0,
enabled, enabled,
style, style,
testID, testID,
@@ -67,6 +76,39 @@ export function ProfileFeedgens({
const {_} = useLingui() const {_} = useLingui()
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 {
@@ -246,22 +288,30 @@ export function ProfileFeedgens({
return ( return (
<View testID={testID} style={style}> <View testID={testID} style={style}>
<List <ScrollProvider
testID={testID ? `${testID}-flatlist` : undefined} onScroll={onScrollWorklet}
ref={scrollElRef} onBeginDrag={onBeginDragFromContext}
data={items} onEndDrag={onEndDragFromContext}
keyExtractor={keyExtractor} onMomentumBegin={onMomentumEndFromContext}
renderItem={renderItem} onMomentumEnd={onMomentumEndFromContext}>
ListFooterComponent={ProfileFeedgensFooter} <List
refreshing={isPTRing} testID={testID ? `${testID}-flatlist` : undefined}
onRefresh={onRefresh} ref={scrollElRef}
headerOffset={headerOffset} data={items}
progressViewOffset={ios(0)} keyExtractor={keyExtractor}
removeClippedSubviews={true} renderItem={renderItem}
desktopFixedHeight ListFooterComponent={ProfileFeedgensFooter}
onEndReached={onEndReached} refreshing={isPTRing}
contentContainerStyle={{minHeight: height + headerOffset}} onRefresh={onRefresh}
/> headerOffset={headerOffset}
progressViewOffset={ios(0)}
removeClippedSubviews={true}
desktopFixedHeight
onEndReached={onEndReached}
contentContainerStyle={{minHeight: height + headerOffset}}
animatedProps={animatedProps}
/>
</ScrollProvider>
</View> </View>
) )
} }
+66 -16
View File
@@ -13,16 +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 {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'
@@ -48,6 +55,7 @@ interface ProfileListsProps {
did: string did: string
scrollElRef: ListRef scrollElRef: ListRef
headerOffset: number headerOffset: number
collapsedHeaderHeight?: number
enabled?: boolean enabled?: boolean
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
testID?: string testID?: string
@@ -59,6 +67,7 @@ export function ProfileLists({
did, did,
scrollElRef, scrollElRef,
headerOffset, headerOffset,
collapsedHeaderHeight = 0,
enabled, enabled,
style, style,
testID, testID,
@@ -67,6 +76,39 @@ export function ProfileLists({
const {_} = useLingui() const {_} = useLingui()
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 {
@@ -245,22 +287,30 @@ export function ProfileLists({
return ( return (
<View testID={testID} style={style}> <View testID={testID} style={style}>
<List <ScrollProvider
testID={testID ? `${testID}-flatlist` : undefined} onScroll={onScrollWorklet}
ref={scrollElRef} onBeginDrag={onBeginDragFromContext}
data={items} onEndDrag={onEndDragFromContext}
keyExtractor={keyExtractor} onMomentumBegin={onMomentumEndFromContext}
renderItem={renderItem} onMomentumEnd={onMomentumEndFromContext}>
ListFooterComponent={ProfileListsFooter} <List
refreshing={isPTRing} testID={testID ? `${testID}-flatlist` : undefined}
onRefresh={onRefresh} ref={scrollElRef}
headerOffset={headerOffset} data={items}
progressViewOffset={ios(0)} keyExtractor={keyExtractor}
removeClippedSubviews={true} renderItem={renderItem}
desktopFixedHeight ListFooterComponent={ProfileListsFooter}
onEndReached={onEndReached} refreshing={isPTRing}
contentContainerStyle={{minHeight: height + headerOffset}} onRefresh={onRefresh}
/> headerOffset={headerOffset}
progressViewOffset={ios(0)}
removeClippedSubviews={true}
desktopFixedHeight
onEndReached={onEndReached}
contentContainerStyle={{minHeight: height + headerOffset}}
animatedProps={animatedProps}
/>
</ScrollProvider>
</View> </View>
) )
} }
+10 -5
View File
@@ -393,7 +393,7 @@ function ProfileScreenLoaded({
renderHeader={renderHeader} renderHeader={renderHeader}
allowHeaderOverScroll> allowHeaderOverScroll>
{showFiltersTab {showFiltersTab
? ({headerHeight, isFocused, scrollElRef}) => ( ? ({headerHeight, collapsedHeaderHeight, isFocused, scrollElRef}) => (
<ProfileLabelsSection <ProfileLabelsSection
ref={labelsSectionRef} ref={labelsSectionRef}
labelerInfo={labelerInfo} labelerInfo={labelerInfo}
@@ -402,18 +402,20 @@ function ProfileScreenLoaded({
moderationOpts={moderationOpts} moderationOpts={moderationOpts}
scrollElRef={scrollElRef as ListRef} scrollElRef={scrollElRef as ListRef}
headerHeight={headerHeight} headerHeight={headerHeight}
collapsedHeaderHeight={collapsedHeaderHeight}
isFocused={isFocused} isFocused={isFocused}
setScrollViewTag={setScrollViewTag} setScrollViewTag={setScrollViewTag}
/> />
) )
: null} : null}
{showListsTab && !!profile.associated?.labeler {showListsTab && !!profile.associated?.labeler
? ({headerHeight, isFocused, scrollElRef}) => ( ? ({headerHeight, collapsedHeaderHeight, isFocused, scrollElRef}) => (
<ProfileLists <ProfileLists
ref={listsSectionRef} ref={listsSectionRef}
did={profile.did} did={profile.did}
scrollElRef={scrollElRef as ListRef} scrollElRef={scrollElRef as ListRef}
headerOffset={headerHeight} headerOffset={headerHeight}
collapsedHeaderHeight={collapsedHeaderHeight}
enabled={isFocused} enabled={isFocused}
setScrollViewTag={setScrollViewTag} setScrollViewTag={setScrollViewTag}
/> />
@@ -535,25 +537,27 @@ function ProfileScreenLoaded({
) )
: null} : null}
{showFeedsTab {showFeedsTab
? ({headerHeight, isFocused, scrollElRef}) => ( ? ({headerHeight, collapsedHeaderHeight, isFocused, scrollElRef}) => (
<ProfileFeedgens <ProfileFeedgens
ref={feedsSectionRef} ref={feedsSectionRef}
did={profile.did} did={profile.did}
scrollElRef={scrollElRef as ListRef} scrollElRef={scrollElRef as ListRef}
headerOffset={headerHeight} headerOffset={headerHeight}
collapsedHeaderHeight={collapsedHeaderHeight}
enabled={isFocused} enabled={isFocused}
setScrollViewTag={setScrollViewTag} setScrollViewTag={setScrollViewTag}
/> />
) )
: null} : null}
{showStarterPacksTab {showStarterPacksTab
? ({headerHeight, isFocused, scrollElRef}) => ( ? ({headerHeight, collapsedHeaderHeight, isFocused, scrollElRef}) => (
<ProfileStarterPacks <ProfileStarterPacks
ref={starterPacksSectionRef} ref={starterPacksSectionRef}
did={profile.did} did={profile.did}
isMe={isMe} isMe={isMe}
scrollElRef={scrollElRef as ListRef} scrollElRef={scrollElRef as ListRef}
headerOffset={headerHeight} headerOffset={headerHeight}
collapsedHeaderHeight={collapsedHeaderHeight}
enabled={isFocused} enabled={isFocused}
setScrollViewTag={setScrollViewTag} setScrollViewTag={setScrollViewTag}
emptyStateMessage={ emptyStateMessage={
@@ -579,12 +583,13 @@ function ProfileScreenLoaded({
) )
: null} : null}
{showListsTab && !profile.associated?.labeler {showListsTab && !profile.associated?.labeler
? ({headerHeight, isFocused, scrollElRef}) => ( ? ({headerHeight, collapsedHeaderHeight, isFocused, scrollElRef}) => (
<ProfileLists <ProfileLists
ref={listsSectionRef} ref={listsSectionRef}
did={profile.did} did={profile.did}
scrollElRef={scrollElRef as ListRef} scrollElRef={scrollElRef as ListRef}
headerOffset={headerHeight} headerOffset={headerHeight}
collapsedHeaderHeight={collapsedHeaderHeight}
enabled={isFocused} enabled={isFocused}
setScrollViewTag={setScrollViewTag} setScrollViewTag={setScrollViewTag}
/> />