@momo-kits/foundation 0.164.1-beta.5 → 0.164.1-beta.7

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.
@@ -6,7 +6,7 @@ import Navigation from './Navigation';
6
6
  import { ApplicationContext, MiniAppContext, ScreenContext } from '../Context';
7
7
  import { GridSystem } from '../Layout';
8
8
  import { version } from '../package.json';
9
- import { useAppState, useSwipeBackHaptic } from './utils';
9
+ import { useAppState } from './utils';
10
10
  import { TooltipPortalHost, TooltipPortalProvider } from './TooltipPortal';
11
11
 
12
12
  const runAfterInteractions = InteractionManager.runAfterInteractions;
@@ -51,7 +51,6 @@ const StackScreen: React.FC<any> = props => {
51
51
  const navigation = useRef(new Navigation(props.navigation, context)).current;
52
52
  const heightHeader = useHeaderHeight();
53
53
  const { isBackgroundToForeground } = useAppState();
54
- useSwipeBackHaptic();
55
54
 
56
55
  const data = {
57
56
  ...initialParams,
@@ -6,7 +6,6 @@ import React, {
6
6
  useRef,
7
7
  } from 'react';
8
8
  import {
9
- CardAnimationContext,
10
9
  CardStyleInterpolators,
11
10
  StackNavigationOptions,
12
11
  TransitionPresets,
@@ -22,7 +21,6 @@ import { Animated, AppState, NativeModules, Platform } from 'react-native';
22
21
  import type { SharedValue } from 'react-native-reanimated';
23
22
  import { useSharedValue } from 'react-native-reanimated';
24
23
  import {
25
- ApplicationContext,
26
24
  MiniAppContext,
27
25
  ScreenContext,
28
26
  TrackingScopeContext,
@@ -334,139 +332,6 @@ const useNativeCanGoBack = () => {
334
332
  );
335
333
  };
336
334
 
337
- /**
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
350
- */
351
- const SWIPE_BACK_HAPTIC_COMMIT_RATIO = 0.5;
352
-
353
- const readAnimatedValue = (node: unknown, fallback: number): number => {
354
- const getValue = (node as { __getValue?: () => unknown } | undefined)
355
- ?.__getValue;
356
- if (typeof getValue !== 'function') {
357
- return fallback;
358
- }
359
-
360
- const value = getValue.call(node);
361
- return typeof value === 'number' ? value : fallback;
362
- };
363
-
364
- /**
365
- * `current.progress` is built as `gesture.interpolate(...)`, so its parent is
366
- * the raw pan translation the finger drives. Private react-navigation / RN
367
- * shape, verified against @react-navigation/stack 7.4.2 + react-native 0.80.1
368
- * — duck-typed so a rename degrades to "no haptic" instead of a crash.
369
- */
370
- const getSwipeGestureValue = (
371
- progress?: Animated.AnimatedInterpolation<number>,
372
- ): Animated.Value | undefined => {
373
- const parent = (progress as { _parent?: Animated.Value } | undefined)
374
- ?._parent;
375
-
376
- return typeof parent?.addListener === 'function' &&
377
- typeof parent?.removeListener === 'function'
378
- ? parent
379
- : undefined;
380
- };
381
-
382
- /**
383
- * one light tick once the swipe back passes the point where releasing commits
384
- * the pop. iOS only, mirroring `gestureEnabled` in getStackOptions.
385
- *
386
- * both listeners are installed on mount and never lazily inside the gesture:
387
- * on the New Architecture `addListener` queues `startListeningToAnimatedNodeValue`
388
- * via `queueOperationBlock`, and that queue is only drained by a React mount
389
- * commit — which never happens during a native driven pan. subscribing when the
390
- * swipe starts installs an observer that the release flush tears down again
391
- * before a single frame is delivered, so it must stay on mount.
392
- *
393
- * `gesture` also drives the programmatic push and pop animations, so it is the
394
- * JS `isSwiping` flag, not the subscription lifetime, that keeps the tick
395
- * exclusive to a real finger drag.
396
- */
397
- const useSwipeBackHaptic = () => {
398
- const { navigator } = useContext<any>(ApplicationContext);
399
- const animation = useContext(CardAnimationContext);
400
-
401
- /**
402
- * the latch has to outlive the effect. `animation` identity changes on every
403
- * navigator render, so a `hasFired` local re-arms several times per drag and
404
- * the tick then repeats on every frame past the threshold
405
- */
406
- const hasFired = useRef(false);
407
-
408
- useEffect(() => {
409
- if (Platform.OS !== 'ios' || !animation) {
410
- return;
411
- }
412
-
413
- const { swiping, inverted, current, layouts } = animation;
414
- const gesture = getSwipeGestureValue(current?.progress);
415
- if (!gesture || typeof swiping?.addListener !== 'function') {
416
- return;
417
- }
418
-
419
- /**
420
- * `animation` identity is not stable, so this effect can re-run mid swipe.
421
- * seed from the live value instead of waiting for a 0 -> 1 transition that
422
- * has already been missed
423
- */
424
- let isSwiping = readAnimatedValue(swiping, 0) === 1;
425
-
426
- /**
427
- * sign, not magnitude: a leftward overdrag reports a negative
428
- * translation and must not count as progress toward dismissal
429
- */
430
- const direction = readAnimatedValue(inverted, 1);
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
- const swipingListener = swiping.addListener(({ value }) => {
441
- isSwiping = value === 1;
442
- if (!isSwiping) {
443
- hasFired.current = false;
444
- }
445
- });
446
-
447
- const gestureListener = gesture.addListener(({ value: translation }) => {
448
- if (!isSwiping || commitThreshold <= 0) {
449
- return;
450
- }
451
-
452
- if (translation * direction < commitThreshold) {
453
- hasFired.current = false;
454
- return;
455
- }
456
-
457
- if (!hasFired.current) {
458
- hasFired.current = true;
459
- navigator?.maxApi?.triggerEventVibration?.('light');
460
- }
461
- });
462
-
463
- return () => {
464
- gesture.removeListener(gestureListener);
465
- swiping.removeListener(swipingListener);
466
- };
467
- }, [animation, navigator]);
468
- };
469
-
470
335
  /**
471
336
  * Union of the two animation runtimes an internal foundation component might
472
337
  * receive. Public Screen props still accept React Native Animated.Value only;
@@ -515,5 +380,4 @@ export {
515
380
  useAppState,
516
381
  useNativeCanGoBack,
517
382
  useNormalizedSharedValue,
518
- useSwipeBackHaptic,
519
383
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.164.1-beta.5",
3
+ "version": "0.164.1-beta.7",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {},