about summary refs log tree commit diff
path: root/src/view/com/util/MainScrollProvider.tsx
diff options
context:
space:
mode:
authordan <dan.abramov@gmail.com>2024-04-28 17:48:20 +0100
committerGitHub <noreply@github.com>2024-04-28 17:48:20 +0100
commit1dd3d6657c631905c27fa6af326d31a0f7c6039e (patch)
tree01b5e2997e2fd7172a8f6ee11ca525f114593541 /src/view/com/util/MainScrollProvider.tsx
parent3b4848ba59591b58a95c61af210697149a858f57 (diff)
downloadvoidsky-1dd3d6657c631905c27fa6af326d31a0f7c6039e.tar.zst
Account for momentum when hiding minimal shell (#3740)
* Add optional momentum events to scroll context

* If there is a velocity, don't snap until momentum end

* Don't show bar on scroll down

* Rm onMomentumBegin
Diffstat (limited to 'src/view/com/util/MainScrollProvider.tsx')
-rw-r--r--src/view/com/util/MainScrollProvider.tsx59
1 files changed, 46 insertions, 13 deletions
diff --git a/src/view/com/util/MainScrollProvider.tsx b/src/view/com/util/MainScrollProvider.tsx
index 01b8a954d..f45229dc4 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,31 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) {
     }
   })
 
+  const snapToClosestState = useCallback(
+    (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) {
+          // 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)
+        }
+      }
+    },
+    [startDragOffset, startMode, setMode, mode, headerHeight],
+  )
+
   const onBeginDrag = useCallback(
     (e: NativeScrollEvent) => {
       'worklet'
@@ -47,18 +73,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 +151,8 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) {
     <ScrollProvider
       onBeginDrag={onBeginDrag}
       onEndDrag={onEndDrag}
-      onScroll={onScroll}>
+      onScroll={onScroll}
+      onMomentumEnd={onMomentumEnd}>
       {children}
     </ScrollProvider>
   )