Fix compact web sheet swipe crashes

This commit is contained in:
Mohamed Boudra
2026-05-18 15:29:01 +07:00
parent 29ce6653fd
commit 120f5d94a9
4 changed files with 111 additions and 13 deletions

8
package-lock.json generated
View File

@@ -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",

View File

@@ -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",

View File

@@ -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<HTMLElement> {
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);

View File

@@ -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);