@momo-kits/foundation 0.164.1-beta.4 → 0.164.1-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Application/utils.tsx +57 -11
- package/package.json +1 -1
package/Application/utils.tsx
CHANGED
|
@@ -335,11 +335,20 @@ const useNativeCanGoBack = () => {
|
|
|
335
335
|
};
|
|
336
336
|
|
|
337
337
|
/**
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
*
|
|
338
|
+
* fraction of screen width at which releasing commits the pop. deliberately
|
|
339
|
+
* pinned to react-navigation's own rule: Card.tsx (GestureState.END,
|
|
340
|
+
* @react-navigation/stack 7.4.2) closes the card when
|
|
341
|
+
* `(translation + velocity * gestureVelocityImpact) * inverted > distance / 2`
|
|
342
|
+
* with `distance = layout.width` for a horizontal gesture, and exposes no
|
|
343
|
+
* option to override it. so the tick means "release now and you go back"
|
|
344
|
+
* instead of marking a distance of its own — keep the two in sync.
|
|
345
|
+
*
|
|
346
|
+
* mid drag only translation is known, velocity exists solely at release, so a
|
|
347
|
+
* short fast flick can commit without ever crossing this and pops with no
|
|
348
|
+
* tick. that asymmetry is intended: modelling velocity per frame would tick
|
|
349
|
+
* on swipes that then do not commit, which is the worse error
|
|
341
350
|
*/
|
|
342
|
-
const
|
|
351
|
+
const SWIPE_BACK_HAPTIC_COMMIT_RATIO = 0.5;
|
|
343
352
|
|
|
344
353
|
const readAnimatedValue = (node: unknown, fallback: number): number => {
|
|
345
354
|
const getValue = (node as { __getValue?: () => unknown } | undefined)
|
|
@@ -371,8 +380,8 @@ const getSwipeGestureValue = (
|
|
|
371
380
|
};
|
|
372
381
|
|
|
373
382
|
/**
|
|
374
|
-
* one light tick once the swipe back
|
|
375
|
-
*
|
|
383
|
+
* one light tick once the swipe back passes the point where releasing commits
|
|
384
|
+
* the pop. iOS only, mirroring `gestureEnabled` in getStackOptions.
|
|
376
385
|
*
|
|
377
386
|
* both listeners are installed on mount and never lazily inside the gesture:
|
|
378
387
|
* on the New Architecture `addListener` queues `startListeningToAnimatedNodeValue`
|
|
@@ -401,7 +410,7 @@ const useSwipeBackHaptic = () => {
|
|
|
401
410
|
return;
|
|
402
411
|
}
|
|
403
412
|
|
|
404
|
-
const { swiping, inverted, current } = animation;
|
|
413
|
+
const { swiping, inverted, current, layouts } = animation;
|
|
405
414
|
const gesture = getSwipeGestureValue(current?.progress);
|
|
406
415
|
if (!gesture || typeof swiping?.addListener !== 'function') {
|
|
407
416
|
return;
|
|
@@ -420,19 +429,52 @@ const useSwipeBackHaptic = () => {
|
|
|
420
429
|
*/
|
|
421
430
|
const direction = readAnimatedValue(inverted, 1);
|
|
422
431
|
|
|
432
|
+
/**
|
|
433
|
+
* plain JS numbers, not animated nodes (`StackCardInterpolationProps`).
|
|
434
|
+
* screen layout can still be unmeasured on an early render, and a zero
|
|
435
|
+
* width would put the threshold at 0 and tick the moment the finger moves
|
|
436
|
+
*/
|
|
437
|
+
const commitThreshold =
|
|
438
|
+
(layouts?.screen?.width ?? 0) * SWIPE_BACK_HAPTIC_COMMIT_RATIO;
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* TEMPORARY DIAGNOSTIC (DS-760) — remove once the swipe-back no-pop is understood.
|
|
442
|
+
*
|
|
443
|
+
* The test device cannot run Xcode, so this borrows the one channel proven to reach it.
|
|
444
|
+
* On release past the commit threshold react-navigation should pop; if this screen is
|
|
445
|
+
* still mounted shortly after, the pop was requested and then swallowed — `Card` clears
|
|
446
|
+
* its 32ms `pendingGestureCallback` from `animate()`, and `componentDidUpdate` re-enters
|
|
447
|
+
* `animate()` for any re-render in that window.
|
|
448
|
+
*
|
|
449
|
+
* A `heavy` tick after release therefore means "gesture completed, pop lost". Silence
|
|
450
|
+
* means the gesture never reached the threshold, or was cancelled before END.
|
|
451
|
+
*/
|
|
452
|
+
let lastTravel = 0;
|
|
453
|
+
let pendingPopProbe: ReturnType<typeof setTimeout> | undefined;
|
|
454
|
+
|
|
423
455
|
const swipingListener = swiping.addListener(({ value }) => {
|
|
456
|
+
const wasSwiping = isSwiping;
|
|
424
457
|
isSwiping = value === 1;
|
|
425
|
-
if (
|
|
426
|
-
|
|
458
|
+
if (isSwiping) {
|
|
459
|
+
return;
|
|
427
460
|
}
|
|
461
|
+
hasFired.current = false;
|
|
462
|
+
if (wasSwiping && commitThreshold > 0 && lastTravel >= commitThreshold) {
|
|
463
|
+
pendingPopProbe = setTimeout(() => {
|
|
464
|
+
navigator?.maxApi?.triggerEventVibration?.('heavy');
|
|
465
|
+
}, 500);
|
|
466
|
+
}
|
|
467
|
+
lastTravel = 0;
|
|
428
468
|
});
|
|
429
469
|
|
|
430
470
|
const gestureListener = gesture.addListener(({ value: translation }) => {
|
|
431
|
-
if (!isSwiping) {
|
|
471
|
+
if (!isSwiping || commitThreshold <= 0) {
|
|
432
472
|
return;
|
|
433
473
|
}
|
|
434
474
|
|
|
435
|
-
|
|
475
|
+
lastTravel = translation * direction;
|
|
476
|
+
|
|
477
|
+
if (lastTravel < commitThreshold) {
|
|
436
478
|
hasFired.current = false;
|
|
437
479
|
return;
|
|
438
480
|
}
|
|
@@ -444,6 +486,10 @@ const useSwipeBackHaptic = () => {
|
|
|
444
486
|
});
|
|
445
487
|
|
|
446
488
|
return () => {
|
|
489
|
+
// unmount cancels the probe: the screen popped, which is the success case
|
|
490
|
+
if (pendingPopProbe !== undefined) {
|
|
491
|
+
clearTimeout(pendingPopProbe);
|
|
492
|
+
}
|
|
447
493
|
gesture.removeListener(gestureListener);
|
|
448
494
|
swiping.removeListener(swipingListener);
|
|
449
495
|
};
|