fix(mobile): delay iOS message actions until a deliberate hold (#9049)

## Summary
Increase the iOS message and reaction hold delay from 200 ms to 500 ms,
giving users more time to start scrolling without opening a menu.
Movement detection and other platforms remain unchanged.

Apple documents a [0.5-second default for
UILongPressGestureRecognizer](https://developer.apple.com/documentation/uikit/uilongpressgesturerecognizer/minimumpressduration).

### Related issue
Fixes #8500.

### Testing
- Regression checks fail with the old timeout and pass with the change:
no recognition at 499 ms, recognition by 501 ms, and scrolling after a
300 ms pause cancels the hold.
- Device testing pending; the local iOS simulator SDK is unavailable.
Before review, verify deliberate holds, slow scroll starts, and reaction
holds on an iPhone.

🤖 Generated with Goose

Signed-off-by: Matt Wilcox <mwilcox@squareup.com>
Co-authored-by: Gen <8a28d770b293a0b223e7c671e5985a35fd9bc1a3c7e3fa92ff715bd2fe61b253@squarewebplatform.communities.buzz.xyz>
This commit is contained in:
Matt Wilcox
2026-11-05 17:34:10 -06:00
committed by GitHub
parent b0f68a06ad
commit 4aae6c22aa
2 changed files with 10 additions and 4 deletions

View File

@@ -8,7 +8,7 @@ import 'package:flutter/rendering.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
/// Shared hold deadline so nested reaction pills win before the message menu.
const iosMessageLongPressDuration = Duration(milliseconds: 200);
const iosMessageLongPressDuration = Duration(milliseconds: 500);
const _maxMessageSnapshotDimension = 2048.0;
double _messageSnapshotPixelRatio(Size size, double devicePixelRatio) {

View File

@@ -476,7 +476,7 @@ void main() {
});
testWidgets(
'message long press keeps taps and scrolling while repeated holds win',
'message long press keeps taps and delayed scrolling while repeated holds win',
(tester) async {
var parentTaps = 0;
var nestedTaps = 0;
@@ -536,6 +536,8 @@ void main() {
final drag = await tester.startGesture(
tester.getCenter(find.byKey(const ValueKey('parent-gesture-target'))),
);
await tester.pump(const Duration(milliseconds: 300));
expect(longPresses, 5);
await drag.moveBy(const Offset(0, -30));
await tester.pump();
await drag.moveBy(const Offset(0, -70));
@@ -547,9 +549,13 @@ void main() {
expect(longPresses, 5);
expect(scrollController.offset, greaterThan(0));
},
variant: TargetPlatformVariant({
TargetPlatform.iOS,
TargetPlatform.android,
}),
);
testWidgets('iOS message long press recognizes at 200 ms', (tester) async {
testWidgets('iOS message long press recognizes at 500 ms', (tester) async {
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
try {
var longPresses = 0;
@@ -571,7 +577,7 @@ void main() {
final gesture = await tester.startGesture(
tester.getCenter(find.byKey(const ValueKey('ios-long-press-target'))),
);
await tester.pump(const Duration(milliseconds: 199));
await tester.pump(const Duration(milliseconds: 499));
expect(longPresses, 0);
await tester.pump(const Duration(milliseconds: 2));
expect(longPresses, 1);