diff --git a/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx b/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx index b2981f63e255..10f2d24535fb 100644 --- a/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx +++ b/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx @@ -76,6 +76,10 @@ import { } from "../../native/v5-workspace-context"; import { useNativeLayoutMetrics } from "../../native/native-layout-metrics"; +import { + WorkspaceColumnsRendererContext, + WorkspacePresentationHostContext, +} from "./workspace-presentation-context"; interface AdaptiveWorkspaceContextValue { readonly layout: Layout; @@ -254,6 +258,7 @@ function AdaptiveWorkspaceLayoutContent( ) { const projectGroupingMode = props.projectGroupingMode; const nativeWorkspace = use(NativeWorkspaceModeContext); + const usesPresentationHost = use(WorkspacePresentationHostContext); const windowDimensions = useWindowDimensions(); const nativeMetrics = useNativeLayoutMetrics(); const { width: viewportWidth, height } = nativeMetrics ?? windowDimensions; @@ -699,108 +704,113 @@ function AdaptiveWorkspaceLayoutContent( ); } - return ( - ( + - - 0 ? "auto" : "no-hide-descendants"} + pointerEvents={navigationRailWidth > 0 ? "auto" : "none"} + style={railAnimatedStyle} > - {shouldRenderNavigationRail ? ( - 0 ? "auto" : "no-hide-descendants"} - pointerEvents={navigationRailWidth > 0 ? "auto" : "none"} - style={railAnimatedStyle} - > - - - ) : null} - {shouldRenderPrimarySidebar && layout.listPaneWidth !== null ? ( - - - ( - - )} - > - - - - - - - - - ) : null} - - + + ) : null} + {shouldRenderPrimarySidebar && layout.listPaneWidth !== null ? ( + + + ( + + )} > - - {props.children} - - + + + + + - + + ) : null} + + + + {children} + + + + + ); + return ( + + + {usesPresentationHost ? ( + + {props.children} + + ) : ( + renderColumns(props.children) + )} ); diff --git a/apps/mobile/src/features/layout/createWorkspaceStackNavigator.android.tsx b/apps/mobile/src/features/layout/createWorkspaceStackNavigator.android.tsx new file mode 100644 index 000000000000..fd495b63f14c --- /dev/null +++ b/apps/mobile/src/features/layout/createWorkspaceStackNavigator.android.tsx @@ -0,0 +1,141 @@ +import { + createNavigatorFactory, + StackRouter, + useNavigationBuilder, + type NavigatorTypeBagBase, + type ParamListBase, + type StackActionHelpers, + type StackNavigationState, + type StackRouterOptions, + type StaticConfig, + type TypedNavigator, +} from "@react-navigation/native"; +import { + NativeStackView, + type NativeStackNavigationEventMap, + type NativeStackNavigationOptions, + type NativeStackNavigatorProps, + type NativeStackTypeBag, +} from "@react-navigation/native-stack"; +import { use, type ComponentProps } from "react"; + +import { projectStackPresentation } from "../../native/workspace-stack-projection"; +import { + WorkspaceColumnsRendererContext, + WorkspacePresentationHostContext, +} from "./workspace-presentation-context"; + +type ViewProps = ComponentProps; +type Descriptor = ViewProps["descriptors"][string]; +type Route = ViewProps["state"]["routes"][number]; + +function WorkspaceStackView(props: ViewProps) { + const renderColumns = use(WorkspaceColumnsRendererContext); + if (!renderColumns) return ; + + const isOverlay = (route: Route) => { + const descriptor = props.descriptors[route.key] ?? props.describe(route, true); + const presentation = descriptor.options.presentation; + return presentation !== undefined && presentation !== "card"; + }; + const projection = projectStackPresentation(props.state, isOverlay); + const workspaceRoutes = projection.workspace; + const visibleRoute = workspaceRoutes.at(-1); + if (!visibleRoute) return ; + + const visibleDescriptor = props.descriptors[visibleRoute.key]!; + const baseRoute = { key: `${props.state.key}:workspace`, name: "Workspace" }; + const baseDescriptor: Descriptor = { + ...visibleDescriptor, + options: { headerShown: false, freezeOnBlur: false }, + render: () => ( + <> + {renderColumns( + !isOverlay(route)), + }} + />, + )} + + ), + }; + const routes = [baseRoute, ...projection.overlays]; + return ( + + ); +} + +function WorkspaceStackNavigator({ + id, + initialRouteName, + UNSTABLE_routeNamesChangeBehavior, + children, + layout, + screenListeners, + screenOptions, + screenLayout, + UNSTABLE_router, + ...rest +}: NativeStackNavigatorProps) { + const { state, describe, descriptors, navigation, NavigationContent } = useNavigationBuilder< + StackNavigationState, + StackRouterOptions, + StackActionHelpers, + NativeStackNavigationOptions, + NativeStackNavigationEventMap + >(StackRouter, { + id, + initialRouteName, + UNSTABLE_routeNamesChangeBehavior, + children, + layout, + screenListeners, + screenOptions, + screenLayout, + UNSTABLE_router, + }); + return ( + + + + + + ); +} + +type WorkspaceTypeBag< + ParamList extends ParamListBase, + NavigatorID extends string | undefined, +> = Omit, "Navigator"> & { + Navigator: typeof WorkspaceStackNavigator; +}; + +export function createWorkspaceStackNavigator< + const ParamList extends ParamListBase, + const NavigatorID extends string | undefined = string | undefined, + const TypeBag extends NavigatorTypeBagBase = WorkspaceTypeBag, + const Config extends StaticConfig = StaticConfig, +>(config?: Config): TypedNavigator { + return createNavigatorFactory(WorkspaceStackNavigator)(config); +} diff --git a/apps/mobile/src/features/layout/workspace-presentation-context.tsx b/apps/mobile/src/features/layout/workspace-presentation-context.tsx new file mode 100644 index 000000000000..b50adfbe4345 --- /dev/null +++ b/apps/mobile/src/features/layout/workspace-presentation-context.tsx @@ -0,0 +1,7 @@ +import { createContext, type ReactNode } from "react"; + +/** Android's native presentation stack surrounds all workspace columns. */ +export const WorkspacePresentationHostContext = createContext(false); +export const WorkspaceColumnsRendererContext = createContext< + ((children: ReactNode) => ReactNode) | null +>(null); diff --git a/apps/mobile/src/native/workspace-stack-projection.test.ts b/apps/mobile/src/native/workspace-stack-projection.test.ts index 6e9585586866..3f6f9bc8de17 100644 --- a/apps/mobile/src/native/workspace-stack-projection.test.ts +++ b/apps/mobile/src/native/workspace-stack-projection.test.ts @@ -6,6 +6,7 @@ import { nativeStackPopAction, nativeWorkspacePopAction, nativeWorkspacePopCount, + projectStackPresentation, projectWorkspaceStack, partitionStackPresentations, reconcileStackScreens, @@ -39,6 +40,65 @@ function history( }; } +describe("workspace presentation envelope", () => { + const isOverlay = (route: { name: string }) => route.name === "ReviewSheet"; + + it("keeps card settings in the detail history and restores it after system Back closes a sheet", () => { + const router = StackRouter({}); + const state = history([home, thread, settings]); + const routerOptions = { + routeNames: [...state.routeNames, "ReviewSheet", "ReviewDetails"], + routeParamList: {}, + routeGetIdList: {}, + }; + const opened = router.getStateForAction( + { ...state, routeNames: routerOptions.routeNames }, + { type: "PUSH", payload: { name: "ReviewSheet" } }, + routerOptions, + ); + if (!opened || opened.stale !== false) throw new Error("Expected an opened sheet history"); + const projection = projectStackPresentation(opened, isOverlay); + expect(projection.workspace).toEqual(state.routes); + expect(projection.workspace.at(-1)).toBe(settings); + expect(projection.overlays.map((route) => route.name)).toEqual(["ReviewSheet"]); + const closed = router.getStateForAction(opened, { type: "GO_BACK" }, routerOptions); + if (!closed || closed.stale !== false) throw new Error("Expected a restored workspace history"); + expect(projectStackPresentation(closed, isOverlay)).toEqual({ + workspace: state.routes, + overlays: [], + }); + }); + + it("keeps pushed sheet pages above the workspace and dismisses the entire sheet group", () => { + const sheet = { key: "review", name: "ReviewSheet" }; + const page = { key: "review-details", name: "ReviewDetails" }; + const state = history([home, thread, sheet, page]); + expect(projectStackPresentation(state, isOverlay)).toEqual({ + workspace: [home, thread], + overlays: [sheet, page], + }); + const closed = StackRouter({}).getStateForAction( + state, + nativeWorkspacePopAction(state, sheet.key)!, + { routeNames: state.routeNames, routeParamList: {}, routeGetIdList: {} }, + ); + if (!closed || closed.stale !== false) throw new Error("Expected a dismissed sheet history"); + expect(projectStackPresentation(closed, isOverlay)).toEqual({ + workspace: [home, thread], + overlays: [], + }); + expect(closed.routes[1]).toBe(thread); + }); + + it("preserves a cold-linked detail history without requiring a Home route", () => { + const sheet = { key: "review", name: "ReviewSheet" }; + expect(projectStackPresentation(history([thread, files, sheet]), isOverlay)).toEqual({ + workspace: [thread, files], + overlays: [sheet], + }); + }); +}); + describe("workspace router projection", () => { it("keeps workspace flows beside Home and restores them after a modal closes", () => { const draft = { key: "draft", name: "NewTaskSheet" }; diff --git a/apps/mobile/src/native/workspace-stack-projection.ts b/apps/mobile/src/native/workspace-stack-projection.ts index e3e716c8c1c2..f146d4d3ab50 100644 --- a/apps/mobile/src/native/workspace-stack-projection.ts +++ b/apps/mobile/src/native/workspace-stack-projection.ts @@ -6,16 +6,26 @@ import type { type Route = StackNavigationState["routes"][number]; +/** Keep the presenting history mounted while modal routes cover the whole workspace. */ +export function projectStackPresentation( + state: StackNavigationState, + isOverlay: (route: Route) => boolean, +) { + const firstOverlay = state.routes.findIndex(isOverlay); + return { + workspace: state.routes.slice(0, firstOverlay < 0 ? undefined : firstOverlay), + overlays: firstOverlay < 0 ? [] : state.routes.slice(firstOverlay), + }; +} + /** Project one router history into native columns and a modal presentation stack. */ export function projectWorkspaceStack( state: StackNavigationState, isOverlay: (route: Route) => boolean, ) { - const firstOverlay = state.routes.findIndex(isOverlay); - const workspaceRoutes = state.routes.slice(0, firstOverlay < 0 ? undefined : firstOverlay); + const { workspace: workspaceRoutes, overlays } = projectStackPresentation(state, isOverlay); const primary = workspaceRoutes.find((route) => route.name === "Home"); const detail = workspaceRoutes.filter((route) => route.name !== "Home"); - const overlays = firstOverlay < 0 ? [] : state.routes.slice(firstOverlay); return { primary, detail, overlays }; } diff --git a/patches/react-native-screens@5.0.0-t3.27.patch b/patches/react-native-screens@5.0.0-t3.27.patch new file mode 100644 index 000000000000..69c99d0cfc82 --- /dev/null +++ b/patches/react-native-screens@5.0.0-t3.27.patch @@ -0,0 +1,170 @@ +diff --git a/android/build.gradle b/android/build.gradle +--- a/android/build.gradle ++++ b/android/build.gradle +@@ -217,6 +217,7 @@ + } + + dependencies { ++ testImplementation 'junit:junit:4.13.2' + implementation 'com.facebook.react:react-native:+' + implementation 'androidx.appcompat:appcompat:1.7.1' + implementation 'androidx.fragment:fragment-ktx:1.8.9' +diff --git a/android/src/main/java/com/swmansion/rnscreens/core/modals/formsheet/FormSheetWidth.kt b/android/src/main/java/com/swmansion/rnscreens/core/modals/formsheet/FormSheetWidth.kt +new file mode 100644 +--- /dev/null ++++ b/android/src/main/java/com/swmansion/rnscreens/core/modals/formsheet/FormSheetWidth.kt +@@ -0,0 +1,13 @@ ++package com.swmansion.rnscreens.core.modals.formsheet ++ ++import kotlin.math.roundToInt ++ ++/** Keep compact sheets edge-to-edge, with Material gutters and a readable width in wide windows. */ ++internal object FormSheetWidth { ++ fun resolve(windowWidth: Int, density: Float): Int = ++ if (windowWidth / density < 600f) { ++ windowWidth ++ } else { ++ minOf((640f * density).roundToInt(), windowWidth - (32f * density).roundToInt()) ++ } ++} +diff --git a/android/src/main/java/com/swmansion/rnscreens/core/modals/formsheet/dialog/FormSheetAvailableHeightProvider.kt b/android/src/main/java/com/swmansion/rnscreens/core/modals/formsheet/dialog/FormSheetAvailableHeightProvider.kt +--- a/android/src/main/java/com/swmansion/rnscreens/core/modals/formsheet/dialog/FormSheetAvailableHeightProvider.kt ++++ b/android/src/main/java/com/swmansion/rnscreens/core/modals/formsheet/dialog/FormSheetAvailableHeightProvider.kt +@@ -24,6 +24,8 @@ + fun onAvailableHeightMeasured(height: Int) + } + ++ internal var onAvailableWidthMeasured: ((Int) -> Unit)? = null ++ + internal var availableHeightListener: OnAvailableHeightMeasuredListener? = null + + init { +@@ -35,6 +37,10 @@ + widthMeasureSpec: Int, + heightMeasureSpec: Int, + ) { ++ val width = MeasureSpec.getSize(widthMeasureSpec) ++ if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED && width > 0) { ++ onAvailableWidthMeasured?.invoke(width) ++ } + val height = MeasureSpec.getSize(heightMeasureSpec) + if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.UNSPECIFIED && height > 0) { + availableHeightListener?.onAvailableHeightMeasured(height) +diff --git a/android/src/main/java/com/swmansion/rnscreens/core/modals/formsheet/dialog/FormSheetDialog.kt b/android/src/main/java/com/swmansion/rnscreens/core/modals/formsheet/dialog/FormSheetDialog.kt +--- a/android/src/main/java/com/swmansion/rnscreens/core/modals/formsheet/dialog/FormSheetDialog.kt ++++ b/android/src/main/java/com/swmansion/rnscreens/core/modals/formsheet/dialog/FormSheetDialog.kt +@@ -3,13 +3,16 @@ + import android.content.Context + import android.os.Bundle + import android.util.Log ++import android.view.Gravity + import android.view.View + import android.view.ViewGroup + import android.view.Window + import android.view.WindowManager + import android.widget.FrameLayout + import androidx.core.view.WindowCompat ++import androidx.coordinatorlayout.widget.CoordinatorLayout + import com.google.android.material.bottomsheet.BottomSheetDialog ++import com.swmansion.rnscreens.core.modals.formsheet.FormSheetWidth + + /** + * A custom BottomSheetDialog override used to render the FormSheet native component. +@@ -87,7 +90,13 @@ + + private fun setupBottomSheetHeight() { + val bottomSheetView = findViewById(com.google.android.material.R.id.design_bottom_sheet) +- bottomSheetView?.layoutParams?.height = ViewGroup.LayoutParams.MATCH_PARENT ++ bottomSheetView?.let { sheet -> ++ sheet.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT ++ (sheet.layoutParams as CoordinatorLayout.LayoutParams).gravity = Gravity.CENTER_HORIZONTAL ++ availableHeightProvider.onAvailableWidthMeasured = { width -> ++ behavior.maxWidth = FormSheetWidth.resolve(width, context.resources.displayMetrics.density) ++ } ++ } + } + + private fun installAvailableHeightProvider() { +@@ -105,7 +114,10 @@ + contentParent.addView( + availableHeightProvider, + 0, +- FrameLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT), ++ FrameLayout.LayoutParams( ++ ViewGroup.LayoutParams.MATCH_PARENT, ++ ViewGroup.LayoutParams.MATCH_PARENT, ++ ), + ) + } + +diff --git a/android/src/main/java/com/swmansion/rnscreens/legacy/stack/views/ScreensCoordinatorLayout.kt b/android/src/main/java/com/swmansion/rnscreens/legacy/stack/views/ScreensCoordinatorLayout.kt +--- a/android/src/main/java/com/swmansion/rnscreens/legacy/stack/views/ScreensCoordinatorLayout.kt ++++ b/android/src/main/java/com/swmansion/rnscreens/legacy/stack/views/ScreensCoordinatorLayout.kt +@@ -1,11 +1,13 @@ + package com.swmansion.rnscreens.legacy.stack.views + + import android.content.Context ++import android.view.Gravity + import android.view.WindowInsets + import android.view.animation.Animation + import android.view.animation.AnimationSet + import androidx.coordinatorlayout.widget.CoordinatorLayout + import com.facebook.react.uimanager.ReactPointerEventsView ++import com.swmansion.rnscreens.core.modals.formsheet.FormSheetWidth + import com.swmansion.rnscreens.legacy.PointerEventsBoxNoneImpl + import com.swmansion.rnscreens.legacy.ScreenStackFragment + import com.swmansion.rnscreens.legacy.bottomsheet.usesFormSheetPresentation +@@ -113,6 +115,22 @@ + + override fun shouldDelayChildPressedState(): Boolean = fragment.screen.usesFormSheetPresentation() + ++ override fun onMeasure( ++ widthMeasureSpec: Int, ++ heightMeasureSpec: Int, ++ ) { ++ if (fragment.screen.usesFormSheetPresentation()) { ++ val params = fragment.screen.layoutParams as LayoutParams ++ params.width = ++ FormSheetWidth.resolve( ++ MeasureSpec.getSize(widthMeasureSpec), ++ resources.displayMetrics.density, ++ ) ++ params.gravity = Gravity.CENTER_HORIZONTAL ++ } ++ super.onMeasure(widthMeasureSpec, heightMeasureSpec) ++ } ++ + override fun onLayout( + changed: Boolean, + l: Int, +diff --git a/android/src/test/java/com/swmansion/rnscreens/core/modals/formsheet/FormSheetWidthTest.kt b/android/src/test/java/com/swmansion/rnscreens/core/modals/formsheet/FormSheetWidthTest.kt +new file mode 100644 +--- /dev/null ++++ b/android/src/test/java/com/swmansion/rnscreens/core/modals/formsheet/FormSheetWidthTest.kt +@@ -0,0 +1,26 @@ ++package com.swmansion.rnscreens.core.modals.formsheet ++ ++import org.junit.Assert.assertEquals ++import org.junit.Test ++ ++class FormSheetWidthTest { ++ @Test ++ fun compactWindowsRemainEdgeToEdge() { ++ assertEquals(412, FormSheetWidth.resolve(412, 1f)) ++ assertEquals(1198, FormSheetWidth.resolve(1198, 2f)) ++ } ++ ++ @Test ++ fun wideWindowsUseGuttersBeforeReachingMaximumWidth() { ++ assertEquals(568, FormSheetWidth.resolve(600, 1f)) ++ assertEquals(1136, FormSheetWidth.resolve(1200, 2f)) ++ assertEquals(640, FormSheetWidth.resolve(800, 1f)) ++ assertEquals(1280, FormSheetWidth.resolve(1600, 2f)) ++ } ++ ++ @Test ++ fun resizingAcrossFoldedAndUnfoldedWidthsRecomputesFromTheCurrentWindow() { ++ val widths = listOf(840, 412, 650, 840) ++ assertEquals(listOf(640, 412, 618, 640), widths.map { FormSheetWidth.resolve(it, 1f) }) ++ } ++} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 70ef13e0c7d8..62c6f590e3f7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -167,6 +167,7 @@ patchedDependencies: node-pty@1.2.0-beta.15: f2fe901c61cde17986240002d05c172d5d0272d83ffaab8d0ebeb922763be414 react-native-gesture-handler@3.2.1: 3037216c0dd1b14e8877441bde1a7404d1c6e9c09f835bd184c3cdba4aa066ad react-native-keyboard-controller@1.22.6: 6e448411781347cd0b87868ee279117514a399801c18f233ddca4fa3935be75d + react-native-screens@5.0.0-t3.27: 09633e07418708aa3f6914c79394b09ed652c0c402f2dbf57ddc652835a467cc react-native-webview@14.0.1: 81caa279ec3dc1f81209062133b66e8f824e13f106cca57f1fa732a72c2ab378 importers: @@ -348,7 +349,7 @@ importers: version: 7.3.4(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0) '@react-navigation/native-stack': specifier: 7.17.6 - version: 7.17.6(patch_hash=d2d52f3776e30765519748fd5e177592dc3b9cc778c52fb00e02674dfa9042c5)(6b53c4f789a319cd2c00cd296dcea732) + version: 7.17.6(patch_hash=d2d52f3776e30765519748fd5e177592dc3b9cc778c52fb00e02674dfa9042c5)(555bc48174527ded4ce04a05413f9466) '@shikijs/core': specifier: 4.2.0 version: 4.2.0 @@ -549,7 +550,7 @@ importers: version: 5.9.1(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0) react-native-screens: specifier: file:deps/react-native-screens-5.0.0-t3.27.tgz - version: file:apps/mobile/deps/react-native-screens-5.0.0-t3.27.tgz(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0) + version: file:apps/mobile/deps/react-native-screens-5.0.0-t3.27.tgz(patch_hash=09633e07418708aa3f6914c79394b09ed652c0c402f2dbf57ddc652835a467cc)(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0) react-native-shiki-engine: specifier: ^0.3.12 version: 0.3.12(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0) @@ -16096,7 +16097,7 @@ snapshots: optionalDependencies: '@react-native-masked-view/masked-view': 0.3.2(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0) - '@react-navigation/native-stack@7.17.6(patch_hash=d2d52f3776e30765519748fd5e177592dc3b9cc778c52fb00e02674dfa9042c5)(6b53c4f789a319cd2c00cd296dcea732)': + '@react-navigation/native-stack@7.17.6(patch_hash=d2d52f3776e30765519748fd5e177592dc3b9cc778c52fb00e02674dfa9042c5)(555bc48174527ded4ce04a05413f9466)': dependencies: '@react-navigation/elements': 2.9.26(83de68753219ecf7f3321e065bd378be) '@react-navigation/native': 7.3.4(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0) @@ -16104,7 +16105,7 @@ snapshots: react: 19.3.0 react-native: 0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6) react-native-safe-area-context: 5.9.1(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0) - react-native-screens: file:apps/mobile/deps/react-native-screens-5.0.0-t3.27.tgz(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0) + react-native-screens: file:apps/mobile/deps/react-native-screens-5.0.0-t3.27.tgz(patch_hash=09633e07418708aa3f6914c79394b09ed652c0c402f2dbf57ddc652835a467cc)(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0) sf-symbols-typescript: 2.2.0 warn-once: 0.1.1 transitivePeerDependencies: @@ -22097,7 +22098,7 @@ snapshots: react: 19.3.0 react-native: 0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6) - react-native-screens@file:apps/mobile/deps/react-native-screens-5.0.0-t3.27.tgz(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0): + react-native-screens@file:apps/mobile/deps/react-native-screens-5.0.0-t3.27.tgz(patch_hash=09633e07418708aa3f6914c79394b09ed652c0c402f2dbf57ddc652835a467cc)(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0): dependencies: react: 19.3.0 react-freeze: 1.0.4(react@19.3.0) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0d37bd2f5729..0e45c8198506 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -315,6 +315,7 @@ patchedDependencies: node-pty@1.2.0-beta.15: patches/node-pty@1.2.0-beta.15.patch react-native-gesture-handler@3.2.1: patches/react-native-gesture-handler@3.2.1.patch react-native-keyboard-controller@1.22.6: patches/react-native-keyboard-controller@1.22.6.patch + react-native-screens@5.0.0-t3.27: patches/react-native-screens@5.0.0-t3.27.patch # Android: a nested WebView hands a vertical drag to its scrolling parent at the page's edge. react-native-webview@14.0.1: patches/react-native-webview@14.0.1.patch