The drawer gesture's 40px-wide left-edge hitSlop spans the full screen height; its y-position check only ran in onEnd, after the pan had already activated and stolen the touch from the native back-swipe recognizer. Fail the gesture immediately when the touch starts below the header so left-edge back-swipes work everywhere else.
Unified · Split
| @@ -276,6 +276,16 @@ export const ThreadDetailScreen = memo(function ThreadDetailScreen(props: Thread · 275 unmodified lines | |||
| 276 | .hitSlop({ left: 0, width: 40 }) | 276 | .hitSlop({ left: 0, width: 40 }) |
| 277 | .activeOffsetX([10, 999]) | 277 | .activeOffsetX([10, 999]) |
| 278 | .failOffsetY([-24, 24]) | 278 | .failOffsetY([-24, 24]) |
| 279 | // Fail at touch-down for anything below the header strip. Activating | ||
| 280 | // there (the y check in onEnd fires too late to matter) swallows the | ||
| 281 | // left-edge touch and blocks the native back-swipe along the whole | ||
| 282 | // screen height. | ||
| 283 | .onTouchesDown((event, stateManager) => { | ||
| 284 | const touchY = event.allTouches[0]?.y ?? Number.POSITIVE_INFINITY; | ||
| 285 | if (touchY >= drawerGestureThreshold) { | ||
| 286 | stateManager.fail(); | ||
| 287 | } | ||
| 288 | }) | ||
| 279 | .onEnd((event) => { | 289 | .onEnd((event) => { |
| 280 | const translationX = Math.max(event.translationX, 0); | 290 | const translationX = Math.max(event.translationX, 0); |
| 281 | if (event.y < drawerGestureThreshold && translationX > 56) { | 291 | if (event.y < drawerGestureThreshold && translationX > 56) { |