diff --git a/package-lock.json b/package-lock.json index a75678dc5..b41b24663 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7267,9 +7267,9 @@ } }, "node_modules/@gorhom/bottom-sheet": { - "version": "5.2.8", - "resolved": "https://registry.npmjs.org/@gorhom/bottom-sheet/-/bottom-sheet-5.2.8.tgz", - "integrity": "sha512-+N27SMpbBxXZQ/IA2nlEV6RGxL/qSFHKfdFKcygvW+HqPG5jVNb1OqehLQsGfBP+Up42i0gW5ppI+DhpB7UCzA==", + "version": "5.2.14", + "resolved": "https://registry.npmjs.org/@gorhom/bottom-sheet/-/bottom-sheet-5.2.14.tgz", + "integrity": "sha512-uLQFlDjp9z+jrOFcMSEldPqL5JdaXL3vXOh+juhwoNvXgTsEorJLjHTugXu+YccAG/0KJnShzKCrb71MHBsvJg==", "license": "MIT", "dependencies": { "@gorhom/portal": "1.0.14", @@ -39298,7 +39298,7 @@ "@floating-ui/react-native": "^0.10.7", "@getpaseo/expo-two-way-audio": "*", "@getpaseo/highlight": "*", - "@gorhom/bottom-sheet": "^5.2.6", + "@gorhom/bottom-sheet": "^5.2.14", "@gorhom/portal": "^1.0.14", "@react-native-async-storage/async-storage": "2.2.0", "@react-native-masked-view/masked-view": "^0.3.2", diff --git a/packages/app/package.json b/packages/app/package.json index 1e1fdcd32..27ee73cdb 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -33,7 +33,7 @@ "@floating-ui/react-native": "^0.10.7", "@getpaseo/expo-two-way-audio": "*", "@getpaseo/highlight": "*", - "@gorhom/bottom-sheet": "^5.2.6", + "@gorhom/bottom-sheet": "^5.2.14", "@gorhom/portal": "^1.0.14", "@react-native-async-storage/async-storage": "2.2.0", "@react-native-masked-view/masked-view": "^0.3.2", diff --git a/patches/react-native-gesture-handler+2.28.0.patch b/patches/react-native-gesture-handler+2.28.0.patch new file mode 100644 index 000000000..0686ac835 --- /dev/null +++ b/patches/react-native-gesture-handler+2.28.0.patch @@ -0,0 +1,60 @@ +diff --git a/node_modules/react-native-gesture-handler/lib/commonjs/web/tools/PointerEventManager.js b/node_modules/react-native-gesture-handler/lib/commonjs/web/tools/PointerEventManager.js +index 76eb808..0debe83 100644 +--- a/node_modules/react-native-gesture-handler/lib/commonjs/web/tools/PointerEventManager.js ++++ b/node_modules/react-native-gesture-handler/lib/commonjs/web/tools/PointerEventManager.js +@@ -58,7 +58,14 @@ class PointerEventManager extends _EventManager.default { + const adaptedEvent = this.mapEvent(event, _interfaces.EventTypes.UP); + const target = event.target; + if (!POINTER_CAPTURE_EXCLUDE_LIST.has(target.tagName)) { +- target.releasePointerCapture(adaptedEvent.pointerId); ++ try { ++ if (target.hasPointerCapture(adaptedEvent.pointerId)) { ++ target.releasePointerCapture(adaptedEvent.pointerId); ++ } ++ } catch { ++ // Pointer capture is implicitly released after pointerup/pointercancel. ++ // Some browsers can report stale capture state during gesture teardown. ++ } + } + this.markAsOutOfBounds(adaptedEvent.pointerId); + this.trackedPointers.delete(adaptedEvent.pointerId); +diff --git a/node_modules/react-native-gesture-handler/lib/module/web/tools/PointerEventManager.js b/node_modules/react-native-gesture-handler/lib/module/web/tools/PointerEventManager.js +index 6ce3b8f..3976f3f 100644 +--- a/node_modules/react-native-gesture-handler/lib/module/web/tools/PointerEventManager.js ++++ b/node_modules/react-native-gesture-handler/lib/module/web/tools/PointerEventManager.js +@@ -53,7 +53,14 @@ export default class PointerEventManager extends EventManager { + const adaptedEvent = this.mapEvent(event, EventTypes.UP); + const target = event.target; + if (!POINTER_CAPTURE_EXCLUDE_LIST.has(target.tagName)) { +- target.releasePointerCapture(adaptedEvent.pointerId); ++ try { ++ if (target.hasPointerCapture(adaptedEvent.pointerId)) { ++ target.releasePointerCapture(adaptedEvent.pointerId); ++ } ++ } catch { ++ // Pointer capture is implicitly released after pointerup/pointercancel. ++ // Some browsers can report stale capture state during gesture teardown. ++ } + } + this.markAsOutOfBounds(adaptedEvent.pointerId); + this.trackedPointers.delete(adaptedEvent.pointerId); +diff --git a/node_modules/react-native-gesture-handler/src/web/tools/PointerEventManager.ts b/node_modules/react-native-gesture-handler/src/web/tools/PointerEventManager.ts +index 18d3627..769b0fe 100644 +--- a/node_modules/react-native-gesture-handler/src/web/tools/PointerEventManager.ts ++++ b/node_modules/react-native-gesture-handler/src/web/tools/PointerEventManager.ts +@@ -67,7 +67,14 @@ export default class PointerEventManager extends EventManager { + const target = event.target as HTMLElement; + + if (!POINTER_CAPTURE_EXCLUDE_LIST.has(target.tagName)) { +- target.releasePointerCapture(adaptedEvent.pointerId); ++ try { ++ if (target.hasPointerCapture(adaptedEvent.pointerId)) { ++ target.releasePointerCapture(adaptedEvent.pointerId); ++ } ++ } catch { ++ // Pointer capture is implicitly released after pointerup/pointercancel. ++ // Some browsers can report stale capture state during gesture teardown. ++ } + } + + this.markAsOutOfBounds(adaptedEvent.pointerId); diff --git a/scripts/postinstall-patches.mjs b/scripts/postinstall-patches.mjs index 80b2dfab7..ac4f61b6c 100644 --- a/scripts/postinstall-patches.mjs +++ b/scripts/postinstall-patches.mjs @@ -1,18 +1,56 @@ -import { existsSync } from "node:fs"; +import { copyFileSync, existsSync, mkdirSync, readdirSync, rmSync } from "node:fs"; import { spawnSync } from "node:child_process"; +import { join } from "node:path"; // In CI we often install a single workspace (e.g. server/relay/website). Only apply patches // when the patched dependency is actually present. -const hasDraggableFlatlist = existsSync("node_modules/react-native-draggable-flatlist"); -if (!hasDraggableFlatlist) { +const patchedPackages = [ + { + nodeModulesPath: "node_modules/react-native-draggable-flatlist", + patchPrefix: "react-native-draggable-flatlist+", + }, + { + nodeModulesPath: "node_modules/react-native-gesture-handler", + patchPrefix: "react-native-gesture-handler+", + }, +]; + +const installedPatchPrefixes = patchedPackages + .filter(({ nodeModulesPath }) => existsSync(nodeModulesPath)) + .map(({ patchPrefix }) => patchPrefix); + +if (!existsSync("patches") || installedPatchPrefixes.length === 0) { + process.exit(0); +} + +const patchFilesToApply = readdirSync("patches").filter( + (file) => + file.endsWith(".patch") && + installedPatchPrefixes.some((patchPrefix) => file.startsWith(patchPrefix)), +); + +if (patchFilesToApply.length === 0) { process.exit(0); } const isWindows = process.platform === "win32"; const cmd = isWindows ? "patch-package.cmd" : "patch-package"; -const result = spawnSync(cmd, [], { - shell: isWindows, - stdio: "inherit", - windowsHide: true, -}); +const tempPatchDir = join(".tmp", `postinstall-patches-${process.pid}`); + +mkdirSync(tempPatchDir, { recursive: true }); +for (const patchFile of patchFilesToApply) { + copyFileSync(join("patches", patchFile), join(tempPatchDir, patchFile)); +} + +let result; +try { + result = spawnSync(cmd, ["--patch-dir", tempPatchDir], { + shell: isWindows, + stdio: "inherit", + windowsHide: true, + }); +} finally { + rmSync(tempPatchDir, { recursive: true, force: true }); +} + process.exit(result.status ?? 1);