Files
bsky-social-app/src/lib/hooks/useScreenPresence/index.web.tsx
T
Claude 5453044368 Hold the compose pill steady between two screens that both show it
Tapping back pops the route from JS, so react-native-screens swaps the
outgoing screen for a snapshot for the pop animation and it stops reporting
transition progress. The pill entry for that screen was then faded out on its
own spring, which ran ahead of the native pop, so the summed visibility dipped
below the glass threshold and the pill dissolved and re-materialized during
every pop between two pill screens. Swipe-back never showed it because the
screen stays mounted through the gesture.

Add a screen coverage registry to the presence system: the summed presence of
every mounted native-stack screen, which is exactly 1 at rest and while all
screens in a transition are still mounted. A pill entry that has left fills
in whatever coverage is missing, which is the incoming screen's complement,
so the total stays pinned at 1 between two pill screens and follows the real
transition when leaving for a screen without one. Its own spring remains as a
floor for a screen whose config switches off while still on screen.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoggCVt9XgpKbjrTZjoJD9
2026-09-07 22:07:03 +00:00

52 lines
1.3 KiB
TypeScript

import {useCallback} from 'react'
import {useSharedValue, withSpring} from 'react-native-reanimated'
import {useFocusEffect} from '@react-navigation/native'
import {SHELL_SPRING_CONFIG} from '#/lib/custom-animations/springs'
import {ScreenPresenceContext, useRegisterScreenCoverage} from './context'
export {
ScreenCoverageProvider,
type ScreenPresence,
useScreenCoverage,
useScreenPresence,
} from './context'
/**
* No-op on web: there are no native stack transitions to track.
*/
export function ScreenTransitionProvider({
children,
}: {
children: React.ReactNode
}) {
return children
}
/**
* On web, screens do not animate in or out, so presence is simply focus, with
* a spring so that dependent shell UI still animates.
*/
export function ScreenPresenceProvider({
children,
}: {
children: React.ReactNode
}) {
const visibility = useSharedValue(1)
const presence = useSharedValue(0)
useRegisterScreenCoverage(presence)
useFocusEffect(
useCallback(() => {
presence.set(withSpring(1, SHELL_SPRING_CONFIG))
return () => presence.set(withSpring(0, SHELL_SPRING_CONFIG))
}, [presence]),
)
return (
<ScreenPresenceContext.Provider value={{visibility, presence}}>
{children}
</ScreenPresenceContext.Provider>
)
}