Skip to content

Commit

Permalink
Toggle minimal shell on any scroll for web (#2499)
Browse files Browse the repository at this point in the history
  • Loading branch information
gaearon authored Jan 11, 2024
1 parent 3662259 commit b147f7a
Showing 1 changed file with 47 additions and 42 deletions.
89 changes: 47 additions & 42 deletions src/view/com/util/MainScrollProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import {ScrollProvider} from '#/lib/ScrollContext'
import {NativeScrollEvent} from 'react-native'
import {useSetMinimalShellMode, useMinimalShellMode} from '#/state/shell'
import {useShellLayout} from '#/state/shell/shell-layout'
import {isWeb} from 'platform/detection'
import {isNative} from 'platform/detection'
import {useSharedValue, interpolate} from 'react-native-reanimated'

const WEB_HIDE_SHELL_THRESHOLD = 200

function clamp(num: number, min: number, max: number) {
'worklet'
return Math.min(Math.max(num, min), max)
Expand All @@ -21,23 +23,27 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) {
const onBeginDrag = useCallback(
(e: NativeScrollEvent) => {
'worklet'
startDragOffset.value = e.contentOffset.y
startMode.value = mode.value
if (isNative) {
startDragOffset.value = e.contentOffset.y
startMode.value = mode.value
}
},
[mode, startDragOffset, startMode],
)

const onEndDrag = useCallback(
(e: NativeScrollEvent) => {
'worklet'
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 (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],
Expand All @@ -46,41 +52,40 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) {
const onScroll = useCallback(
(e: NativeScrollEvent) => {
'worklet'
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.
// Even if we're not dragging.
setMode(false)
if (isNative) {
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.
// Even if we're not dragging.
setMode(false)
}
return
}
if (isWeb) {
// On the web, there is no concept of "starting" the drag.
// When we get the first scroll event, we consider that the start.
startDragOffset.value = e.contentOffset.y
startMode.value = mode.value
}
return
}

// The "mode" value is always between 0 and 1.
// Figure out how much to move it based on the current dragged distance.
const dy = e.contentOffset.y - startDragOffset.value
const dProgress = interpolate(
dy,
[-headerHeight.value, headerHeight.value],
[-1, 1],
)
const newValue = clamp(startMode.value + dProgress, 0, 1)
if (newValue !== mode.value) {
// Manually adjust the value. This won't be (and shouldn't be) animated.
mode.value = newValue
}
if (isWeb) {
// On the web, there is no concept of "starting" the drag,
// so we don't have any specific anchor point to calculate the distance.
// Instead, update it continuosly along the way and diff with the last event.
// The "mode" value is always between 0 and 1.
// Figure out how much to move it based on the current dragged distance.
const dy = e.contentOffset.y - startDragOffset.value
const dProgress = interpolate(
dy,
[-headerHeight.value, headerHeight.value],
[-1, 1],
)
const newValue = clamp(startMode.value + dProgress, 0, 1)
if (newValue !== mode.value) {
// Manually adjust the value. This won't be (and shouldn't be) animated.
mode.value = newValue
}
} else {
// On the web, we don't try to follow the drag because we don't know when it ends.
// Instead, show/hide immediately based on whether we're scrolling up or down.
const dy = e.contentOffset.y - (startDragOffset.value ?? 0)
startDragOffset.value = e.contentOffset.y
startMode.value = mode.value

if (dy < 0 || e.contentOffset.y < WEB_HIDE_SHELL_THRESHOLD) {
setMode(false)
} else if (dy > 0) {
setMode(true)
}
}
},
[headerHeight, mode, setMode, startDragOffset, startMode],
Expand Down

0 comments on commit b147f7a

Please sign in to comment.