Open History to see the last commit
1import { requireNativeView, requireOptionalNativeModule } from "expo";
2import { createContext, use, type ComponentProps } from "react";
3import { Platform, ScrollView, type ViewProps } from "react-native";
4// oxlint-disable-next-line eslint/no-restricted-imports -- The wrapper every screen must use.
5import { ScrollViewMarker } from "react-native-screens";
6import { nativeHeaderScrollEdgeEffects } from "./scrollEdgeEffects";
7
8type ScrollEdgeEffects = ComponentProps<typeof ScrollViewMarker>["scrollEdgeEffects"];
9
10const DEFAULT_SCROLL_EDGE_EFFECTS = nativeHeaderScrollEdgeEffects(Platform.OS, Platform.Version);
11
12export const NativeScrollEdgeEffectsContext = createContext<ScrollEdgeEffects>(undefined);
13
14function resolveScrollEdgeBleed() {
15 try {
16 const module = requireOptionalNativeModule<{
17 readonly ViewPrototypes?: { readonly T3NativeControls_ScrollEdgeBleed?: unknown };
18 }>("T3NativeControls");
19 if (!module?.ViewPrototypes?.T3NativeControls_ScrollEdgeBleed) return null;
20 return requireNativeView<ViewProps>("T3NativeControls", "ScrollEdgeBleed");
21 } catch {
22 return null;
23 }
24}
25// Widens the scroll view under the side safe areas its column pads away (the Duo's vertical bar),
26// so the header's scroll-edge blur, which UIKit draws inside the scroll view, reaches the bar
27// while the content stays in the safe area. Every header scroll view comes through here.
28const ScrollEdgeBleed = Platform.OS === "ios" ? resolveScrollEdgeBleed() : null;
29
30/** Registers a screen's scroll view with its header. The only way to do so: see the lint rule. */
31export function NativeScrollViewMarker(props: ComponentProps<typeof ScrollViewMarker>) {
32 const effects = use(NativeScrollEdgeEffectsContext);
33 const marker = (
34 <ScrollViewMarker
35 {...props}
36 {...(ScrollEdgeBleed ? { style: { flex: 1 } } : {})}
37 scrollEdgeEffects={props.scrollEdgeEffects ?? effects ?? DEFAULT_SCROLL_EDGE_EFFECTS}
38 />
39 );
40 return ScrollEdgeBleed ? <ScrollEdgeBleed style={props.style}>{marker}</ScrollEdgeBleed> : marker;
41}
42
43export function NativeScrollView(props: ComponentProps<typeof ScrollView>) {
44 const scrollView = <ScrollView {...props} />;
45 return Platform.OS === "ios" ? (
46 <NativeScrollViewMarker style={{ flex: 1 }}>{scrollView}</NativeScrollViewMarker>
47 ) : (
48 scrollView
49 );
50}