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