From 8a612b3b054460d334fd45e2af34af4072d01673 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Sun, 28 Apr 2024 16:43:06 +0100 Subject: [PATCH 1/4] Add optional momentum events to scroll context --- src/lib/ScrollContext.tsx | 8 +++++++- src/screens/Profile/Sections/Labels.tsx | 6 ++++++ src/view/com/util/List.tsx | 6 ++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/lib/ScrollContext.tsx b/src/lib/ScrollContext.tsx index 00b197bedc..2987670f02 100644 --- a/src/lib/ScrollContext.tsx +++ b/src/lib/ScrollContext.tsx @@ -5,6 +5,8 @@ const ScrollContext = createContext>({ onBeginDrag: undefined, onEndDrag: undefined, onScroll: undefined, + onMomentumBegin: undefined, + onMomentumEnd: undefined, }) export function useScrollHandlers(): ScrollHandlers { @@ -20,14 +22,18 @@ export function ScrollProvider({ onBeginDrag, onEndDrag, onScroll, + onMomentumBegin, + onMomentumEnd, }: ProviderProps) { const handlers = useMemo( () => ({ onBeginDrag, onEndDrag, onScroll, + onMomentumBegin, + onMomentumEnd, }), - [onBeginDrag, onEndDrag, onScroll], + [onBeginDrag, onEndDrag, onScroll, onMomentumBegin, onMomentumEnd], ) return ( {children} diff --git a/src/screens/Profile/Sections/Labels.tsx b/src/screens/Profile/Sections/Labels.tsx index f43e3633db..c04fcdc760 100644 --- a/src/screens/Profile/Sections/Labels.tsx +++ b/src/screens/Profile/Sections/Labels.tsx @@ -123,6 +123,12 @@ export function ProfileLabelsSectionInner({ onScroll(e, ctx) { contextScrollHandlers.onScroll?.(e, ctx) }, + onMomentumBegin(e, ctx) { + contextScrollHandlers.onMomentumBegin?.(e, ctx) + }, + onMomentumEnd(e, ctx) { + contextScrollHandlers.onMomentumEnd?.(e, ctx) + }, }) const {labelValues} = labelerInfo.policies diff --git a/src/view/com/util/List.tsx b/src/view/com/util/List.tsx index 5729a43a52..fd65ef349b 100644 --- a/src/view/com/util/List.tsx +++ b/src/view/com/util/List.tsx @@ -64,6 +64,12 @@ function ListImpl( } } }, + onMomentumBegin(e, ctx) { + contextScrollHandlers.onMomentumBegin?.(e, ctx) + }, + onMomentumEnd(e, ctx) { + contextScrollHandlers.onMomentumEnd?.(e, ctx) + }, }) let refreshControl From 94c8bba6c94724c7bce9412e860c4433b95b236a Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Sun, 28 Apr 2024 16:44:06 +0100 Subject: [PATCH 2/4] If there is a velocity, don't snap until momentum end --- src/view/com/util/MainScrollProvider.tsx | 52 ++++++++++++++++++------ 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/src/view/com/util/MainScrollProvider.tsx b/src/view/com/util/MainScrollProvider.tsx index 01b8a954d5..2c7d6eecef 100644 --- a/src/view/com/util/MainScrollProvider.tsx +++ b/src/view/com/util/MainScrollProvider.tsx @@ -1,11 +1,12 @@ import React, {useCallback, useEffect} from 'react' +import {NativeScrollEvent} from 'react-native' +import {interpolate, useSharedValue} from 'react-native-reanimated' import EventEmitter from 'eventemitter3' + import {ScrollProvider} from '#/lib/ScrollContext' -import {NativeScrollEvent} from 'react-native' -import {useSetMinimalShellMode, useMinimalShellMode} from '#/state/shell' +import {useMinimalShellMode, useSetMinimalShellMode} from '#/state/shell' import {useShellLayout} from '#/state/shell/shell-layout' import {isNative, isWeb} from 'platform/detection' -import {useSharedValue, interpolate} from 'react-native-reanimated' const WEB_HIDE_SHELL_THRESHOLD = 200 @@ -32,6 +33,24 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) { } }) + const snapToClosestState = useCallback( + (e: NativeScrollEvent) => { + 'worklet' + if (isNative) { + startDragOffset.value = null + startMode.value = null + if (e.contentOffset.y < headerHeight.value / 2) { + // If we're close to the top, show the shell. + setMode(false) + } else { + // Snap to whichever state is the closest. + setMode(Math.round(mode.value) === 1) + } + } + }, + [startDragOffset, startMode, setMode, mode, headerHeight], + ) + const onBeginDrag = useCallback( (e: NativeScrollEvent) => { 'worklet' @@ -47,18 +66,24 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) { (e: NativeScrollEvent) => { 'worklet' if (isNative) { - startDragOffset.value = null - startMode.value = null - if (e.contentOffset.y < headerHeight.value / 2) { - // If we're close to the top, show the shell. - setMode(false) - } else { - // Snap to whichever state is the closest. - setMode(Math.round(mode.value) === 1) + if (e.velocity && e.velocity.y !== 0) { + // If we detect a velocity, wait for onMomentumEnd to snap. + return } + snapToClosestState(e) } }, - [startDragOffset, startMode, setMode, mode, headerHeight], + [snapToClosestState], + ) + + const onMomentumEnd = useCallback( + (e: NativeScrollEvent) => { + 'worklet' + if (isNative) { + snapToClosestState(e) + } + }, + [snapToClosestState], ) const onScroll = useCallback( @@ -119,7 +144,8 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) { + onScroll={onScroll} + onMomentumEnd={onMomentumEnd}> {children} ) From 02777e58629ee7cea49e75424434706c1ab5f88d Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Sun, 28 Apr 2024 17:09:06 +0100 Subject: [PATCH 3/4] Don't show bar on scroll down --- src/view/com/util/MainScrollProvider.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/view/com/util/MainScrollProvider.tsx b/src/view/com/util/MainScrollProvider.tsx index 2c7d6eecef..f45229dc42 100644 --- a/src/view/com/util/MainScrollProvider.tsx +++ b/src/view/com/util/MainScrollProvider.tsx @@ -37,11 +37,18 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) { (e: NativeScrollEvent) => { 'worklet' if (isNative) { + if (startDragOffset.value === null) { + return + } + const didScrollDown = e.contentOffset.y > startDragOffset.value startDragOffset.value = null startMode.value = null - if (e.contentOffset.y < headerHeight.value / 2) { + if (e.contentOffset.y < headerHeight.value) { // If we're close to the top, show the shell. setMode(false) + } else if (didScrollDown) { + // Showing the bar again on scroll down feels annoying, so don't. + setMode(true) } else { // Snap to whichever state is the closest. setMode(Math.round(mode.value) === 1) From b324b928d390f97051cb30916d02770f842a2fc9 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Sun, 28 Apr 2024 17:45:27 +0100 Subject: [PATCH 4/4] Rm onMomentumBegin --- src/lib/ScrollContext.tsx | 5 +---- src/screens/Profile/Sections/Labels.tsx | 3 --- src/view/com/util/List.tsx | 5 ++--- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/lib/ScrollContext.tsx b/src/lib/ScrollContext.tsx index 2987670f02..d55b8cdabb 100644 --- a/src/lib/ScrollContext.tsx +++ b/src/lib/ScrollContext.tsx @@ -5,7 +5,6 @@ const ScrollContext = createContext>({ onBeginDrag: undefined, onEndDrag: undefined, onScroll: undefined, - onMomentumBegin: undefined, onMomentumEnd: undefined, }) @@ -22,7 +21,6 @@ export function ScrollProvider({ onBeginDrag, onEndDrag, onScroll, - onMomentumBegin, onMomentumEnd, }: ProviderProps) { const handlers = useMemo( @@ -30,10 +28,9 @@ export function ScrollProvider({ onBeginDrag, onEndDrag, onScroll, - onMomentumBegin, onMomentumEnd, }), - [onBeginDrag, onEndDrag, onScroll, onMomentumBegin, onMomentumEnd], + [onBeginDrag, onEndDrag, onScroll, onMomentumEnd], ) return ( {children} diff --git a/src/screens/Profile/Sections/Labels.tsx b/src/screens/Profile/Sections/Labels.tsx index c04fcdc760..553d94d2ee 100644 --- a/src/screens/Profile/Sections/Labels.tsx +++ b/src/screens/Profile/Sections/Labels.tsx @@ -123,9 +123,6 @@ export function ProfileLabelsSectionInner({ onScroll(e, ctx) { contextScrollHandlers.onScroll?.(e, ctx) }, - onMomentumBegin(e, ctx) { - contextScrollHandlers.onMomentumBegin?.(e, ctx) - }, onMomentumEnd(e, ctx) { contextScrollHandlers.onMomentumEnd?.(e, ctx) }, diff --git a/src/view/com/util/List.tsx b/src/view/com/util/List.tsx index fd65ef349b..d96a634ef2 100644 --- a/src/view/com/util/List.tsx +++ b/src/view/com/util/List.tsx @@ -64,9 +64,8 @@ function ListImpl( } } }, - onMomentumBegin(e, ctx) { - contextScrollHandlers.onMomentumBegin?.(e, ctx) - }, + // Note: adding onMomentumBegin here makes simulator scroll + // lag on Android. So either don't add it, or figure out why. onMomentumEnd(e, ctx) { contextScrollHandlers.onMomentumEnd?.(e, ctx) },