| author | |
| committer | |
| log | 6f6197c4657f9e2e44a7e8f2fa759d00a15c1cbd |
| tree | f0596f4ddaa8b8d85e106627a00f4c38982370d5 |
| parent | d514a8c6176ace9057dc8c7619f0b5f0d81d1bd0 |
| signature | Signed by PGP key B5690EEEBB952194 |
8 files changed, 47 insertions(+), 9 deletions(-)
apps/mobile/src/components/ScreenScrollView.tsx+2-1| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | import type { ComponentProps } from "react"; |
| 2 | import { Platform, ScrollView } from "react-native"; | |
| 2 | import { Platform } from "react-native"; | |
| 3 | import { NativeScrollView as ScrollView } from "../native/NativeScrollViewMarker"; | |
| 3 | 4 | |
| 4 | 5 | /** Keeps forms and settings readable inside a wide pane while its surface fills the screen. */ |
| 5 | 6 | export function ScreenScrollView(props: ComponentProps<typeof ScrollView>) { |
apps/mobile/src/features/pull-requests/PullRequestDiscussionScreen.tsx+2-2| ... | ... | @@ -5,7 +5,7 @@ import type { PullRequestThreadComment } from "@t3tools/contracts"; |
| 5 | 5 | import { useRef, useState } from "react"; |
| 6 | 6 | import { ActivityIndicator, Platform, ScrollView, View } from "react-native"; |
| 7 | 7 | import { KeyboardAvoidingView, useKeyboardState } from "react-native-keyboard-controller"; |
| 8 | import { ScrollViewMarker } from "react-native-screens"; | |
| 8 | import { NativeScrollViewMarker as ScrollViewMarker } from "../../native/NativeScrollViewMarker"; | |
| 9 | 9 | import { useSafeAreaInsets } from "react-native-safe-area-context"; |
| 10 | 10 | import { AppText as Text, AppTextInput as TextInput } from "../../components/AppText"; |
| 11 | 11 | import { AndroidScreenHeader } from "../../components/AndroidScreenHeader"; |
| ... | ... | @@ -153,7 +153,7 @@ function Discussion({ params }: { params: PullRequestParams & { readonly discuss |
| 153 | 153 | /> |
| 154 | 154 | </NativeHeaderToolbar> |
| 155 | 155 | ) : null} |
| 156 | <ScrollViewMarker style={{ flex: 1 }} scrollEdgeEffects={{ top: "soft", bottom: "soft" }}> | |
| 156 | <ScrollViewMarker style={{ flex: 1 }}> | |
| 157 | 157 | <ScrollView |
| 158 | 158 | className="flex-1" |
| 159 | 159 | contentInsetAdjustmentBehavior="automatic" |
apps/mobile/src/features/pull-requests/PullRequestRouteScreen.tsx+2-2| ... | ... | @@ -52,7 +52,7 @@ import { |
| 52 | 52 | StyleSheet, |
| 53 | 53 | View, |
| 54 | 54 | } from "react-native"; |
| 55 | import { ScrollViewMarker } from "react-native-screens"; | |
| 55 | import { NativeScrollViewMarker as ScrollViewMarker } from "../../native/NativeScrollViewMarker"; | |
| 56 | 56 | import { useSafeAreaInsets } from "react-native-safe-area-context"; |
| 57 | 57 | |
| 58 | 58 | import { AppText as Text } from "../../components/AppText"; |
| ... | ... | @@ -115,7 +115,7 @@ const EMPTY_IDS: readonly string[] = []; |
| 115 | 115 | function PullRequestTabScrollView(props: ScrollViewProps) { |
| 116 | 116 | const insets = useSafeAreaInsets(); |
| 117 | 117 | return ( |
| 118 | <ScrollViewMarker style={{ flex: 1 }} scrollEdgeEffects={{ top: "soft", bottom: "soft" }}> | |
| 118 | <ScrollViewMarker style={{ flex: 1 }}> | |
| 119 | 119 | <ScrollView |
| 120 | 120 | {...props} |
| 121 | 121 | automaticallyAdjustContentInsets={false} |
apps/mobile/src/features/settings/SettingsFollowUpRouteScreen.tsx+2-1| ... | ... | @@ -1,7 +1,8 @@ |
| 1 | import { ScreenScrollView as ScrollView } from "../../components/ScreenScrollView"; | |
| 1 | 2 | import { useAtomSet, useAtomValue } from "@effect/atom-react"; |
| 2 | 3 | import { useNavigation } from "@react-navigation/native"; |
| 3 | 4 | import { AsyncResult } from "effect/reactivity"; |
| 4 | import { Platform, ScrollView, View } from "react-native"; | |
| 5 | import { Platform, View } from "react-native"; | |
| 5 | 6 | import { useSafeAreaInsets } from "react-native-safe-area-context"; |
| 6 | 7 | |
| 7 | 8 | import { AndroidScreenHeader } from "../../components/AndroidScreenHeader"; |
apps/mobile/src/features/settings/SettingsKeyboardRouteScreen.tsx+2-1| ... | ... | @@ -1,7 +1,8 @@ |
| 1 | import { ScreenScrollView as ScrollView } from "../../components/ScreenScrollView"; | |
| 1 | 2 | import { useAtomSet, useAtomValue } from "@effect/atom-react"; |
| 2 | 3 | import { useNavigation } from "@react-navigation/native"; |
| 3 | 4 | import { AsyncResult } from "effect/reactivity"; |
| 4 | import { Platform, ScrollView, View } from "react-native"; | |
| 5 | import { Platform, View } from "react-native"; | |
| 5 | 6 | import { useSafeAreaInsets } from "react-native-safe-area-context"; |
| 6 | 7 | |
| 7 | 8 | import { AndroidScreenHeader } from "../../components/AndroidScreenHeader"; |
apps/mobile/src/features/settings/SettingsMicrophoneRouteScreen.tsx+2-1| ... | ... | @@ -1,8 +1,9 @@ |
| 1 | import { ScreenScrollView as ScrollView } from "../../components/ScreenScrollView"; | |
| 1 | 2 | import { useAtomSet, useAtomValue } from "@effect/atom-react"; |
| 2 | 3 | import { AsyncResult } from "effect/reactivity"; |
| 3 | 4 | import { getRecordingPermissionsAsync, requestRecordingPermissionsAsync } from "expo-audio"; |
| 4 | 5 | import { useCallback, useEffect, useState } from "react"; |
| 5 | import { AppState, Linking, Pressable, ScrollView, View } from "react-native"; | |
| 6 | import { AppState, Linking, Pressable, View } from "react-native"; | |
| 6 | 7 | import Reanimated, { ReduceMotion, useAnimatedStyle, withTiming } from "react-native-reanimated"; |
| 7 | 8 | import { useSafeAreaInsets } from "react-native-safe-area-context"; |
| 8 | 9 |
apps/mobile/src/native/NativeScrollViewMarker.tsx created+31| ... | ... | @@ -0,0 +1,31 @@ |
| 1 | import { createContext, use, type ComponentProps } from "react"; | |
| 2 | import { Platform, ScrollView } from "react-native"; | |
| 3 | import { ScrollViewMarker } from "react-native-screens"; | |
| 4 | import { nativeHeaderScrollEdgeEffects } from "./scrollEdgeEffects"; | |
| 5 | ||
| 6 | type ScrollEdgeEffects = ComponentProps<typeof ScrollViewMarker>["scrollEdgeEffects"]; | |
| 7 | ||
| 8 | const DEFAULT_SCROLL_EDGE_EFFECTS = nativeHeaderScrollEdgeEffects(Platform.OS, Platform.Version); | |
| 9 | ||
| 10 | export const NativeScrollEdgeEffectsContext = createContext<ScrollEdgeEffects>(undefined); | |
| 11 | ||
| 12 | /** Register the content scroll view and apply its route's native edge settings. */ | |
| 13 | export function NativeScrollViewMarker(props: ComponentProps<typeof ScrollViewMarker>) { | |
| 14 | const effects = use(NativeScrollEdgeEffectsContext); | |
| 15 | return ( | |
| 16 | <ScrollViewMarker | |
| 17 | {...props} | |
| 18 | scrollEdgeEffects={props.scrollEdgeEffects ?? effects ?? DEFAULT_SCROLL_EDGE_EFFECTS} | |
| 19 | /> | |
| 20 | ); | |
| 21 | } | |
| 22 | ||
| 23 | /** Primary screen scrolling; Android keeps its existing native view hierarchy. */ | |
| 24 | export function NativeScrollView(props: ComponentProps<typeof ScrollView>) { | |
| 25 | const scrollView = <ScrollView {...props} />; | |
| 26 | return Platform.OS === "ios" ? ( | |
| 27 | <NativeScrollViewMarker style={{ flex: 1 }}>{scrollView}</NativeScrollViewMarker> | |
| 28 | ) : ( | |
| 29 | scrollView | |
| 30 | ); | |
| 31 | } |
apps/mobile/src/native/createV5StackNavigator.ios.tsx+4-1| ... | ... | @@ -23,6 +23,7 @@ import { |
| 23 | 23 | import { useCallback, useRef, useState, type ComponentProps } from "react"; |
| 24 | 24 | import { StyleSheet, View } from "react-native"; |
| 25 | 25 | import { FormSheet, Stack } from "react-native-screens"; |
| 26 | import { NativeScrollEdgeEffectsContext } from "./NativeScrollViewMarker"; | |
| 26 | 27 | import { V5StackHeader } from "./V5StackHeader.ios"; |
| 27 | 28 | import type { AppNativeStackNavigationOptions } from "./StackHeader"; |
| 28 | 29 | import { NativeColumnContent } from "./NativeColumnContent.ios"; |
| ... | ... | @@ -243,7 +244,9 @@ export function V5CardStackView( |
| 243 | 244 | .nativeContentInsetHorizontally |
| 244 | 245 | } |
| 245 | 246 | > |
| 246 | {descriptor.render()} | |
| 247 | <NativeScrollEdgeEffectsContext value={descriptor.options.scrollEdgeEffects}> | |
| 248 | {descriptor.render()} | |
| 249 | </NativeScrollEdgeEffectsContext> | |
| 247 | 250 | </NativeColumnContent> |
| 248 | 251 | </NavigationRouteContext> |
| 249 | 252 | </NavigationContext> |