Replace onMainScroll with MainScrollProvider
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import React, {createContext, useContext, useMemo} from 'react'
|
||||
import {ScrollHandlers} from 'react-native-reanimated'
|
||||
|
||||
const ScrollContext = createContext<ScrollHandlers<any>>({
|
||||
onBeginDrag: undefined,
|
||||
onEndDrag: undefined,
|
||||
onScroll: undefined,
|
||||
})
|
||||
|
||||
export function useScrollHandlers(): ScrollHandlers<any> {
|
||||
return useContext(ScrollContext)
|
||||
}
|
||||
|
||||
type ProviderProps = {children: React.ReactNode} & ScrollHandlers<any>
|
||||
|
||||
// Note: this completely *overrides* the parent handlers.
|
||||
// It's up to you to compose them with the parent ones via useScrollHandlers() if needed.
|
||||
export function ScrollProvider({
|
||||
children,
|
||||
onBeginDrag,
|
||||
onEndDrag,
|
||||
onScroll,
|
||||
}: ProviderProps) {
|
||||
const handlers = useMemo(
|
||||
() => ({
|
||||
onBeginDrag,
|
||||
onEndDrag,
|
||||
onScroll,
|
||||
}),
|
||||
[onBeginDrag, onEndDrag, onScroll],
|
||||
)
|
||||
return (
|
||||
<ScrollContext.Provider value={handlers}>{children}</ScrollContext.Provider>
|
||||
)
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import {useNavigation} from '@react-navigation/native'
|
||||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
|
||||
import {useOnMainScroll} from 'lib/hooks/useOnMainScroll'
|
||||
import {MainScrollProvider} from '../util/MainScrollProvider'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
@@ -54,7 +54,6 @@ export function FeedPage({
|
||||
const queryClient = useQueryClient()
|
||||
const {openComposer} = useComposerControls()
|
||||
const [isScrolledDown, setIsScrolledDown] = React.useState(false)
|
||||
const onMainScroll = useOnMainScroll()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const {screen, track} = useAnalytics()
|
||||
const headerOffset = useHeaderOffset()
|
||||
@@ -168,20 +167,22 @@ export function FeedPage({
|
||||
|
||||
return (
|
||||
<View testID={testID} style={s.h100pct}>
|
||||
<Feed
|
||||
testID={testID ? `${testID}-feed` : undefined}
|
||||
enabled={isPageFocused}
|
||||
feed={feed}
|
||||
feedParams={feedParams}
|
||||
pollInterval={POLL_FREQ}
|
||||
scrollElRef={scrollElRef}
|
||||
onScrolledDownChange={setIsScrolledDown}
|
||||
onHasNew={setHasNew}
|
||||
renderEmptyState={renderEmptyState}
|
||||
renderEndOfFeed={renderEndOfFeed}
|
||||
ListHeaderComponent={ListHeaderComponent}
|
||||
headerOffset={headerOffset}
|
||||
/>
|
||||
<MainScrollProvider>
|
||||
<Feed
|
||||
testID={testID ? `${testID}-feed` : undefined}
|
||||
enabled={isPageFocused}
|
||||
feed={feed}
|
||||
feedParams={feedParams}
|
||||
pollInterval={POLL_FREQ}
|
||||
scrollElRef={scrollElRef}
|
||||
onScrolledDownChange={setIsScrolledDown}
|
||||
onHasNew={setHasNew}
|
||||
renderEmptyState={renderEmptyState}
|
||||
renderEndOfFeed={renderEndOfFeed}
|
||||
ListHeaderComponent={ListHeaderComponent}
|
||||
headerOffset={headerOffset}
|
||||
/>
|
||||
</MainScrollProvider>
|
||||
{(isScrolledDown || hasNew) && (
|
||||
<LoadLatestBtn
|
||||
onPress={onPressLoadLatest}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, {useState} from 'react'
|
||||
import {FlatListProps} from 'react-native'
|
||||
import {FlatList_INTERNAL} from './Views'
|
||||
import {useScrollHandlers} from '#/lib/ScrollContext'
|
||||
import {runOnJS} from 'react-native-reanimated'
|
||||
import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED'
|
||||
|
||||
@@ -17,19 +18,26 @@ function ListImpl<ItemT>(
|
||||
ref: React.Ref<ListMethods>,
|
||||
) {
|
||||
const [isScrolledDown, setIsScrolledDown] = useState(false)
|
||||
const scrollHandlers = useScrollHandlers()
|
||||
|
||||
function handleScrolledDownChange(didScrollDown: boolean) {
|
||||
setIsScrolledDown(didScrollDown)
|
||||
onScrolledDownChange?.(didScrollDown)
|
||||
}
|
||||
|
||||
// TODO: This ignores the passed-in onScroll completely.
|
||||
const scrollHandler = useAnimatedScrollHandler({
|
||||
onScroll(e) {
|
||||
onBeginDrag(e, ctx) {
|
||||
scrollHandlers.onBeginDrag?.(e, ctx)
|
||||
},
|
||||
onEndDrag(e, ctx) {
|
||||
scrollHandlers.onEndDrag?.(e, ctx)
|
||||
},
|
||||
onScroll(e, ctx) {
|
||||
const didScrollDown = e.contentOffset.y > SCROLLED_DOWN_LIMIT
|
||||
if (isScrolledDown !== didScrollDown) {
|
||||
runOnJS(handleScrolledDownChange)(didScrollDown)
|
||||
}
|
||||
scrollHandlers.onScroll?.(e, ctx)
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
import {useCallback, useMemo} from 'react'
|
||||
import React, {useCallback} from 'react'
|
||||
import {ScrollProvider} from '#/lib/ScrollContext'
|
||||
import {NativeScrollEvent} from 'react-native'
|
||||
import {useSetMinimalShellMode, useMinimalShellMode} from '#/state/shell'
|
||||
import {useShellLayout} from '#/state/shell/shell-layout'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import {
|
||||
useSharedValue,
|
||||
interpolate,
|
||||
ScrollHandlers,
|
||||
} from 'react-native-reanimated'
|
||||
import {useSharedValue, interpolate} from 'react-native-reanimated'
|
||||
|
||||
function clamp(num: number, min: number, max: number) {
|
||||
'worklet'
|
||||
return Math.min(Math.max(num, min), max)
|
||||
}
|
||||
|
||||
export function useOnMainScroll(): ScrollHandlers<any> {
|
||||
export function MainScrollProvider({children}: {children: React.ReactNode}) {
|
||||
const {headerHeight} = useShellLayout()
|
||||
const mode = useMinimalShellMode()
|
||||
const setMode = useSetMinimalShellMode()
|
||||
@@ -89,14 +86,12 @@ export function useOnMainScroll(): ScrollHandlers<any> {
|
||||
[headerHeight, mode, setMode, startDragOffset, startMode],
|
||||
)
|
||||
|
||||
const scrollHandler: ScrollHandlers<any> = useMemo(
|
||||
() => ({
|
||||
onBeginDrag,
|
||||
onEndDrag,
|
||||
onScroll,
|
||||
}),
|
||||
[onBeginDrag, onEndDrag, onScroll],
|
||||
return (
|
||||
<ScrollProvider
|
||||
onBeginDrag={onBeginDrag}
|
||||
onEndDrag={onEndDrag}
|
||||
onScroll={onScroll}>
|
||||
{children}
|
||||
</ScrollProvider>
|
||||
)
|
||||
|
||||
return scrollHandler
|
||||
}
|
||||
@@ -11,7 +11,7 @@ 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 {MainScrollProvider} from '../com/util/MainScrollProvider'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {s, colors} from 'lib/styles'
|
||||
@@ -36,7 +36,6 @@ type Props = NativeStackScreenProps<
|
||||
export function NotificationsScreen({}: Props) {
|
||||
const {_} = useLingui()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const onMainScroll = useOnMainScroll()
|
||||
const [isScrolledDown, setIsScrolledDown] = React.useState(false)
|
||||
const scrollElRef = React.useRef<ListMethods>(null)
|
||||
const checkLatestRef = React.useRef<() => void | null>()
|
||||
@@ -132,11 +131,13 @@ export function NotificationsScreen({}: Props) {
|
||||
return (
|
||||
<View testID="notificationsScreen" style={s.hContentRegion}>
|
||||
<ViewHeader title={_(msg`Notifications`)} canGoBack={false} />
|
||||
<Feed
|
||||
onScrolledDownChange={setIsScrolledDown}
|
||||
scrollElRef={scrollElRef}
|
||||
ListHeaderComponent={ListHeaderComponent}
|
||||
/>
|
||||
<MainScrollProvider>
|
||||
<Feed
|
||||
onScrolledDownChange={setIsScrolledDown}
|
||||
scrollElRef={scrollElRef}
|
||||
ListHeaderComponent={ListHeaderComponent}
|
||||
/>
|
||||
</MainScrollProvider>
|
||||
{(isScrolledDown || hasNew) && (
|
||||
<LoadLatestBtn
|
||||
onPress={onPressLoadLatest}
|
||||
|
||||
Reference in New Issue
Block a user