diff --git a/src/lib/hooks/useOnMainScroll.ts b/src/lib/hooks/useOnMainScroll.ts index 2e7a799137..491a9a71ba 100644 --- a/src/lib/hooks/useOnMainScroll.ts +++ b/src/lib/hooks/useOnMainScroll.ts @@ -1,13 +1,11 @@ -import {useState, useCallback, useMemo} from 'react' +import {useCallback, useMemo} from 'react' import {NativeSyntheticEvent, NativeScrollEvent} from 'react-native' import {useSetMinimalShellMode, useMinimalShellMode} from '#/state/shell' import {useShellLayout} from '#/state/shell/shell-layout' -import {s} from 'lib/styles' import {isWeb} from 'platform/detection' import { useSharedValue, interpolate, - runOnJS, ScrollHandlers, } from 'react-native-reanimated' @@ -22,9 +20,8 @@ export type OnScrollCb = ( export type OnScrollHandler = ScrollHandlers export type ResetCb = () => void -export function useOnMainScroll(): [OnScrollHandler, boolean, ResetCb] { +export function useOnMainScroll(): [OnScrollHandler, ResetCb] { const {headerHeight} = useShellLayout() - const [isScrolledDown, setIsScrolledDown] = useState(false) const mode = useMinimalShellMode() const setMode = useSetMinimalShellMode() const startDragOffset = useSharedValue(null) @@ -58,13 +55,6 @@ export function useOnMainScroll(): [OnScrollHandler, boolean, ResetCb] { const onScroll = useCallback( (e: NativeScrollEvent) => { 'worklet' - // Keep track of whether we want to show "scroll to top". - if (!isScrolledDown && e.contentOffset.y > s.window.height) { - runOnJS(setIsScrolledDown)(true) - } else if (isScrolledDown && e.contentOffset.y < s.window.height) { - runOnJS(setIsScrolledDown)(false) - } - if (startDragOffset.value === null || startMode.value === null) { if (mode.value !== 0 && e.contentOffset.y < headerHeight.value) { // If we're close enough to the top, always show the shell. @@ -102,7 +92,7 @@ export function useOnMainScroll(): [OnScrollHandler, boolean, ResetCb] { startMode.value = mode.value } }, - [headerHeight, mode, setMode, isScrolledDown, startDragOffset, startMode], + [headerHeight, mode, setMode, startDragOffset, startMode], ) const scrollHandler: ScrollHandlers = useMemo( @@ -116,9 +106,7 @@ export function useOnMainScroll(): [OnScrollHandler, boolean, ResetCb] { return [ scrollHandler, - isScrolledDown, useCallback(() => { - setIsScrolledDown(false) setMode(false) }, [setMode]), ] diff --git a/src/view/com/feeds/FeedPage.tsx b/src/view/com/feeds/FeedPage.tsx index a64809ca70..458c0c72de 100644 --- a/src/view/com/feeds/FeedPage.tsx +++ b/src/view/com/feeds/FeedPage.tsx @@ -52,7 +52,8 @@ export function FeedPage({ const {isDesktop} = useWebMediaQueries() const queryClient = useQueryClient() const {openComposer} = useComposerControls() - const [onMainScroll, isScrolledDown, resetMainScroll] = useOnMainScroll() + const [isScrolledDown, setIsScrolledDown] = React.useState(false) + const [onMainScroll, resetMainScroll] = useOnMainScroll() const {screen, track} = useAnalytics() const headerOffset = useHeaderOffset() const scrollElRef = React.useRef(null) @@ -173,6 +174,7 @@ export function FeedPage({ pollInterval={POLL_FREQ} scrollElRef={scrollElRef} onScroll={onMainScroll} + onScrolledDownChange={setIsScrolledDown} onHasNew={setHasNew} scrollEventThrottle={1} renderEmptyState={renderEmptyState} diff --git a/src/view/com/notifications/Feed.tsx b/src/view/com/notifications/Feed.tsx index 1701dde313..8dec5d5d12 100644 --- a/src/view/com/notifications/Feed.tsx +++ b/src/view/com/notifications/Feed.tsx @@ -25,11 +25,13 @@ export function Feed({ scrollElRef, onPressTryAgain, onScroll, + onScrolledDownChange, ListHeaderComponent, }: { scrollElRef?: ListRef onPressTryAgain?: () => void onScroll?: OnScrollHandler + onScrolledDownChange: (isScrolledDown: boolean) => void ListHeaderComponent?: () => JSX.Element }) { const pal = usePalette('default') @@ -166,6 +168,7 @@ export function Feed({ onEndReached={onEndReached} onEndReachedThreshold={0.6} onScroll={scrollHandler} + onScrolledDownChange={onScrolledDownChange} scrollEventThrottle={1} contentContainerStyle={s.contentContainer} // @ts-ignore our .web version only -prf diff --git a/src/view/com/posts/Feed.tsx b/src/view/com/posts/Feed.tsx index 14dbd7fed8..081ff10f78 100644 --- a/src/view/com/posts/Feed.tsx +++ b/src/view/com/posts/Feed.tsx @@ -46,6 +46,7 @@ let Feed = ({ pollInterval, scrollElRef, onScroll, + onScrolledDownChange, onHasNew, scrollEventThrottle, renderEmptyState, @@ -65,6 +66,7 @@ let Feed = ({ scrollElRef?: ListRef onHasNew?: (v: boolean) => void onScroll?: OnScrollHandler + onScrolledDownChange?: (isScrolledDown: boolean) => void scrollEventThrottle?: number renderEmptyState: () => JSX.Element renderEndOfFeed?: () => JSX.Element @@ -295,6 +297,7 @@ let Feed = ({ }} style={{paddingTop: headerOffset}} onScroll={onScroll != null ? scrollHandler : undefined} + onScrolledDownChange={onScrolledDownChange} scrollEventThrottle={scrollEventThrottle} indicatorStyle={theme.colorScheme === 'dark' ? 'white' : 'black'} onEndReached={onEndReached} diff --git a/src/view/com/util/List.tsx b/src/view/com/util/List.tsx index 19b43d5455..e5ff2892cf 100644 --- a/src/view/com/util/List.tsx +++ b/src/view/com/util/List.tsx @@ -1,13 +1,34 @@ -import React from 'react' +import React, {useState} from 'react' import {FlatListProps} from 'react-native' import {FlatList_INTERNAL} from './Views' +import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED' export type ListMethods = FlatList_INTERNAL -export type ListProps = FlatListProps +export type ListProps = FlatListProps & { + onScrolledDownChange?: (isScrolledDown: boolean) => void +} export type ListRef = React.MutableRefObject -function ListImpl(props: ListProps, ref: React.Ref) { - return +const SCROLLED_DOWN_LIMIT = 200 + +function ListImpl( + {onScrolledDownChange, ...props}: ListProps, + ref: React.Ref, +) { + const [isScrolledDown, setIsScrolledDown] = useState(false) + + // TODO: This ignores the passed-in onScroll completely. + const scrollHandler = useAnimatedScrollHandler({ + onScroll(e) { + const didScrollDown = e.contentOffset.y > SCROLLED_DOWN_LIMIT + if (isScrolledDown !== didScrollDown) { + setIsScrolledDown(didScrollDown) + onScrolledDownChange?.(didScrollDown) + } + }, + }) + + return } export const List = React.forwardRef(ListImpl) as ( diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx index 09cdfcd0aa..d707ae01dd 100644 --- a/src/view/screens/Notifications.tsx +++ b/src/view/screens/Notifications.tsx @@ -36,7 +36,8 @@ type Props = NativeStackScreenProps< export function NotificationsScreen({}: Props) { const {_} = useLingui() const setMinimalShellMode = useSetMinimalShellMode() - const [onMainScroll, isScrolledDown, resetMainScroll] = useOnMainScroll() + const [onMainScroll, resetMainScroll] = useOnMainScroll() + const [isScrolledDown, setIsScrolledDown] = React.useState(false) const scrollElRef = React.useRef(null) const checkLatestRef = React.useRef<() => void | null>() const {screen} = useAnalytics() @@ -133,6 +134,7 @@ export function NotificationsScreen({}: Props) {