Scroll sync in the pager without jumps (#1863)
This commit is contained in:
parent
65def37165
commit
91f8a23fbc
6 changed files with 160 additions and 87 deletions
|
@ -1,6 +1,7 @@
|
||||||
import React, {MutableRefObject} from 'react'
|
import React, {MutableRefObject} from 'react'
|
||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
|
Dimensions,
|
||||||
RefreshControl,
|
RefreshControl,
|
||||||
StyleProp,
|
StyleProp,
|
||||||
View,
|
View,
|
||||||
|
@ -18,7 +19,6 @@ import {ListModel} from 'state/models/content/list'
|
||||||
import {useAnalytics} from 'lib/analytics/analytics'
|
import {useAnalytics} from 'lib/analytics/analytics'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
import {s} from 'lib/styles'
|
|
||||||
import {OnScrollHandler} from 'lib/hooks/useOnMainScroll'
|
import {OnScrollHandler} from 'lib/hooks/useOnMainScroll'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {useModalControls} from '#/state/modals'
|
import {useModalControls} from '#/state/modals'
|
||||||
|
@ -226,7 +226,9 @@ export const ListItems = observer(function ListItemsImpl({
|
||||||
progressViewOffset={headerOffset}
|
progressViewOffset={headerOffset}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
contentContainerStyle={s.contentContainer}
|
contentContainerStyle={{
|
||||||
|
paddingBottom: Dimensions.get('window').height - headerOffset,
|
||||||
|
}}
|
||||||
style={{paddingTop: headerOffset}}
|
style={{paddingTop: headerOffset}}
|
||||||
onScroll={scrollHandler}
|
onScroll={scrollHandler}
|
||||||
onEndReached={onEndReached}
|
onEndReached={onEndReached}
|
||||||
|
|
|
@ -49,7 +49,18 @@ export const Pager = React.forwardRef(function PagerImpl(
|
||||||
onSelect: onTabBarSelect,
|
onSelect: onTabBarSelect,
|
||||||
})}
|
})}
|
||||||
{React.Children.map(children, (child, i) => (
|
{React.Children.map(children, (child, i) => (
|
||||||
<View style={selectedPage === i ? s.flex1 : s.hidden} key={`page-${i}`}>
|
<View
|
||||||
|
style={
|
||||||
|
selectedPage === i
|
||||||
|
? s.flex1
|
||||||
|
: {
|
||||||
|
position: 'absolute',
|
||||||
|
pointerEvents: 'none',
|
||||||
|
// @ts-ignore web-only
|
||||||
|
visibility: 'hidden',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
key={`page-${i}`}>
|
||||||
{child}
|
{child}
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import {
|
import {
|
||||||
LayoutChangeEvent,
|
LayoutChangeEvent,
|
||||||
NativeScrollEvent,
|
FlatList,
|
||||||
|
ScrollView,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
View,
|
View,
|
||||||
|
NativeScrollEvent,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import Animated, {
|
import Animated, {
|
||||||
Easing,
|
|
||||||
useAnimatedReaction,
|
|
||||||
useAnimatedStyle,
|
useAnimatedStyle,
|
||||||
useSharedValue,
|
useSharedValue,
|
||||||
withTiming,
|
|
||||||
runOnJS,
|
runOnJS,
|
||||||
|
scrollTo,
|
||||||
|
useAnimatedRef,
|
||||||
|
AnimatedRef,
|
||||||
} from 'react-native-reanimated'
|
} from 'react-native-reanimated'
|
||||||
import {Pager, PagerRef, RenderTabBarFnProps} from 'view/com/pager/Pager'
|
import {Pager, PagerRef, RenderTabBarFnProps} from 'view/com/pager/Pager'
|
||||||
import {TabBar} from './TabBar'
|
import {TabBar} from './TabBar'
|
||||||
|
@ -24,6 +26,7 @@ interface PagerWithHeaderChildParams {
|
||||||
headerHeight: number
|
headerHeight: number
|
||||||
onScroll: OnScrollHandler
|
onScroll: OnScrollHandler
|
||||||
isScrolledDown: boolean
|
isScrolledDown: boolean
|
||||||
|
scrollElRef: React.MutableRefObject<FlatList<any> | ScrollView | null>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PagerWithHeaderProps {
|
export interface PagerWithHeaderProps {
|
||||||
|
@ -54,28 +57,12 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
||||||
) {
|
) {
|
||||||
const {isMobile} = useWebMediaQueries()
|
const {isMobile} = useWebMediaQueries()
|
||||||
const [currentPage, setCurrentPage] = React.useState(0)
|
const [currentPage, setCurrentPage] = React.useState(0)
|
||||||
const scrollYs = React.useRef<Record<number, number>>({})
|
|
||||||
const scrollY = useSharedValue(scrollYs.current[currentPage] || 0)
|
|
||||||
const [tabBarHeight, setTabBarHeight] = React.useState(0)
|
const [tabBarHeight, setTabBarHeight] = React.useState(0)
|
||||||
const [headerOnlyHeight, setHeaderOnlyHeight] = React.useState(0)
|
const [headerOnlyHeight, setHeaderOnlyHeight] = React.useState(0)
|
||||||
const [isScrolledDown, setIsScrolledDown] = React.useState(
|
const [isScrolledDown, setIsScrolledDown] = React.useState(false)
|
||||||
scrollYs.current[currentPage] > SCROLLED_DOWN_LIMIT,
|
const scrollY = useSharedValue(0)
|
||||||
)
|
|
||||||
|
|
||||||
const headerHeight = headerOnlyHeight + tabBarHeight
|
const headerHeight = headerOnlyHeight + tabBarHeight
|
||||||
|
|
||||||
// react to scroll updates
|
|
||||||
function onScrollUpdate(v: number) {
|
|
||||||
// track each page's current scroll position
|
|
||||||
scrollYs.current[currentPage] = Math.min(v, headerOnlyHeight)
|
|
||||||
// update the 'is scrolled down' value
|
|
||||||
setIsScrolledDown(v > SCROLLED_DOWN_LIMIT)
|
|
||||||
}
|
|
||||||
useAnimatedReaction(
|
|
||||||
() => scrollY.value,
|
|
||||||
v => runOnJS(onScrollUpdate)(v),
|
|
||||||
)
|
|
||||||
|
|
||||||
// capture the header bar sizing
|
// capture the header bar sizing
|
||||||
const onTabBarLayout = React.useCallback(
|
const onTabBarLayout = React.useCallback(
|
||||||
(evt: LayoutChangeEvent) => {
|
(evt: LayoutChangeEvent) => {
|
||||||
|
@ -91,19 +78,17 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
||||||
)
|
)
|
||||||
|
|
||||||
// render the the header and tab bar
|
// render the the header and tab bar
|
||||||
const headerTransform = useAnimatedStyle(
|
const headerTransform = useAnimatedStyle(() => ({
|
||||||
() => ({
|
transform: [
|
||||||
transform: [
|
{
|
||||||
{
|
translateY: Math.min(
|
||||||
translateY: Math.min(
|
Math.min(scrollY.value, headerOnlyHeight) * -1,
|
||||||
Math.min(scrollY.value, headerOnlyHeight) * -1,
|
0,
|
||||||
0,
|
),
|
||||||
),
|
},
|
||||||
},
|
],
|
||||||
],
|
}))
|
||||||
}),
|
|
||||||
[scrollY, headerHeight, tabBarHeight],
|
|
||||||
)
|
|
||||||
const renderTabBar = React.useCallback(
|
const renderTabBar = React.useCallback(
|
||||||
(props: RenderTabBarFnProps) => {
|
(props: RenderTabBarFnProps) => {
|
||||||
return (
|
return (
|
||||||
|
@ -144,12 +129,38 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
// props to pass into children render functions
|
const scrollRefs = useSharedValue<AnimatedRef<any>[]>([])
|
||||||
function onScrollWorklet(e: NativeScrollEvent) {
|
const registerRef = (scrollRef: AnimatedRef<any>, index: number) => {
|
||||||
'worklet'
|
scrollRefs.modify(refs => {
|
||||||
scrollY.value = e.contentOffset.y
|
'worklet'
|
||||||
|
refs[index] = scrollRef
|
||||||
|
return refs
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onScrollWorklet = React.useCallback(
|
||||||
|
(e: NativeScrollEvent) => {
|
||||||
|
'worklet'
|
||||||
|
const nextScrollY = e.contentOffset.y
|
||||||
|
scrollY.value = nextScrollY
|
||||||
|
|
||||||
|
if (nextScrollY < headerOnlyHeight) {
|
||||||
|
const refs = scrollRefs.value
|
||||||
|
for (let i = 0; i < refs.length; i++) {
|
||||||
|
if (i !== currentPage) {
|
||||||
|
scrollTo(refs[i], 0, nextScrollY, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextIsScrolledDown = nextScrollY > SCROLLED_DOWN_LIMIT
|
||||||
|
if (isScrolledDown !== nextIsScrolledDown) {
|
||||||
|
runOnJS(setIsScrolledDown)(nextIsScrolledDown)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[currentPage, headerOnlyHeight, isScrolledDown, scrollRefs, scrollY],
|
||||||
|
)
|
||||||
|
|
||||||
const onPageSelectedInner = React.useCallback(
|
const onPageSelectedInner = React.useCallback(
|
||||||
(index: number) => {
|
(index: number) => {
|
||||||
setCurrentPage(index)
|
setCurrentPage(index)
|
||||||
|
@ -158,19 +169,9 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
||||||
[onPageSelected, setCurrentPage],
|
[onPageSelected, setCurrentPage],
|
||||||
)
|
)
|
||||||
|
|
||||||
const onPageSelecting = React.useCallback(
|
const onPageSelecting = React.useCallback((index: number) => {
|
||||||
(index: number) => {
|
setCurrentPage(index)
|
||||||
setCurrentPage(index)
|
}, [])
|
||||||
if (scrollY.value > headerHeight) {
|
|
||||||
scrollY.value = headerHeight
|
|
||||||
}
|
|
||||||
scrollY.value = withTiming(scrollYs.current[index] || 0, {
|
|
||||||
duration: 170,
|
|
||||||
easing: Easing.inOut(Easing.quad),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[scrollY, setCurrentPage, scrollYs, headerHeight],
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Pager
|
<Pager
|
||||||
|
@ -184,26 +185,18 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
||||||
{toArray(children)
|
{toArray(children)
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.map((child, i) => {
|
.map((child, i) => {
|
||||||
let output = null
|
const isReady =
|
||||||
if (
|
isHeaderReady && headerOnlyHeight > 0 && tabBarHeight > 0
|
||||||
child != null &&
|
|
||||||
// Defer showing content until we know it won't jump.
|
|
||||||
isHeaderReady &&
|
|
||||||
headerOnlyHeight > 0 &&
|
|
||||||
tabBarHeight > 0
|
|
||||||
) {
|
|
||||||
output = child({
|
|
||||||
headerHeight,
|
|
||||||
isScrolledDown,
|
|
||||||
onScroll: {
|
|
||||||
onScroll: i === currentPage ? onScrollWorklet : noop,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// Pager children must be noncollapsible plain <View>s.
|
|
||||||
return (
|
return (
|
||||||
<View key={i} collapsable={false}>
|
<View key={i} collapsable={false}>
|
||||||
{output}
|
<PagerItem
|
||||||
|
headerHeight={headerHeight}
|
||||||
|
isReady={isReady}
|
||||||
|
isScrolledDown={isScrolledDown}
|
||||||
|
onScrollWorklet={i === currentPage ? onScrollWorklet : noop}
|
||||||
|
registerRef={(r: AnimatedRef<any>) => registerRef(r, i)}
|
||||||
|
renderTab={child}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
@ -212,6 +205,43 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
function PagerItem({
|
||||||
|
headerHeight,
|
||||||
|
isReady,
|
||||||
|
isScrolledDown,
|
||||||
|
onScrollWorklet,
|
||||||
|
renderTab,
|
||||||
|
registerRef,
|
||||||
|
}: {
|
||||||
|
headerHeight: number
|
||||||
|
isReady: boolean
|
||||||
|
isScrolledDown: boolean
|
||||||
|
registerRef: (scrollRef: AnimatedRef<any>) => void
|
||||||
|
onScrollWorklet: (e: NativeScrollEvent) => void
|
||||||
|
renderTab: ((props: PagerWithHeaderChildParams) => JSX.Element) | null
|
||||||
|
}) {
|
||||||
|
const scrollElRef = useAnimatedRef()
|
||||||
|
registerRef(scrollElRef)
|
||||||
|
|
||||||
|
const scrollHandler = React.useMemo(
|
||||||
|
() => ({onScroll: onScrollWorklet}),
|
||||||
|
[onScrollWorklet],
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!isReady || renderTab == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderTab({
|
||||||
|
headerHeight,
|
||||||
|
isScrolledDown,
|
||||||
|
onScroll: scrollHandler,
|
||||||
|
scrollElRef: scrollElRef as React.MutableRefObject<
|
||||||
|
FlatList<any> | ScrollView | null
|
||||||
|
>,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
tabBarMobile: {
|
tabBarMobile: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
|
|
|
@ -2,6 +2,7 @@ import React, {MutableRefObject} from 'react'
|
||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
|
Dimensions,
|
||||||
RefreshControl,
|
RefreshControl,
|
||||||
StyleProp,
|
StyleProp,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
|
@ -15,7 +16,6 @@ import {PostsFeedModel} from 'state/models/feeds/posts'
|
||||||
import {FeedSlice} from './FeedSlice'
|
import {FeedSlice} from './FeedSlice'
|
||||||
import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
|
import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
|
||||||
import {OnScrollHandler} from 'lib/hooks/useOnMainScroll'
|
import {OnScrollHandler} from 'lib/hooks/useOnMainScroll'
|
||||||
import {s} from 'lib/styles'
|
|
||||||
import {useAnalytics} from 'lib/analytics/analytics'
|
import {useAnalytics} from 'lib/analytics/analytics'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED'
|
import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED'
|
||||||
|
@ -178,7 +178,9 @@ export const Feed = observer(function Feed({
|
||||||
progressViewOffset={headerOffset}
|
progressViewOffset={headerOffset}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
contentContainerStyle={s.contentContainer}
|
contentContainerStyle={{
|
||||||
|
paddingBottom: Dimensions.get('window').height - headerOffset,
|
||||||
|
}}
|
||||||
style={{paddingTop: headerOffset}}
|
style={{paddingTop: headerOffset}}
|
||||||
onScroll={onScroll != null ? scrollHandler : undefined}
|
onScroll={onScroll != null ? scrollHandler : undefined}
|
||||||
scrollEventThrottle={scrollEventThrottle}
|
scrollEventThrottle={scrollEventThrottle}
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
import React, {useMemo, useCallback} from 'react'
|
import React, {useMemo, useCallback} from 'react'
|
||||||
import {FlatList, StyleSheet, View, ActivityIndicator} from 'react-native'
|
import {
|
||||||
|
Dimensions,
|
||||||
|
StyleSheet,
|
||||||
|
View,
|
||||||
|
ActivityIndicator,
|
||||||
|
FlatList,
|
||||||
|
} from 'react-native'
|
||||||
import {NativeStackScreenProps} from '@react-navigation/native-stack'
|
import {NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||||
import {useNavigation} from '@react-navigation/native'
|
import {useNavigation} from '@react-navigation/native'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
|
@ -343,16 +349,19 @@ export const ProfileFeedScreenInner = observer(
|
||||||
isHeaderReady={feedInfo?.hasLoaded ?? false}
|
isHeaderReady={feedInfo?.hasLoaded ?? false}
|
||||||
renderHeader={renderHeader}
|
renderHeader={renderHeader}
|
||||||
onCurrentPageSelected={onCurrentPageSelected}>
|
onCurrentPageSelected={onCurrentPageSelected}>
|
||||||
{({onScroll, headerHeight, isScrolledDown}) => (
|
{({onScroll, headerHeight, isScrolledDown, scrollElRef}) => (
|
||||||
<FeedSection
|
<FeedSection
|
||||||
ref={feedSectionRef}
|
ref={feedSectionRef}
|
||||||
feed={feed}
|
feed={feed}
|
||||||
onScroll={onScroll}
|
onScroll={onScroll}
|
||||||
headerHeight={headerHeight}
|
headerHeight={headerHeight}
|
||||||
isScrolledDown={isScrolledDown}
|
isScrolledDown={isScrolledDown}
|
||||||
|
scrollElRef={
|
||||||
|
scrollElRef as React.MutableRefObject<FlatList<any> | null>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{({onScroll, headerHeight}) => (
|
{({onScroll, headerHeight, scrollElRef}) => (
|
||||||
<AboutSection
|
<AboutSection
|
||||||
feedOwnerDid={feedOwnerDid}
|
feedOwnerDid={feedOwnerDid}
|
||||||
feedRkey={rkey}
|
feedRkey={rkey}
|
||||||
|
@ -360,6 +369,9 @@ export const ProfileFeedScreenInner = observer(
|
||||||
headerHeight={headerHeight}
|
headerHeight={headerHeight}
|
||||||
onToggleLiked={onToggleLiked}
|
onToggleLiked={onToggleLiked}
|
||||||
onScroll={onScroll}
|
onScroll={onScroll}
|
||||||
|
scrollElRef={
|
||||||
|
scrollElRef as React.MutableRefObject<ScrollView | null>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</PagerWithHeader>
|
</PagerWithHeader>
|
||||||
|
@ -387,14 +399,14 @@ interface FeedSectionProps {
|
||||||
onScroll: OnScrollHandler
|
onScroll: OnScrollHandler
|
||||||
headerHeight: number
|
headerHeight: number
|
||||||
isScrolledDown: boolean
|
isScrolledDown: boolean
|
||||||
|
scrollElRef: React.MutableRefObject<FlatList<any> | null>
|
||||||
}
|
}
|
||||||
const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
||||||
function FeedSectionImpl(
|
function FeedSectionImpl(
|
||||||
{feed, onScroll, headerHeight, isScrolledDown},
|
{feed, onScroll, headerHeight, isScrolledDown, scrollElRef},
|
||||||
ref,
|
ref,
|
||||||
) {
|
) {
|
||||||
const hasNew = feed.hasNewLatest && !feed.isRefreshing
|
const hasNew = feed.hasNewLatest && !feed.isRefreshing
|
||||||
const scrollElRef = React.useRef<FlatList>(null)
|
|
||||||
|
|
||||||
const onScrollToTop = useCallback(() => {
|
const onScrollToTop = useCallback(() => {
|
||||||
scrollElRef.current?.scrollToOffset({offset: -headerHeight})
|
scrollElRef.current?.scrollToOffset({offset: -headerHeight})
|
||||||
|
@ -438,6 +450,7 @@ const AboutSection = observer(function AboutPageImpl({
|
||||||
headerHeight,
|
headerHeight,
|
||||||
onToggleLiked,
|
onToggleLiked,
|
||||||
onScroll,
|
onScroll,
|
||||||
|
scrollElRef,
|
||||||
}: {
|
}: {
|
||||||
feedOwnerDid: string
|
feedOwnerDid: string
|
||||||
feedRkey: string
|
feedRkey: string
|
||||||
|
@ -445,6 +458,7 @@ const AboutSection = observer(function AboutPageImpl({
|
||||||
headerHeight: number
|
headerHeight: number
|
||||||
onToggleLiked: () => void
|
onToggleLiked: () => void
|
||||||
onScroll: OnScrollHandler
|
onScroll: OnScrollHandler
|
||||||
|
scrollElRef: React.MutableRefObject<ScrollView | null>
|
||||||
}) {
|
}) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
|
@ -456,8 +470,12 @@ const AboutSection = observer(function AboutPageImpl({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollView
|
<ScrollView
|
||||||
|
ref={scrollElRef}
|
||||||
scrollEventThrottle={1}
|
scrollEventThrottle={1}
|
||||||
contentContainerStyle={{paddingTop: headerHeight}}
|
contentContainerStyle={{
|
||||||
|
paddingTop: headerHeight,
|
||||||
|
paddingBottom: Dimensions.get('window').height - headerHeight,
|
||||||
|
}}
|
||||||
onScroll={scrollHandler}>
|
onScroll={scrollHandler}>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
|
|
|
@ -175,18 +175,24 @@ export const ProfileListScreenInner = observer(
|
||||||
isHeaderReady={list.hasLoaded}
|
isHeaderReady={list.hasLoaded}
|
||||||
renderHeader={renderHeader}
|
renderHeader={renderHeader}
|
||||||
onCurrentPageSelected={onCurrentPageSelected}>
|
onCurrentPageSelected={onCurrentPageSelected}>
|
||||||
{({onScroll, headerHeight, isScrolledDown}) => (
|
{({onScroll, headerHeight, isScrolledDown, scrollElRef}) => (
|
||||||
<FeedSection
|
<FeedSection
|
||||||
ref={feedSectionRef}
|
ref={feedSectionRef}
|
||||||
|
scrollElRef={
|
||||||
|
scrollElRef as React.MutableRefObject<FlatList<any> | null>
|
||||||
|
}
|
||||||
feed={feed}
|
feed={feed}
|
||||||
onScroll={onScroll}
|
onScroll={onScroll}
|
||||||
headerHeight={headerHeight}
|
headerHeight={headerHeight}
|
||||||
isScrolledDown={isScrolledDown}
|
isScrolledDown={isScrolledDown}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{({onScroll, headerHeight, isScrolledDown}) => (
|
{({onScroll, headerHeight, isScrolledDown, scrollElRef}) => (
|
||||||
<AboutSection
|
<AboutSection
|
||||||
ref={aboutSectionRef}
|
ref={aboutSectionRef}
|
||||||
|
scrollElRef={
|
||||||
|
scrollElRef as React.MutableRefObject<FlatList<any> | null>
|
||||||
|
}
|
||||||
list={list}
|
list={list}
|
||||||
descriptionRT={list.descriptionRT}
|
descriptionRT={list.descriptionRT}
|
||||||
creator={list.data ? list.data.creator : undefined}
|
creator={list.data ? list.data.creator : undefined}
|
||||||
|
@ -223,9 +229,12 @@ export const ProfileListScreenInner = observer(
|
||||||
items={SECTION_TITLES_MOD}
|
items={SECTION_TITLES_MOD}
|
||||||
isHeaderReady={list.hasLoaded}
|
isHeaderReady={list.hasLoaded}
|
||||||
renderHeader={renderHeader}>
|
renderHeader={renderHeader}>
|
||||||
{({onScroll, headerHeight, isScrolledDown}) => (
|
{({onScroll, headerHeight, isScrolledDown, scrollElRef}) => (
|
||||||
<AboutSection
|
<AboutSection
|
||||||
list={list}
|
list={list}
|
||||||
|
scrollElRef={
|
||||||
|
scrollElRef as React.MutableRefObject<FlatList<any> | null>
|
||||||
|
}
|
||||||
descriptionRT={list.descriptionRT}
|
descriptionRT={list.descriptionRT}
|
||||||
creator={list.data ? list.data.creator : undefined}
|
creator={list.data ? list.data.creator : undefined}
|
||||||
isCurateList={list.isCuratelist}
|
isCurateList={list.isCuratelist}
|
||||||
|
@ -557,14 +566,14 @@ interface FeedSectionProps {
|
||||||
onScroll: OnScrollHandler
|
onScroll: OnScrollHandler
|
||||||
headerHeight: number
|
headerHeight: number
|
||||||
isScrolledDown: boolean
|
isScrolledDown: boolean
|
||||||
|
scrollElRef: React.MutableRefObject<FlatList<any> | null>
|
||||||
}
|
}
|
||||||
const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
||||||
function FeedSectionImpl(
|
function FeedSectionImpl(
|
||||||
{feed, onScroll, headerHeight, isScrolledDown},
|
{feed, scrollElRef, onScroll, headerHeight, isScrolledDown},
|
||||||
ref,
|
ref,
|
||||||
) {
|
) {
|
||||||
const hasNew = feed.hasNewLatest && !feed.isRefreshing
|
const hasNew = feed.hasNewLatest && !feed.isRefreshing
|
||||||
const scrollElRef = React.useRef<FlatList>(null)
|
|
||||||
|
|
||||||
const onScrollToTop = useCallback(() => {
|
const onScrollToTop = useCallback(() => {
|
||||||
scrollElRef.current?.scrollToOffset({offset: -headerHeight})
|
scrollElRef.current?.scrollToOffset({offset: -headerHeight})
|
||||||
|
@ -611,6 +620,7 @@ interface AboutSectionProps {
|
||||||
onScroll: OnScrollHandler
|
onScroll: OnScrollHandler
|
||||||
headerHeight: number
|
headerHeight: number
|
||||||
isScrolledDown: boolean
|
isScrolledDown: boolean
|
||||||
|
scrollElRef: React.MutableRefObject<FlatList<any> | null>
|
||||||
}
|
}
|
||||||
const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>(
|
const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>(
|
||||||
function AboutSectionImpl(
|
function AboutSectionImpl(
|
||||||
|
@ -624,13 +634,13 @@ const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>(
|
||||||
onScroll,
|
onScroll,
|
||||||
headerHeight,
|
headerHeight,
|
||||||
isScrolledDown,
|
isScrolledDown,
|
||||||
|
scrollElRef,
|
||||||
},
|
},
|
||||||
ref,
|
ref,
|
||||||
) {
|
) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {isMobile} = useWebMediaQueries()
|
const {isMobile} = useWebMediaQueries()
|
||||||
const scrollElRef = React.useRef<FlatList>(null)
|
|
||||||
|
|
||||||
const onScrollToTop = useCallback(() => {
|
const onScrollToTop = useCallback(() => {
|
||||||
scrollElRef.current?.scrollToOffset({offset: -headerHeight})
|
scrollElRef.current?.scrollToOffset({offset: -headerHeight})
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue