Add onScrolledDownChange
This commit is contained in:
@@ -1,13 +1,11 @@
|
|||||||
import {useState, useCallback, useMemo} from 'react'
|
import {useCallback, useMemo} from 'react'
|
||||||
import {NativeSyntheticEvent, NativeScrollEvent} from 'react-native'
|
import {NativeSyntheticEvent, 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 {s} from 'lib/styles'
|
|
||||||
import {isWeb} from 'platform/detection'
|
import {isWeb} from 'platform/detection'
|
||||||
import {
|
import {
|
||||||
useSharedValue,
|
useSharedValue,
|
||||||
interpolate,
|
interpolate,
|
||||||
runOnJS,
|
|
||||||
ScrollHandlers,
|
ScrollHandlers,
|
||||||
} from 'react-native-reanimated'
|
} from 'react-native-reanimated'
|
||||||
|
|
||||||
@@ -22,9 +20,8 @@ export type OnScrollCb = (
|
|||||||
export type OnScrollHandler = ScrollHandlers<any>
|
export type OnScrollHandler = ScrollHandlers<any>
|
||||||
export type ResetCb = () => void
|
export type ResetCb = () => void
|
||||||
|
|
||||||
export function useOnMainScroll(): [OnScrollHandler, boolean, ResetCb] {
|
export function useOnMainScroll(): [OnScrollHandler, ResetCb] {
|
||||||
const {headerHeight} = useShellLayout()
|
const {headerHeight} = useShellLayout()
|
||||||
const [isScrolledDown, setIsScrolledDown] = useState(false)
|
|
||||||
const mode = useMinimalShellMode()
|
const mode = useMinimalShellMode()
|
||||||
const setMode = useSetMinimalShellMode()
|
const setMode = useSetMinimalShellMode()
|
||||||
const startDragOffset = useSharedValue<number | null>(null)
|
const startDragOffset = useSharedValue<number | null>(null)
|
||||||
@@ -58,13 +55,6 @@ export function useOnMainScroll(): [OnScrollHandler, boolean, ResetCb] {
|
|||||||
const onScroll = useCallback(
|
const onScroll = useCallback(
|
||||||
(e: NativeScrollEvent) => {
|
(e: NativeScrollEvent) => {
|
||||||
'worklet'
|
'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 (startDragOffset.value === null || startMode.value === null) {
|
||||||
if (mode.value !== 0 && e.contentOffset.y < headerHeight.value) {
|
if (mode.value !== 0 && e.contentOffset.y < headerHeight.value) {
|
||||||
// If we're close enough to the top, always show the shell.
|
// 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
|
startMode.value = mode.value
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[headerHeight, mode, setMode, isScrolledDown, startDragOffset, startMode],
|
[headerHeight, mode, setMode, startDragOffset, startMode],
|
||||||
)
|
)
|
||||||
|
|
||||||
const scrollHandler: ScrollHandlers<any> = useMemo(
|
const scrollHandler: ScrollHandlers<any> = useMemo(
|
||||||
@@ -116,9 +106,7 @@ export function useOnMainScroll(): [OnScrollHandler, boolean, ResetCb] {
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
scrollHandler,
|
scrollHandler,
|
||||||
isScrolledDown,
|
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
setIsScrolledDown(false)
|
|
||||||
setMode(false)
|
setMode(false)
|
||||||
}, [setMode]),
|
}, [setMode]),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ export function FeedPage({
|
|||||||
const {isDesktop} = useWebMediaQueries()
|
const {isDesktop} = useWebMediaQueries()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const {openComposer} = useComposerControls()
|
const {openComposer} = useComposerControls()
|
||||||
const [onMainScroll, isScrolledDown, resetMainScroll] = useOnMainScroll()
|
const [isScrolledDown, setIsScrolledDown] = React.useState(false)
|
||||||
|
const [onMainScroll, resetMainScroll] = useOnMainScroll()
|
||||||
const {screen, track} = useAnalytics()
|
const {screen, track} = useAnalytics()
|
||||||
const headerOffset = useHeaderOffset()
|
const headerOffset = useHeaderOffset()
|
||||||
const scrollElRef = React.useRef<ListMethods>(null)
|
const scrollElRef = React.useRef<ListMethods>(null)
|
||||||
@@ -173,6 +174,7 @@ export function FeedPage({
|
|||||||
pollInterval={POLL_FREQ}
|
pollInterval={POLL_FREQ}
|
||||||
scrollElRef={scrollElRef}
|
scrollElRef={scrollElRef}
|
||||||
onScroll={onMainScroll}
|
onScroll={onMainScroll}
|
||||||
|
onScrolledDownChange={setIsScrolledDown}
|
||||||
onHasNew={setHasNew}
|
onHasNew={setHasNew}
|
||||||
scrollEventThrottle={1}
|
scrollEventThrottle={1}
|
||||||
renderEmptyState={renderEmptyState}
|
renderEmptyState={renderEmptyState}
|
||||||
|
|||||||
@@ -25,11 +25,13 @@ export function Feed({
|
|||||||
scrollElRef,
|
scrollElRef,
|
||||||
onPressTryAgain,
|
onPressTryAgain,
|
||||||
onScroll,
|
onScroll,
|
||||||
|
onScrolledDownChange,
|
||||||
ListHeaderComponent,
|
ListHeaderComponent,
|
||||||
}: {
|
}: {
|
||||||
scrollElRef?: ListRef
|
scrollElRef?: ListRef
|
||||||
onPressTryAgain?: () => void
|
onPressTryAgain?: () => void
|
||||||
onScroll?: OnScrollHandler
|
onScroll?: OnScrollHandler
|
||||||
|
onScrolledDownChange: (isScrolledDown: boolean) => void
|
||||||
ListHeaderComponent?: () => JSX.Element
|
ListHeaderComponent?: () => JSX.Element
|
||||||
}) {
|
}) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
@@ -166,6 +168,7 @@ export function Feed({
|
|||||||
onEndReached={onEndReached}
|
onEndReached={onEndReached}
|
||||||
onEndReachedThreshold={0.6}
|
onEndReachedThreshold={0.6}
|
||||||
onScroll={scrollHandler}
|
onScroll={scrollHandler}
|
||||||
|
onScrolledDownChange={onScrolledDownChange}
|
||||||
scrollEventThrottle={1}
|
scrollEventThrottle={1}
|
||||||
contentContainerStyle={s.contentContainer}
|
contentContainerStyle={s.contentContainer}
|
||||||
// @ts-ignore our .web version only -prf
|
// @ts-ignore our .web version only -prf
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ let Feed = ({
|
|||||||
pollInterval,
|
pollInterval,
|
||||||
scrollElRef,
|
scrollElRef,
|
||||||
onScroll,
|
onScroll,
|
||||||
|
onScrolledDownChange,
|
||||||
onHasNew,
|
onHasNew,
|
||||||
scrollEventThrottle,
|
scrollEventThrottle,
|
||||||
renderEmptyState,
|
renderEmptyState,
|
||||||
@@ -65,6 +66,7 @@ let Feed = ({
|
|||||||
scrollElRef?: ListRef
|
scrollElRef?: ListRef
|
||||||
onHasNew?: (v: boolean) => void
|
onHasNew?: (v: boolean) => void
|
||||||
onScroll?: OnScrollHandler
|
onScroll?: OnScrollHandler
|
||||||
|
onScrolledDownChange?: (isScrolledDown: boolean) => void
|
||||||
scrollEventThrottle?: number
|
scrollEventThrottle?: number
|
||||||
renderEmptyState: () => JSX.Element
|
renderEmptyState: () => JSX.Element
|
||||||
renderEndOfFeed?: () => JSX.Element
|
renderEndOfFeed?: () => JSX.Element
|
||||||
@@ -295,6 +297,7 @@ let Feed = ({
|
|||||||
}}
|
}}
|
||||||
style={{paddingTop: headerOffset}}
|
style={{paddingTop: headerOffset}}
|
||||||
onScroll={onScroll != null ? scrollHandler : undefined}
|
onScroll={onScroll != null ? scrollHandler : undefined}
|
||||||
|
onScrolledDownChange={onScrolledDownChange}
|
||||||
scrollEventThrottle={scrollEventThrottle}
|
scrollEventThrottle={scrollEventThrottle}
|
||||||
indicatorStyle={theme.colorScheme === 'dark' ? 'white' : 'black'}
|
indicatorStyle={theme.colorScheme === 'dark' ? 'white' : 'black'}
|
||||||
onEndReached={onEndReached}
|
onEndReached={onEndReached}
|
||||||
|
|||||||
@@ -1,13 +1,34 @@
|
|||||||
import React 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 {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED'
|
||||||
|
|
||||||
export type ListMethods = FlatList_INTERNAL
|
export type ListMethods = FlatList_INTERNAL
|
||||||
export type ListProps<ItemT> = FlatListProps<ItemT>
|
export type ListProps<ItemT> = FlatListProps<ItemT> & {
|
||||||
|
onScrolledDownChange?: (isScrolledDown: boolean) => void
|
||||||
|
}
|
||||||
export type ListRef = React.MutableRefObject<FlatList_INTERNAL | null>
|
export type ListRef = React.MutableRefObject<FlatList_INTERNAL | null>
|
||||||
|
|
||||||
function ListImpl<ItemT>(props: ListProps<ItemT>, ref: React.Ref<ListMethods>) {
|
const SCROLLED_DOWN_LIMIT = 200
|
||||||
return <FlatList_INTERNAL {...props} ref={ref} />
|
|
||||||
|
function ListImpl<ItemT>(
|
||||||
|
{onScrolledDownChange, ...props}: ListProps<ItemT>,
|
||||||
|
ref: React.Ref<ListMethods>,
|
||||||
|
) {
|
||||||
|
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 <FlatList_INTERNAL {...props} onScroll={scrollHandler} ref={ref} />
|
||||||
}
|
}
|
||||||
|
|
||||||
export const List = React.forwardRef(ListImpl) as <ItemT>(
|
export const List = React.forwardRef(ListImpl) as <ItemT>(
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ type Props = NativeStackScreenProps<
|
|||||||
export function NotificationsScreen({}: Props) {
|
export function NotificationsScreen({}: Props) {
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const setMinimalShellMode = useSetMinimalShellMode()
|
const setMinimalShellMode = useSetMinimalShellMode()
|
||||||
const [onMainScroll, isScrolledDown, resetMainScroll] = useOnMainScroll()
|
const [onMainScroll, resetMainScroll] = useOnMainScroll()
|
||||||
|
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>()
|
||||||
const {screen} = useAnalytics()
|
const {screen} = useAnalytics()
|
||||||
@@ -133,6 +134,7 @@ export function NotificationsScreen({}: Props) {
|
|||||||
<ViewHeader title={_(msg`Notifications`)} canGoBack={false} />
|
<ViewHeader title={_(msg`Notifications`)} canGoBack={false} />
|
||||||
<Feed
|
<Feed
|
||||||
onScroll={onMainScroll}
|
onScroll={onMainScroll}
|
||||||
|
onScrolledDownChange={setIsScrolledDown}
|
||||||
scrollElRef={scrollElRef}
|
scrollElRef={scrollElRef}
|
||||||
ListHeaderComponent={ListHeaderComponent}
|
ListHeaderComponent={ListHeaderComponent}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user