@userflow-tech/react-native 0.4.5 → 0.5.0
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/README.md +7 -2
- package/docs/anchoring.md +6 -1
- package/docs/troubleshooting.md +26 -0
- package/lib/commonjs/anchors/AutoAnchorRegistry.js +144 -34
- package/lib/commonjs/anchors/AutoAnchorRegistry.js.map +1 -1
- package/lib/commonjs/anchors/animationFrame.js +29 -1
- package/lib/commonjs/anchors/animationFrame.js.map +1 -1
- package/lib/commonjs/anchors/appActive.js +61 -0
- package/lib/commonjs/anchors/appActive.js.map +1 -0
- package/lib/commonjs/anchors/elementConditionEvaluator.js +25 -8
- package/lib/commonjs/anchors/elementConditionEvaluator.js.map +1 -1
- package/lib/commonjs/anchors/elementTapMonitor.js +67 -12
- package/lib/commonjs/anchors/elementTapMonitor.js.map +1 -1
- package/lib/commonjs/anchors/elementTargetMatch.js +33 -7
- package/lib/commonjs/anchors/elementTargetMatch.js.map +1 -1
- package/lib/commonjs/anchors/fiber/fiberInternals.js +66 -0
- package/lib/commonjs/anchors/fiber/fiberInternals.js.map +1 -1
- package/lib/commonjs/anchors/fiber/indexHostFibers.js +4 -3
- package/lib/commonjs/anchors/fiber/indexHostFibers.js.map +1 -1
- package/lib/commonjs/anchors/fiber/resolveFiberPath.js +30 -15
- package/lib/commonjs/anchors/fiber/resolveFiberPath.js.map +1 -1
- package/lib/commonjs/anchors/fiberAnchorCache.js +20 -0
- package/lib/commonjs/anchors/fiberAnchorCache.js.map +1 -1
- package/lib/commonjs/anchors/gestureHandlerHandoff.js +304 -0
- package/lib/commonjs/anchors/gestureHandlerHandoff.js.map +1 -0
- package/lib/commonjs/anchors/resolveAnchorHost.js +6 -0
- package/lib/commonjs/anchors/resolveAnchorHost.js.map +1 -1
- package/lib/commonjs/anchors/resolveAutoAnchorHost.js +11 -6
- package/lib/commonjs/anchors/resolveAutoAnchorHost.js.map +1 -1
- package/lib/commonjs/anchors/resolveElementTargets.js +63 -8
- package/lib/commonjs/anchors/resolveElementTargets.js.map +1 -1
- package/lib/commonjs/anchors/semanticMatcher.js +43 -21
- package/lib/commonjs/anchors/semanticMatcher.js.map +1 -1
- package/lib/commonjs/anchors/tapSlop.js +16 -0
- package/lib/commonjs/anchors/tapSlop.js.map +1 -0
- package/lib/commonjs/anchors/targetPressMonitor.js +66 -12
- package/lib/commonjs/anchors/targetPressMonitor.js.map +1 -1
- package/lib/commonjs/authoring/fingerprintFromInstance.js +5 -9
- package/lib/commonjs/authoring/fingerprintFromInstance.js.map +1 -1
- package/lib/commonjs/content/RichContentRenderer.js +3 -3
- package/lib/commonjs/content/RichContentRenderer.js.map +1 -1
- package/lib/commonjs/content/openSafeUrl.js +3 -1
- package/lib/commonjs/content/openSafeUrl.js.map +1 -1
- package/lib/commonjs/expo-router/useUserflowExpoRouter.js +6 -0
- package/lib/commonjs/expo-router/useUserflowExpoRouter.js.map +1 -1
- package/lib/commonjs/index.js +19 -5
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/navigation/navigateUrlUtils.js +105 -0
- package/lib/commonjs/navigation/navigateUrlUtils.js.map +1 -0
- package/lib/commonjs/navigation/openExternalNavigateUrl.js +14 -0
- package/lib/commonjs/navigation/openExternalNavigateUrl.js.map +1 -0
- package/lib/commonjs/navigation/openFlowNavigateUrl.js +47 -41
- package/lib/commonjs/navigation/openFlowNavigateUrl.js.map +1 -1
- package/lib/commonjs/pairing/PairingController.js +3 -1
- package/lib/commonjs/pairing/PairingController.js.map +1 -1
- package/lib/commonjs/runtime/BannerHost.js +38 -29
- package/lib/commonjs/runtime/BannerHost.js.map +1 -1
- package/lib/commonjs/runtime/TargetPressBridge.js +55 -7
- package/lib/commonjs/runtime/TargetPressBridge.js.map +1 -1
- package/lib/commonjs/runtime/UserflowOverlay.js +5 -11
- package/lib/commonjs/runtime/UserflowOverlay.js.map +1 -1
- package/lib/commonjs/runtime/UserflowProvider.js +66 -40
- package/lib/commonjs/runtime/UserflowProvider.js.map +1 -1
- package/lib/commonjs/runtime/bannerEngine.js +8 -2
- package/lib/commonjs/runtime/bannerEngine.js.map +1 -1
- package/lib/commonjs/runtime/bannerFlowSpacer.js +71 -0
- package/lib/commonjs/runtime/bannerFlowSpacer.js.map +1 -0
- package/lib/commonjs/runtime/flowEngine.js +11 -0
- package/lib/commonjs/runtime/flowEngine.js.map +1 -1
- package/lib/commonjs/runtime/previewStart.js +39 -0
- package/lib/commonjs/runtime/previewStart.js.map +1 -0
- package/lib/commonjs/runtime/pushBannerLayoutContext.js +98 -0
- package/lib/commonjs/runtime/pushBannerLayoutContext.js.map +1 -0
- package/lib/commonjs/screenTracker.js +12 -0
- package/lib/commonjs/screenTracker.js.map +1 -1
- package/lib/commonjs/surfaces/BannerBar.js +79 -61
- package/lib/commonjs/surfaces/BannerBar.js.map +1 -1
- package/lib/commonjs/surfaces/BannerSurface.js +26 -0
- package/lib/commonjs/surfaces/BannerSurface.js.map +1 -1
- package/lib/commonjs/surfaces/TooltipSurface.js +43 -41
- package/lib/commonjs/surfaces/TooltipSurface.js.map +1 -1
- package/lib/commonjs/surfaces/bannerBarLayout.js +77 -10
- package/lib/commonjs/surfaces/bannerBarLayout.js.map +1 -1
- package/lib/commonjs/surfaces/bannerWebView/BannerWebViewBar.js +112 -63
- package/lib/commonjs/surfaces/bannerWebView/BannerWebViewBar.js.map +1 -1
- package/lib/commonjs/surfaces/bannerWebView/buildCustomBannerHtml.js +59 -11
- package/lib/commonjs/surfaces/bannerWebView/buildCustomBannerHtml.js.map +1 -1
- package/lib/commonjs/surfaces/hostAccessibilityTrap.js +28 -0
- package/lib/commonjs/surfaces/hostAccessibilityTrap.js.map +1 -0
- package/lib/commonjs/surfaces/resolveBannerTheme.js +6 -3
- package/lib/commonjs/surfaces/resolveBannerTheme.js.map +1 -1
- package/lib/commonjs/surfaces/tooltip/TooltipBubble.js +3 -2
- package/lib/commonjs/surfaces/tooltip/TooltipBubble.js.map +1 -1
- package/lib/commonjs/surfaces/tooltip/targetSearchSchedule.js +134 -0
- package/lib/commonjs/surfaces/tooltip/targetSearchSchedule.js.map +1 -0
- package/lib/commonjs/version.js +1 -1
- package/lib/module/anchors/AutoAnchorRegistry.js +145 -35
- package/lib/module/anchors/AutoAnchorRegistry.js.map +1 -1
- package/lib/module/anchors/animationFrame.js +29 -2
- package/lib/module/anchors/animationFrame.js.map +1 -1
- package/lib/module/anchors/appActive.js +57 -0
- package/lib/module/anchors/appActive.js.map +1 -0
- package/lib/module/anchors/elementConditionEvaluator.js +26 -9
- package/lib/module/anchors/elementConditionEvaluator.js.map +1 -1
- package/lib/module/anchors/elementTapMonitor.js +67 -12
- package/lib/module/anchors/elementTapMonitor.js.map +1 -1
- package/lib/module/anchors/elementTargetMatch.js +32 -7
- package/lib/module/anchors/elementTargetMatch.js.map +1 -1
- package/lib/module/anchors/fiber/fiberInternals.js +65 -0
- package/lib/module/anchors/fiber/fiberInternals.js.map +1 -1
- package/lib/module/anchors/fiber/indexHostFibers.js +4 -3
- package/lib/module/anchors/fiber/indexHostFibers.js.map +1 -1
- package/lib/module/anchors/fiber/resolveFiberPath.js +30 -15
- package/lib/module/anchors/fiber/resolveFiberPath.js.map +1 -1
- package/lib/module/anchors/fiberAnchorCache.js +19 -0
- package/lib/module/anchors/fiberAnchorCache.js.map +1 -1
- package/lib/module/anchors/gestureHandlerHandoff.js +298 -0
- package/lib/module/anchors/gestureHandlerHandoff.js.map +1 -0
- package/lib/module/anchors/resolveAnchorHost.js +1 -0
- package/lib/module/anchors/resolveAnchorHost.js.map +1 -1
- package/lib/module/anchors/resolveAutoAnchorHost.js +11 -6
- package/lib/module/anchors/resolveAutoAnchorHost.js.map +1 -1
- package/lib/module/anchors/resolveElementTargets.js +63 -8
- package/lib/module/anchors/resolveElementTargets.js.map +1 -1
- package/lib/module/anchors/semanticMatcher.js +43 -21
- package/lib/module/anchors/semanticMatcher.js.map +1 -1
- package/lib/module/anchors/tapSlop.js +11 -0
- package/lib/module/anchors/tapSlop.js.map +1 -0
- package/lib/module/anchors/targetPressMonitor.js +53 -8
- package/lib/module/anchors/targetPressMonitor.js.map +1 -1
- package/lib/module/authoring/fingerprintFromInstance.js +6 -10
- package/lib/module/authoring/fingerprintFromInstance.js.map +1 -1
- package/lib/module/content/RichContentRenderer.js +3 -3
- package/lib/module/content/RichContentRenderer.js.map +1 -1
- package/lib/module/content/openSafeUrl.js +3 -1
- package/lib/module/content/openSafeUrl.js.map +1 -1
- package/lib/module/expo-router/useUserflowExpoRouter.js +7 -1
- package/lib/module/expo-router/useUserflowExpoRouter.js.map +1 -1
- package/lib/module/index.js +22 -7
- package/lib/module/index.js.map +1 -1
- package/lib/module/navigation/navigateUrlUtils.js +94 -0
- package/lib/module/navigation/navigateUrlUtils.js.map +1 -0
- package/lib/module/navigation/openExternalNavigateUrl.js +11 -0
- package/lib/module/navigation/openExternalNavigateUrl.js.map +1 -0
- package/lib/module/navigation/openFlowNavigateUrl.js +22 -36
- package/lib/module/navigation/openFlowNavigateUrl.js.map +1 -1
- package/lib/module/pairing/PairingController.js +3 -1
- package/lib/module/pairing/PairingController.js.map +1 -1
- package/lib/module/runtime/BannerHost.js +38 -29
- package/lib/module/runtime/BannerHost.js.map +1 -1
- package/lib/module/runtime/TargetPressBridge.js +56 -8
- package/lib/module/runtime/TargetPressBridge.js.map +1 -1
- package/lib/module/runtime/UserflowOverlay.js +5 -11
- package/lib/module/runtime/UserflowOverlay.js.map +1 -1
- package/lib/module/runtime/UserflowProvider.js +66 -40
- package/lib/module/runtime/UserflowProvider.js.map +1 -1
- package/lib/module/runtime/bannerEngine.js +7 -2
- package/lib/module/runtime/bannerEngine.js.map +1 -1
- package/lib/module/runtime/bannerFlowSpacer.js +65 -0
- package/lib/module/runtime/bannerFlowSpacer.js.map +1 -0
- package/lib/module/runtime/flowEngine.js +11 -0
- package/lib/module/runtime/flowEngine.js.map +1 -1
- package/lib/module/runtime/previewStart.js +35 -0
- package/lib/module/runtime/previewStart.js.map +1 -0
- package/lib/module/runtime/pushBannerLayoutContext.js +91 -0
- package/lib/module/runtime/pushBannerLayoutContext.js.map +1 -0
- package/lib/module/screenTracker.js +11 -0
- package/lib/module/screenTracker.js.map +1 -1
- package/lib/module/surfaces/BannerBar.js +80 -62
- package/lib/module/surfaces/BannerBar.js.map +1 -1
- package/lib/module/surfaces/BannerSurface.js +26 -0
- package/lib/module/surfaces/BannerSurface.js.map +1 -1
- package/lib/module/surfaces/TooltipSurface.js +44 -42
- package/lib/module/surfaces/TooltipSurface.js.map +1 -1
- package/lib/module/surfaces/bannerBarLayout.js +72 -11
- package/lib/module/surfaces/bannerBarLayout.js.map +1 -1
- package/lib/module/surfaces/bannerWebView/BannerWebViewBar.js +113 -64
- package/lib/module/surfaces/bannerWebView/BannerWebViewBar.js.map +1 -1
- package/lib/module/surfaces/bannerWebView/buildCustomBannerHtml.js +59 -11
- package/lib/module/surfaces/bannerWebView/buildCustomBannerHtml.js.map +1 -1
- package/lib/module/surfaces/hostAccessibilityTrap.js +24 -0
- package/lib/module/surfaces/hostAccessibilityTrap.js.map +1 -0
- package/lib/module/surfaces/resolveBannerTheme.js +6 -3
- package/lib/module/surfaces/resolveBannerTheme.js.map +1 -1
- package/lib/module/surfaces/tooltip/TooltipBubble.js +3 -2
- package/lib/module/surfaces/tooltip/TooltipBubble.js.map +1 -1
- package/lib/module/surfaces/tooltip/targetSearchSchedule.js +128 -0
- package/lib/module/surfaces/tooltip/targetSearchSchedule.js.map +1 -0
- package/lib/module/version.js +1 -1
- package/lib/typescript/commonjs/src/anchors/AutoAnchorRegistry.d.ts +36 -2
- package/lib/typescript/commonjs/src/anchors/AutoAnchorRegistry.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/animationFrame.d.ts +0 -4
- package/lib/typescript/commonjs/src/anchors/animationFrame.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/appActive.d.ts +8 -0
- package/lib/typescript/commonjs/src/anchors/appActive.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/anchors/elementTapMonitor.d.ts +17 -0
- package/lib/typescript/commonjs/src/anchors/elementTapMonitor.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/elementTargetMatch.d.ts +7 -0
- package/lib/typescript/commonjs/src/anchors/elementTargetMatch.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/fiber/fiberInternals.d.ts +7 -0
- package/lib/typescript/commonjs/src/anchors/fiber/fiberInternals.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/fiber/indexHostFibers.d.ts +3 -2
- package/lib/typescript/commonjs/src/anchors/fiber/indexHostFibers.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/fiber/resolveFiberPath.d.ts +3 -0
- package/lib/typescript/commonjs/src/anchors/fiber/resolveFiberPath.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/fiberAnchorCache.d.ts +5 -0
- package/lib/typescript/commonjs/src/anchors/fiberAnchorCache.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/gestureHandlerHandoff.d.ts +70 -0
- package/lib/typescript/commonjs/src/anchors/gestureHandlerHandoff.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/anchors/resolveAnchorHost.d.ts +1 -0
- package/lib/typescript/commonjs/src/anchors/resolveAnchorHost.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/resolveAutoAnchorHost.d.ts +11 -4
- package/lib/typescript/commonjs/src/anchors/resolveAutoAnchorHost.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/resolveElementTargets.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/semanticMatcher.d.ts +2 -0
- package/lib/typescript/commonjs/src/anchors/semanticMatcher.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/tapSlop.d.ts +9 -0
- package/lib/typescript/commonjs/src/anchors/tapSlop.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/anchors/targetPressMonitor.d.ts +13 -8
- package/lib/typescript/commonjs/src/anchors/targetPressMonitor.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/authoring/fingerprintFromInstance.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/content/RichContentRenderer.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/content/openSafeUrl.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/expo-router/useUserflowExpoRouter.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/index.d.ts +6 -5
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/navigation/navigateUrlUtils.d.ts +15 -0
- package/lib/typescript/commonjs/src/navigation/navigateUrlUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/navigation/openExternalNavigateUrl.d.ts +3 -0
- package/lib/typescript/commonjs/src/navigation/openExternalNavigateUrl.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/navigation/openFlowNavigateUrl.d.ts +2 -5
- package/lib/typescript/commonjs/src/navigation/openFlowNavigateUrl.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/pairing/PairingController.d.ts +6 -0
- package/lib/typescript/commonjs/src/pairing/PairingController.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/runtime/BannerHost.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/runtime/TargetPressBridge.d.ts +3 -0
- package/lib/typescript/commonjs/src/runtime/TargetPressBridge.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/runtime/UserflowOverlay.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/runtime/UserflowProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/runtime/bannerEngine.d.ts +5 -1
- package/lib/typescript/commonjs/src/runtime/bannerEngine.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/runtime/bannerFlowSpacer.d.ts +15 -0
- package/lib/typescript/commonjs/src/runtime/bannerFlowSpacer.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/runtime/flowEngine.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/runtime/previewStart.d.ts +13 -0
- package/lib/typescript/commonjs/src/runtime/previewStart.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/runtime/pushBannerLayoutContext.d.ts +17 -0
- package/lib/typescript/commonjs/src/runtime/pushBannerLayoutContext.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/screenTracker.d.ts +6 -0
- package/lib/typescript/commonjs/src/screenTracker.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/surfaces/BannerBar.d.ts +3 -1
- package/lib/typescript/commonjs/src/surfaces/BannerBar.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/surfaces/BannerSurface.d.ts +5 -1
- package/lib/typescript/commonjs/src/surfaces/BannerSurface.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/surfaces/TooltipSurface.d.ts +1 -1
- package/lib/typescript/commonjs/src/surfaces/TooltipSurface.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/surfaces/bannerBarLayout.d.ts +17 -0
- package/lib/typescript/commonjs/src/surfaces/bannerBarLayout.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/surfaces/bannerWebView/BannerWebViewBar.d.ts +3 -1
- package/lib/typescript/commonjs/src/surfaces/bannerWebView/BannerWebViewBar.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/surfaces/bannerWebView/buildCustomBannerHtml.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/surfaces/hostAccessibilityTrap.d.ts +14 -0
- package/lib/typescript/commonjs/src/surfaces/hostAccessibilityTrap.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/surfaces/resolveBannerTheme.d.ts +4 -1
- package/lib/typescript/commonjs/src/surfaces/resolveBannerTheme.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/surfaces/tooltip/TooltipBubble.d.ts +7 -1
- package/lib/typescript/commonjs/src/surfaces/tooltip/TooltipBubble.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/surfaces/tooltip/targetSearchSchedule.d.ts +60 -0
- package/lib/typescript/commonjs/src/surfaces/tooltip/targetSearchSchedule.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/version.d.ts +1 -1
- package/lib/typescript/module/src/anchors/AutoAnchorRegistry.d.ts +36 -2
- package/lib/typescript/module/src/anchors/AutoAnchorRegistry.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/animationFrame.d.ts +0 -4
- package/lib/typescript/module/src/anchors/animationFrame.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/appActive.d.ts +8 -0
- package/lib/typescript/module/src/anchors/appActive.d.ts.map +1 -0
- package/lib/typescript/module/src/anchors/elementTapMonitor.d.ts +17 -0
- package/lib/typescript/module/src/anchors/elementTapMonitor.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/elementTargetMatch.d.ts +7 -0
- package/lib/typescript/module/src/anchors/elementTargetMatch.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/fiber/fiberInternals.d.ts +7 -0
- package/lib/typescript/module/src/anchors/fiber/fiberInternals.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/fiber/indexHostFibers.d.ts +3 -2
- package/lib/typescript/module/src/anchors/fiber/indexHostFibers.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/fiber/resolveFiberPath.d.ts +3 -0
- package/lib/typescript/module/src/anchors/fiber/resolveFiberPath.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/fiberAnchorCache.d.ts +5 -0
- package/lib/typescript/module/src/anchors/fiberAnchorCache.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/gestureHandlerHandoff.d.ts +70 -0
- package/lib/typescript/module/src/anchors/gestureHandlerHandoff.d.ts.map +1 -0
- package/lib/typescript/module/src/anchors/resolveAnchorHost.d.ts +1 -0
- package/lib/typescript/module/src/anchors/resolveAnchorHost.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/resolveAutoAnchorHost.d.ts +11 -4
- package/lib/typescript/module/src/anchors/resolveAutoAnchorHost.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/resolveElementTargets.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/semanticMatcher.d.ts +2 -0
- package/lib/typescript/module/src/anchors/semanticMatcher.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/tapSlop.d.ts +9 -0
- package/lib/typescript/module/src/anchors/tapSlop.d.ts.map +1 -0
- package/lib/typescript/module/src/anchors/targetPressMonitor.d.ts +13 -8
- package/lib/typescript/module/src/anchors/targetPressMonitor.d.ts.map +1 -1
- package/lib/typescript/module/src/authoring/fingerprintFromInstance.d.ts.map +1 -1
- package/lib/typescript/module/src/content/RichContentRenderer.d.ts.map +1 -1
- package/lib/typescript/module/src/content/openSafeUrl.d.ts.map +1 -1
- package/lib/typescript/module/src/expo-router/useUserflowExpoRouter.d.ts.map +1 -1
- package/lib/typescript/module/src/index.d.ts +6 -5
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/navigation/navigateUrlUtils.d.ts +15 -0
- package/lib/typescript/module/src/navigation/navigateUrlUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/navigation/openExternalNavigateUrl.d.ts +3 -0
- package/lib/typescript/module/src/navigation/openExternalNavigateUrl.d.ts.map +1 -0
- package/lib/typescript/module/src/navigation/openFlowNavigateUrl.d.ts +2 -5
- package/lib/typescript/module/src/navigation/openFlowNavigateUrl.d.ts.map +1 -1
- package/lib/typescript/module/src/pairing/PairingController.d.ts +6 -0
- package/lib/typescript/module/src/pairing/PairingController.d.ts.map +1 -1
- package/lib/typescript/module/src/runtime/BannerHost.d.ts.map +1 -1
- package/lib/typescript/module/src/runtime/TargetPressBridge.d.ts +3 -0
- package/lib/typescript/module/src/runtime/TargetPressBridge.d.ts.map +1 -1
- package/lib/typescript/module/src/runtime/UserflowOverlay.d.ts.map +1 -1
- package/lib/typescript/module/src/runtime/UserflowProvider.d.ts.map +1 -1
- package/lib/typescript/module/src/runtime/bannerEngine.d.ts +5 -1
- package/lib/typescript/module/src/runtime/bannerEngine.d.ts.map +1 -1
- package/lib/typescript/module/src/runtime/bannerFlowSpacer.d.ts +15 -0
- package/lib/typescript/module/src/runtime/bannerFlowSpacer.d.ts.map +1 -0
- package/lib/typescript/module/src/runtime/flowEngine.d.ts.map +1 -1
- package/lib/typescript/module/src/runtime/previewStart.d.ts +13 -0
- package/lib/typescript/module/src/runtime/previewStart.d.ts.map +1 -0
- package/lib/typescript/module/src/runtime/pushBannerLayoutContext.d.ts +17 -0
- package/lib/typescript/module/src/runtime/pushBannerLayoutContext.d.ts.map +1 -0
- package/lib/typescript/module/src/screenTracker.d.ts +6 -0
- package/lib/typescript/module/src/screenTracker.d.ts.map +1 -1
- package/lib/typescript/module/src/surfaces/BannerBar.d.ts +3 -1
- package/lib/typescript/module/src/surfaces/BannerBar.d.ts.map +1 -1
- package/lib/typescript/module/src/surfaces/BannerSurface.d.ts +5 -1
- package/lib/typescript/module/src/surfaces/BannerSurface.d.ts.map +1 -1
- package/lib/typescript/module/src/surfaces/TooltipSurface.d.ts +1 -1
- package/lib/typescript/module/src/surfaces/TooltipSurface.d.ts.map +1 -1
- package/lib/typescript/module/src/surfaces/bannerBarLayout.d.ts +17 -0
- package/lib/typescript/module/src/surfaces/bannerBarLayout.d.ts.map +1 -1
- package/lib/typescript/module/src/surfaces/bannerWebView/BannerWebViewBar.d.ts +3 -1
- package/lib/typescript/module/src/surfaces/bannerWebView/BannerWebViewBar.d.ts.map +1 -1
- package/lib/typescript/module/src/surfaces/bannerWebView/buildCustomBannerHtml.d.ts.map +1 -1
- package/lib/typescript/module/src/surfaces/hostAccessibilityTrap.d.ts +14 -0
- package/lib/typescript/module/src/surfaces/hostAccessibilityTrap.d.ts.map +1 -0
- package/lib/typescript/module/src/surfaces/resolveBannerTheme.d.ts +4 -1
- package/lib/typescript/module/src/surfaces/resolveBannerTheme.d.ts.map +1 -1
- package/lib/typescript/module/src/surfaces/tooltip/TooltipBubble.d.ts +7 -1
- package/lib/typescript/module/src/surfaces/tooltip/TooltipBubble.d.ts.map +1 -1
- package/lib/typescript/module/src/surfaces/tooltip/targetSearchSchedule.d.ts +60 -0
- package/lib/typescript/module/src/surfaces/tooltip/targetSearchSchedule.d.ts.map +1 -0
- package/lib/typescript/module/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/anchors/AutoAnchorRegistry.ts +166 -35
- package/src/anchors/animationFrame.ts +31 -1
- package/src/anchors/appActive.ts +63 -0
- package/src/anchors/elementConditionEvaluator.ts +29 -12
- package/src/anchors/elementTapMonitor.ts +68 -10
- package/src/anchors/elementTargetMatch.ts +34 -7
- package/src/anchors/fiber/fiberInternals.ts +68 -0
- package/src/anchors/fiber/indexHostFibers.ts +8 -3
- package/src/anchors/fiber/resolveFiberPath.ts +25 -16
- package/src/anchors/fiberAnchorCache.ts +19 -0
- package/src/anchors/gestureHandlerHandoff.ts +356 -0
- package/src/anchors/resolveAnchorHost.ts +2 -0
- package/src/anchors/resolveAutoAnchorHost.ts +18 -8
- package/src/anchors/resolveElementTargets.ts +64 -7
- package/src/anchors/semanticMatcher.ts +48 -27
- package/src/anchors/tapSlop.ts +18 -0
- package/src/anchors/targetPressMonitor.ts +56 -19
- package/src/authoring/fingerprintFromInstance.ts +4 -5
- package/src/content/RichContentRenderer.tsx +4 -3
- package/src/content/openSafeUrl.ts +3 -1
- package/src/expo-router/useUserflowExpoRouter.ts +10 -1
- package/src/index.ts +20 -9
- package/src/navigation/navigateUrlUtils.ts +100 -0
- package/src/navigation/openExternalNavigateUrl.ts +9 -0
- package/src/navigation/openFlowNavigateUrl.ts +36 -36
- package/src/pairing/PairingController.ts +9 -1
- package/src/runtime/BannerHost.tsx +41 -23
- package/src/runtime/TargetPressBridge.tsx +66 -9
- package/src/runtime/UserflowOverlay.tsx +7 -9
- package/src/runtime/UserflowProvider.tsx +56 -32
- package/src/runtime/bannerEngine.ts +10 -2
- package/src/runtime/bannerFlowSpacer.tsx +70 -0
- package/src/runtime/flowEngine.ts +11 -0
- package/src/runtime/previewStart.ts +49 -0
- package/src/runtime/pushBannerLayoutContext.tsx +121 -0
- package/src/screenTracker.ts +11 -0
- package/src/surfaces/BannerBar.tsx +93 -55
- package/src/surfaces/BannerSurface.tsx +59 -3
- package/src/surfaces/TooltipSurface.tsx +63 -51
- package/src/surfaces/bannerBarLayout.ts +80 -7
- package/src/surfaces/bannerWebView/BannerWebViewBar.tsx +149 -57
- package/src/surfaces/bannerWebView/buildCustomBannerHtml.ts +63 -11
- package/src/surfaces/hostAccessibilityTrap.ts +25 -0
- package/src/surfaces/resolveBannerTheme.ts +14 -3
- package/src/surfaces/tooltip/TooltipBubble.tsx +8 -1
- package/src/surfaces/tooltip/targetSearchSchedule.ts +140 -0
- package/src/version.ts +1 -1
- package/vendor/core/dist/engine/BannerSessionEngine.d.ts +29 -2
- package/vendor/core/dist/engine/BannerSessionEngine.d.ts.map +1 -1
- package/vendor/core/dist/engine/BannerSessionEngine.js +87 -6
- package/vendor/core/dist/engine/BannerSessionEngine.js.map +1 -1
- package/vendor/core/dist/engine/ClientConditionEngine.d.ts.map +1 -1
- package/vendor/core/dist/engine/ClientConditionEngine.js +30 -7
- package/vendor/core/dist/engine/ClientConditionEngine.js.map +1 -1
- package/vendor/core/dist/engine/PairingEngine.d.ts +6 -1
- package/vendor/core/dist/engine/PairingEngine.d.ts.map +1 -1
- package/vendor/core/dist/engine/PairingEngine.js +23 -3
- package/vendor/core/dist/engine/PairingEngine.js.map +1 -1
- package/vendor/core/dist/engine/handleActions.d.ts.map +1 -1
- package/vendor/core/dist/engine/handleActions.js +67 -2
- package/vendor/core/dist/engine/handleActions.js.map +1 -1
- package/vendor/core/dist/version.d.ts +1 -1
- package/vendor/core/dist/version.js +1 -1
- package/vendor/core/package.json +1 -1
- package/vendor/protocol/package.json +1 -1
|
@@ -26,19 +26,46 @@ export function anchorIdsAboveTap(tap: ElementTap): ReadonlySet<string> {
|
|
|
26
26
|
const ids = new Set<string>();
|
|
27
27
|
let current: AnyFiber = tap.targetFiber;
|
|
28
28
|
for (let depth = 0; current && depth < MAX_ANCESTOR_WALK; depth += 1) {
|
|
29
|
-
|
|
30
|
-
if (props) {
|
|
31
|
-
for (const tier of ANCHOR_PROP_TIERS) {
|
|
32
|
-
const value = props[tier];
|
|
33
|
-
if (typeof value === 'string') ids.add(value);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
29
|
+
collectAnchorIds(current, ids);
|
|
36
30
|
current = getReturn(current);
|
|
37
31
|
}
|
|
38
32
|
anchorIdsByTap.set(tap, ids);
|
|
39
33
|
return ids;
|
|
40
34
|
}
|
|
41
35
|
|
|
36
|
+
/**
|
|
37
|
+
* The fiber on `start` (or an ancestor) that carries `anchorId`.
|
|
38
|
+
*
|
|
39
|
+
* The touch-start fiber is kept after the press navigates away, so a click can
|
|
40
|
+
* still match when a live scan of the new screen no longer finds the id.
|
|
41
|
+
*/
|
|
42
|
+
export function fiberBearingAnchorId(start: AnyFiber, anchorId: string): AnyFiber | null {
|
|
43
|
+
let current: AnyFiber = start;
|
|
44
|
+
for (let depth = 0; current && depth < MAX_ANCESTOR_WALK; depth += 1) {
|
|
45
|
+
if (fiberHasAnchorId(current, anchorId)) return current;
|
|
46
|
+
current = getReturn(current);
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function fiberHasAnchorId(fiber: AnyFiber, anchorId: string): boolean {
|
|
52
|
+
const props = getMemoizedProps(fiber);
|
|
53
|
+
if (!props) return false;
|
|
54
|
+
for (const tier of ANCHOR_PROP_TIERS) {
|
|
55
|
+
if (props[tier] === anchorId) return true;
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function collectAnchorIds(fiber: AnyFiber, ids: Set<string>): void {
|
|
61
|
+
const props = getMemoizedProps(fiber);
|
|
62
|
+
if (!props) return;
|
|
63
|
+
for (const tier of ANCHOR_PROP_TIERS) {
|
|
64
|
+
const value = props[tier];
|
|
65
|
+
if (typeof value === 'string') ids.add(value);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
42
69
|
export function sameFiber(a: AnyFiber, b: AnyFiber): boolean {
|
|
43
70
|
if (a == null || b == null) return false;
|
|
44
71
|
if (a === b) return true;
|
|
@@ -319,6 +319,74 @@ export function resolveCurrentFiber(fiber: AnyFiber): AnyFiber {
|
|
|
319
319
|
return fiber;
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
+
/** Where a fiber sits: its tree's current fiber, and whether it is under it. */
|
|
323
|
+
interface TreePlace {
|
|
324
|
+
current: AnyFiber;
|
|
325
|
+
inTree: boolean;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* {@link resolveCurrentFiber} for many fibers in one synchronous pass. Where
|
|
330
|
+
* each ancestor sits is remembered, so the pass walks every ancestor once
|
|
331
|
+
* instead of once per fiber below it. Discard it when the pass ends: a render
|
|
332
|
+
* moves `return` pointers.
|
|
333
|
+
*/
|
|
334
|
+
export function createCurrentFiberResolver(): (fiber: AnyFiber) => AnyFiber {
|
|
335
|
+
const placeOf = new Map<object, TreePlace | null>();
|
|
336
|
+
|
|
337
|
+
const locate = (fiber: AnyFiber): TreePlace | null => {
|
|
338
|
+
const path: AnyFiber[] = [];
|
|
339
|
+
let above: TreePlace | null = null;
|
|
340
|
+
let cur: AnyFiber = fiber;
|
|
341
|
+
for (let depth = 0; cur && depth < MAX_ANCESTOR_WALK; depth += 1) {
|
|
342
|
+
const known = placeOf.get(cur);
|
|
343
|
+
if (known !== undefined) {
|
|
344
|
+
above = known;
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
path.push(cur);
|
|
348
|
+
// Only a HostRoot (or a detached fiber) has no parent, so the tag is
|
|
349
|
+
// read once, at the top, not at every step.
|
|
350
|
+
const parent = getReturn(cur);
|
|
351
|
+
if (!parent) {
|
|
352
|
+
const rootState =
|
|
353
|
+
getFiberTag(cur) === HOST_ROOT_TAG
|
|
354
|
+
? (getStateNode(cur) as { current?: AnyFiber } | null)
|
|
355
|
+
: null;
|
|
356
|
+
above = rootState?.current ? { current: rootState.current, inTree: false } : null;
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
cur = parent;
|
|
360
|
+
}
|
|
361
|
+
// Top down: a fiber is in the current tree when the current fiber is it
|
|
362
|
+
// or one of its ancestors.
|
|
363
|
+
let place = above;
|
|
364
|
+
for (let i = path.length - 1; i >= 0; i -= 1) {
|
|
365
|
+
if (place && !place.inTree && path[i] === place.current) {
|
|
366
|
+
place = { current: place.current, inTree: true };
|
|
367
|
+
}
|
|
368
|
+
placeOf.set(path[i], place);
|
|
369
|
+
}
|
|
370
|
+
return place;
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
return (fiber) => {
|
|
374
|
+
if (!fiber) return fiber;
|
|
375
|
+
try {
|
|
376
|
+
const alternate = getAlternate(fiber);
|
|
377
|
+
if (!alternate) return fiber;
|
|
378
|
+
const own = locate(fiber);
|
|
379
|
+
if (!own || own.inTree) return fiber;
|
|
380
|
+
const other = locate(alternate);
|
|
381
|
+
// Two trees for one pair only happens on a corrupt graph.
|
|
382
|
+
if (!other || other.current !== own.current) return resolveCurrentFiber(fiber);
|
|
383
|
+
return other.inTree ? alternate : fiber;
|
|
384
|
+
} catch {
|
|
385
|
+
return fiber;
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
322
390
|
function isInTree(fiber: AnyFiber, root: AnyFiber): boolean {
|
|
323
391
|
let cur: AnyFiber = fiber;
|
|
324
392
|
for (let depth = 0; cur && depth < MAX_ANCESTOR_WALK; depth += 1) {
|
|
@@ -111,13 +111,18 @@ export function createLazyHostFiberIndex(
|
|
|
111
111
|
|
|
112
112
|
/**
|
|
113
113
|
* Fiber for a public instance: direct pointer first, then the scan-root index
|
|
114
|
-
* by identity / native tag.
|
|
114
|
+
* by identity / native tag. Pass a `createCurrentFiberResolver()` resolver when
|
|
115
|
+
* looking up many instances in one pass.
|
|
115
116
|
*/
|
|
116
|
-
export function lookupHostFiber(
|
|
117
|
+
export function lookupHostFiber(
|
|
118
|
+
index: HostFiberIndexSource,
|
|
119
|
+
instance: unknown,
|
|
120
|
+
resolveCurrent: (fiber: AnyFiber) => AnyFiber = resolveCurrentFiber,
|
|
121
|
+
): AnyFiber | null {
|
|
117
122
|
const direct = fiberFromPublicInstance(instance);
|
|
118
123
|
if (direct) {
|
|
119
124
|
try {
|
|
120
|
-
return
|
|
125
|
+
return resolveCurrent(direct);
|
|
121
126
|
} catch {
|
|
122
127
|
return direct;
|
|
123
128
|
}
|
|
@@ -31,10 +31,13 @@ import { walkVisibleFibers } from './walkVisibleFibers';
|
|
|
31
31
|
const MIN_SCORE = 40;
|
|
32
32
|
const UNIQUENESS_MARGIN = 12;
|
|
33
33
|
|
|
34
|
+
/** Why a fiber path did not resolve. Element conditions map these differently. */
|
|
35
|
+
export type FiberPathMissReason = 'empty' | 'no-root' | 'no-match';
|
|
36
|
+
|
|
34
37
|
export type FiberPathResolution =
|
|
35
38
|
| { status: 'found'; fiber: AnyFiber; cacheId: string; textDisagreed?: boolean }
|
|
36
39
|
| { status: 'ambiguous' }
|
|
37
|
-
| { status: 'unknown' };
|
|
40
|
+
| { status: 'unknown'; reason: FiberPathMissReason };
|
|
38
41
|
|
|
39
42
|
function stringProp(value: unknown): string | undefined {
|
|
40
43
|
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
@@ -50,18 +53,13 @@ function sameFiber(a: AnyFiber, b: AnyFiber): boolean {
|
|
|
50
53
|
return alt != null && alt === b;
|
|
51
54
|
}
|
|
52
55
|
|
|
53
|
-
|
|
56
|
+
/** The live segment without its text, which needs a subtree walk. */
|
|
57
|
+
function liveIdentity(fiber: AnyFiber): MobileFiberSegment {
|
|
54
58
|
const props = getLiveHostProps(fiber);
|
|
55
59
|
const role = stringProp(props?.accessibilityRole) ?? stringProp(props?.role);
|
|
56
60
|
const testID = stringProp(props?.testID);
|
|
57
61
|
const accessibilityLabel =
|
|
58
62
|
stringProp(props?.accessibilityLabel) ?? stringProp(props?.['aria-label']);
|
|
59
|
-
let text: string | undefined;
|
|
60
|
-
try {
|
|
61
|
-
text = readHostText(fiber, 1);
|
|
62
|
-
} catch {
|
|
63
|
-
text = undefined;
|
|
64
|
-
}
|
|
65
63
|
const hostKind = getHostTypeName(fiber);
|
|
66
64
|
return {
|
|
67
65
|
displayName: typeof fiber?.type === 'string' ? fiber.type : '',
|
|
@@ -69,10 +67,19 @@ function liveSegment(fiber: AnyFiber): MobileFiberSegment {
|
|
|
69
67
|
...(role ? { role } : {}),
|
|
70
68
|
...(testID ? { testID } : {}),
|
|
71
69
|
...(accessibilityLabel ? { accessibilityLabel } : {}),
|
|
72
|
-
...(text ? { text } : {}),
|
|
73
70
|
};
|
|
74
71
|
}
|
|
75
72
|
|
|
73
|
+
function liveSegment(fiber: AnyFiber): MobileFiberSegment {
|
|
74
|
+
let text: string | undefined;
|
|
75
|
+
try {
|
|
76
|
+
text = readHostText(fiber, 1);
|
|
77
|
+
} catch {
|
|
78
|
+
text = undefined;
|
|
79
|
+
}
|
|
80
|
+
return { ...liveIdentity(fiber), ...(text ? { text } : {}) };
|
|
81
|
+
}
|
|
82
|
+
|
|
76
83
|
function ancestorsOf(fiber: AnyFiber): AnyFiber[] {
|
|
77
84
|
const chain: AnyFiber[] = [];
|
|
78
85
|
let current: AnyFiber | null = fiber;
|
|
@@ -90,7 +97,8 @@ function scoreAlignment(expected: MobileFiberSegment[], actual: AnyFiber[]): num
|
|
|
90
97
|
let kindAligned = 0;
|
|
91
98
|
for (let i = 0; i < n; i += 1) {
|
|
92
99
|
const exp = expected[expected.length - 1 - i]!;
|
|
93
|
-
|
|
100
|
+
// Alignment never compares text, so skip the subtree walk for every ancestor.
|
|
101
|
+
const live = liveIdentity(actual[actual.length - 1 - i]!);
|
|
94
102
|
const expKind = exp.hostKind ?? exp.displayName;
|
|
95
103
|
const liveKind = live.hostKind ?? live.displayName;
|
|
96
104
|
if (expKind && liveKind && expKind !== liveKind) break;
|
|
@@ -107,13 +115,14 @@ function scoreHost(
|
|
|
107
115
|
path: MobileFiberPath,
|
|
108
116
|
fiber: AnyFiber,
|
|
109
117
|
): { score: number; textDisagreed: boolean } {
|
|
110
|
-
const live = liveSegment(fiber);
|
|
111
118
|
const leaf = path.segments[path.segments.length - 1];
|
|
112
119
|
const leafKind = leaf?.hostKind ?? path.leafKind;
|
|
113
|
-
|
|
120
|
+
// Kind first: most hosts fail here, before any props or text are read.
|
|
121
|
+
const liveKind = getHostTypeName(fiber) ?? '';
|
|
114
122
|
if (leafKind && liveKind && leafKind !== liveKind) {
|
|
115
123
|
return { score: -Infinity, textDisagreed: false };
|
|
116
124
|
}
|
|
125
|
+
const live = liveSegment(fiber);
|
|
117
126
|
|
|
118
127
|
let score = 20;
|
|
119
128
|
if (leaf?.role && live.role === leaf.role) score += 15;
|
|
@@ -151,15 +160,15 @@ function scoreHost(
|
|
|
151
160
|
|
|
152
161
|
function resolveScored(path: MobileFiberPath, root: AnyFiber): FiberPathResolution {
|
|
153
162
|
const scored: Array<{ fiber: AnyFiber; score: number; textDisagreed: boolean }> = [];
|
|
163
|
+
// The walk already skips covered screens, so no per-host visibility walk.
|
|
154
164
|
walkVisibleFibers(root, (fiber) => {
|
|
155
165
|
if (!isHostFiber(fiber)) return;
|
|
156
|
-
if (!isFiberOnVisibleScreen(fiber)) return;
|
|
157
166
|
const { score, textDisagreed } = scoreHost(path, fiber);
|
|
158
167
|
if (score > -Infinity) scored.push({ fiber, score, textDisagreed });
|
|
159
168
|
});
|
|
160
169
|
scored.sort((a, b) => b.score - a.score);
|
|
161
170
|
const best = scored[0];
|
|
162
|
-
if (!best || best.score < MIN_SCORE) return { status: 'unknown' };
|
|
171
|
+
if (!best || best.score < MIN_SCORE) return { status: 'unknown', reason: 'no-match' };
|
|
163
172
|
const second = scored[1];
|
|
164
173
|
if (second && best.score - second.score < UNIQUENESS_MARGIN) return { status: 'ambiguous' };
|
|
165
174
|
return {
|
|
@@ -175,7 +184,7 @@ function resolveScored(path: MobileFiberPath, root: AnyFiber): FiberPathResoluti
|
|
|
175
184
|
* and reuses the manual fiber scan.
|
|
176
185
|
*/
|
|
177
186
|
export function resolveFiberPath(path: MobileFiberPath | null | undefined): FiberPathResolution {
|
|
178
|
-
if (!path || path.segments.length === 0) return { status: 'unknown' };
|
|
187
|
+
if (!path || path.segments.length === 0) return { status: 'unknown', reason: 'empty' };
|
|
179
188
|
|
|
180
189
|
const leaf = path.segments[path.segments.length - 1];
|
|
181
190
|
const exactId = leaf?.testID;
|
|
@@ -200,7 +209,7 @@ export function resolveFiberPath(path: MobileFiberPath | null | undefined): Fibe
|
|
|
200
209
|
}
|
|
201
210
|
|
|
202
211
|
const root = fiberScanRoot.getFiber();
|
|
203
|
-
if (!root) return { status: 'unknown' };
|
|
212
|
+
if (!root) return { status: 'unknown', reason: 'no-root' };
|
|
204
213
|
|
|
205
214
|
const resolved = resolveScored(path, root);
|
|
206
215
|
if (resolved.status === 'found') {
|
|
@@ -7,6 +7,7 @@ interface FiberCacheEntry {
|
|
|
7
7
|
|
|
8
8
|
const fiberCache = new Map<string, FiberCacheEntry>();
|
|
9
9
|
let currentScreenKey: string | null = null;
|
|
10
|
+
const screenChangeListeners = new Set<() => void>();
|
|
10
11
|
|
|
11
12
|
export function getFiberAnchorScreenKey(): string | null {
|
|
12
13
|
return currentScreenKey;
|
|
@@ -17,6 +18,24 @@ export function notifyAnchorScreenChange(screenKey: string | null): void {
|
|
|
17
18
|
if (screenKey === currentScreenKey) return;
|
|
18
19
|
currentScreenKey = screenKey;
|
|
19
20
|
fiberCache.clear();
|
|
21
|
+
for (const listener of Array.from(screenChangeListeners)) {
|
|
22
|
+
try {
|
|
23
|
+
listener();
|
|
24
|
+
} catch {
|
|
25
|
+
// A listener must not break screen tracking.
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* `listener` runs after the active screen changes, once the cache above is
|
|
32
|
+
* cleared. Searches that back off while a target is missing restart from here.
|
|
33
|
+
*/
|
|
34
|
+
export function subscribeAnchorScreenChange(listener: () => void): () => void {
|
|
35
|
+
screenChangeListeners.add(listener);
|
|
36
|
+
return () => {
|
|
37
|
+
screenChangeListeners.delete(listener);
|
|
38
|
+
};
|
|
20
39
|
}
|
|
21
40
|
|
|
22
41
|
/** Drop the fiber cache (preview restart / tests). */
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Taps that react-native-gesture-handler takes away from React Native.
|
|
3
|
+
*
|
|
4
|
+
* When a gesture-handler control activates, it cancels RN's touch stream, so
|
|
5
|
+
* the bridge sees `touchcancel` instead of `touchend`. The handler tag is read
|
|
6
|
+
* off the touched fiber (no import of gesture-handler), and the touch counts
|
|
7
|
+
* as a tap only if that handler later ends from ACTIVE on
|
|
8
|
+
* `onGestureHandlerStateChange`. Apps that never hit this path never subscribe.
|
|
9
|
+
*
|
|
10
|
+
* State numbers are gesture-handler's `State` enum. A future major that
|
|
11
|
+
* renumbers them degrades to "no tap", which is today's behavior.
|
|
12
|
+
*/
|
|
13
|
+
import type { AnyFiber } from './fiber/fiberInternals';
|
|
14
|
+
import {
|
|
15
|
+
getMemoizedProps,
|
|
16
|
+
getReturn,
|
|
17
|
+
getStateNode,
|
|
18
|
+
MAX_ANCESTOR_WALK,
|
|
19
|
+
} from './fiber/fiberInternals';
|
|
20
|
+
import { type TouchPoint, withinTapSlop } from './tapSlop';
|
|
21
|
+
|
|
22
|
+
/** gesture-handler `State` values this hand-off acts on. */
|
|
23
|
+
export const GESTURE_HANDLER_STATE = {
|
|
24
|
+
FAILED: 1,
|
|
25
|
+
CANCELLED: 3,
|
|
26
|
+
ACTIVE: 4,
|
|
27
|
+
END: 5,
|
|
28
|
+
} as const;
|
|
29
|
+
|
|
30
|
+
/** Drop a handed-off touch that never reaches a terminal state. */
|
|
31
|
+
export const GESTURE_HANDLER_HANDOFF_TIMEOUT_MS = 10_000;
|
|
32
|
+
|
|
33
|
+
const STATE_CHANGE_EVENT = 'onGestureHandlerStateChange';
|
|
34
|
+
|
|
35
|
+
export interface HandedOffTouch {
|
|
36
|
+
identifier: number | string;
|
|
37
|
+
pageX: number;
|
|
38
|
+
pageY: number;
|
|
39
|
+
handlerTags: readonly number[];
|
|
40
|
+
/**
|
|
41
|
+
* The gesture completed as a tap. `end` is set when the payload carried
|
|
42
|
+
* window coordinates; otherwise only the start point is known.
|
|
43
|
+
*/
|
|
44
|
+
onResolve: (end: TouchPoint | null) => void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface DeviceEventSubscription {
|
|
48
|
+
remove: () => void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface DeviceEventEmitterLike {
|
|
52
|
+
addListener(event: string, listener: (payload: unknown) => void): DeviceEventSubscription;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface GestureStatePayload {
|
|
56
|
+
handlerTag: number;
|
|
57
|
+
state: number;
|
|
58
|
+
oldState?: number;
|
|
59
|
+
pointerInside?: boolean;
|
|
60
|
+
translationX?: number;
|
|
61
|
+
translationY?: number;
|
|
62
|
+
absoluteX?: number;
|
|
63
|
+
absoluteY?: number;
|
|
64
|
+
/** Present only so a long-press payload can be told apart from a tap. */
|
|
65
|
+
hasDuration: boolean;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface PendingTouch extends HandedOffTouch {
|
|
69
|
+
id: number;
|
|
70
|
+
settled: boolean;
|
|
71
|
+
/** Tags that already reached a terminal state without counting as a tap. */
|
|
72
|
+
finishedTags: Set<number>;
|
|
73
|
+
timer: ReturnType<typeof setTimeout>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Handler tags for the control under `fiber`.
|
|
78
|
+
*
|
|
79
|
+
* Old-API buttons keep `handlerTag` on the class instance. `GestureDetector`
|
|
80
|
+
* keeps a `gesture` prop whose `toGestureArray()` entries each have one.
|
|
81
|
+
* Negative tags (gesture-handler's own root helper) are ignored.
|
|
82
|
+
*
|
|
83
|
+
* The walk stops at the nearest fiber that has a tag. A native handler with
|
|
84
|
+
* `disallowInterruption` (gesture-handler `ScrollView`, `FlatList`,
|
|
85
|
+
* `RefreshControl`) is a container, not a tap, so a scroll that starts inside
|
|
86
|
+
* it is dropped instead of latching the row. Never throws.
|
|
87
|
+
*/
|
|
88
|
+
export function collectGestureHandlerTags(fiber: AnyFiber | null): number[] {
|
|
89
|
+
if (fiber == null) return [];
|
|
90
|
+
try {
|
|
91
|
+
let current: AnyFiber | null = fiber;
|
|
92
|
+
let steps = 0;
|
|
93
|
+
while (current && steps < MAX_ANCESTOR_WALK) {
|
|
94
|
+
steps += 1;
|
|
95
|
+
const found = tagsOnFiber(current);
|
|
96
|
+
if (found.pressable.length > 0) return found.pressable;
|
|
97
|
+
// The nearest handler is a scroll container. An outer control did not
|
|
98
|
+
// receive this touch.
|
|
99
|
+
if (found.container) return [];
|
|
100
|
+
current = getReturn(current);
|
|
101
|
+
}
|
|
102
|
+
} catch {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export class GestureHandlerHandoff {
|
|
109
|
+
private readonly pending = new Map<number, PendingTouch>();
|
|
110
|
+
private nextId = 1;
|
|
111
|
+
private subscription: DeviceEventSubscription | null = null;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Keep `touch` until its handler finishes or it expires.
|
|
115
|
+
* Returns a stop function; calling it drops the touch without a tap.
|
|
116
|
+
*/
|
|
117
|
+
track(touch: HandedOffTouch): () => void {
|
|
118
|
+
if (touch.handlerTags.length === 0) return () => {};
|
|
119
|
+
const id = this.nextId;
|
|
120
|
+
this.nextId += 1;
|
|
121
|
+
const pending: PendingTouch = {
|
|
122
|
+
...touch,
|
|
123
|
+
id,
|
|
124
|
+
settled: false,
|
|
125
|
+
finishedTags: new Set(),
|
|
126
|
+
timer: setTimeout(() => {
|
|
127
|
+
this.drop(id);
|
|
128
|
+
}, GESTURE_HANDLER_HANDOFF_TIMEOUT_MS),
|
|
129
|
+
};
|
|
130
|
+
this.pending.set(id, pending);
|
|
131
|
+
this.ensureListening();
|
|
132
|
+
return () => this.drop(id);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Test helper. Drops every pending touch and unsubscribes. */
|
|
136
|
+
resetForTests(): void {
|
|
137
|
+
for (const touch of this.pending.values()) {
|
|
138
|
+
touch.settled = true;
|
|
139
|
+
clearTimeout(touch.timer);
|
|
140
|
+
}
|
|
141
|
+
this.pending.clear();
|
|
142
|
+
this.stopListeningIfIdle();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** @internal Test hook: how many touches are still waiting. */
|
|
146
|
+
get pendingCount(): number {
|
|
147
|
+
return this.pending.size;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private ensureListening(): void {
|
|
151
|
+
if (this.subscription || this.pending.size === 0) return;
|
|
152
|
+
const emitter = deviceEventEmitter();
|
|
153
|
+
if (!emitter) return;
|
|
154
|
+
this.subscription = emitter.addListener(STATE_CHANGE_EVENT, (payload: unknown) => {
|
|
155
|
+
try {
|
|
156
|
+
this.handleStateChange(payload);
|
|
157
|
+
} catch {
|
|
158
|
+
// A bad payload must not break the next gesture.
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private stopListeningIfIdle(): void {
|
|
164
|
+
if (this.pending.size > 0 || !this.subscription) return;
|
|
165
|
+
this.subscription.remove();
|
|
166
|
+
this.subscription = null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private handleStateChange(event: unknown): void {
|
|
170
|
+
const payload = asPayload(event);
|
|
171
|
+
if (!payload) return;
|
|
172
|
+
const matching = [...this.pending.values()].filter(
|
|
173
|
+
(touch) => !touch.settled && touch.handlerTags.includes(payload.handlerTag),
|
|
174
|
+
);
|
|
175
|
+
for (const touch of matching) {
|
|
176
|
+
this.apply(touch, payload);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private apply(touch: PendingTouch, payload: GestureStatePayload): void {
|
|
181
|
+
if (touch.settled || !isTerminal(payload.state)) return;
|
|
182
|
+
if (isCompletedTap(touch, payload)) {
|
|
183
|
+
this.resolve(touch, endPoint(payload));
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
touch.finishedTags.add(payload.handlerTag);
|
|
187
|
+
const allDone = touch.handlerTags.every((tag) => touch.finishedTags.has(tag));
|
|
188
|
+
if (allDone) this.drop(touch.id);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private resolve(touch: PendingTouch, end: TouchPoint | null): void {
|
|
192
|
+
const notify = touch.onResolve;
|
|
193
|
+
this.drop(touch.id);
|
|
194
|
+
try {
|
|
195
|
+
notify(end);
|
|
196
|
+
} catch {
|
|
197
|
+
// One monitor's failure must not skip the other touch on this event.
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private drop(id: number): void {
|
|
202
|
+
const touch = this.pending.get(id);
|
|
203
|
+
if (!touch || touch.settled) return;
|
|
204
|
+
touch.settled = true;
|
|
205
|
+
clearTimeout(touch.timer);
|
|
206
|
+
this.pending.delete(id);
|
|
207
|
+
this.stopListeningIfIdle();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** Shared singleton used by the tap monitors. */
|
|
212
|
+
export const gestureHandlerHandoff = new GestureHandlerHandoff();
|
|
213
|
+
|
|
214
|
+
interface FiberTags {
|
|
215
|
+
/** Tags that can count as a tap. */
|
|
216
|
+
pressable: number[];
|
|
217
|
+
/** The fiber has a container handler and no pressable tag. */
|
|
218
|
+
container: boolean;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function tagsOnFiber(fiber: AnyFiber): FiberTags {
|
|
222
|
+
try {
|
|
223
|
+
const pressable: number[] = [];
|
|
224
|
+
const seen = new Set<number>();
|
|
225
|
+
let container = false;
|
|
226
|
+
const add = (tag: number | undefined, isContainer: boolean): void => {
|
|
227
|
+
if (tag === undefined) return;
|
|
228
|
+
if (isContainer) {
|
|
229
|
+
container = true;
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (seen.has(tag)) return;
|
|
233
|
+
seen.add(tag);
|
|
234
|
+
pressable.push(tag);
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const props = getMemoizedProps(fiber);
|
|
238
|
+
add(handlerTagOf(getStateNode(fiber)), props?.disallowInterruption === true);
|
|
239
|
+
|
|
240
|
+
const gesture = props?.gesture;
|
|
241
|
+
if (gesture && typeof gesture === 'object' && 'toGestureArray' in gesture) {
|
|
242
|
+
const toArray = (gesture as { toGestureArray?: unknown }).toGestureArray;
|
|
243
|
+
if (typeof toArray === 'function') {
|
|
244
|
+
const list = (toArray as () => unknown).call(gesture);
|
|
245
|
+
if (Array.isArray(list)) {
|
|
246
|
+
for (const item of list) {
|
|
247
|
+
const tag = handlerTagOf(item) ?? handlerTagOf(readHandlers(item));
|
|
248
|
+
add(tag, disallowsInterruption(item));
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return { pressable, container: container && pressable.length === 0 };
|
|
254
|
+
} catch {
|
|
255
|
+
return { pressable: [], container: false };
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Gesture-handler scroll containers set this so a drag is not stolen. */
|
|
260
|
+
function disallowsInterruption(value: unknown): boolean {
|
|
261
|
+
if (!value || typeof value !== 'object' || !('config' in value)) return false;
|
|
262
|
+
const config = (value as { config?: unknown }).config;
|
|
263
|
+
if (!config || typeof config !== 'object' || !('disallowInterruption' in config)) return false;
|
|
264
|
+
return (config as { disallowInterruption?: unknown }).disallowInterruption === true;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function readHandlers(value: unknown): unknown {
|
|
268
|
+
if (!value || typeof value !== 'object' || !('handlers' in value)) return undefined;
|
|
269
|
+
return (value as { handlers?: unknown }).handlers;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** A mounted gesture-handler tag. `-1` means "not attached yet". */
|
|
273
|
+
function handlerTagOf(value: unknown): number | undefined {
|
|
274
|
+
if (!value || typeof value !== 'object' || !('handlerTag' in value)) return undefined;
|
|
275
|
+
const tag = (value as { handlerTag?: unknown }).handlerTag;
|
|
276
|
+
if (typeof tag !== 'number' || !Number.isFinite(tag) || tag < 0) return undefined;
|
|
277
|
+
return tag;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function deviceEventEmitter(): DeviceEventEmitterLike | null {
|
|
281
|
+
try {
|
|
282
|
+
// Lazy so a tap that is never cancelled does not load React Native's emitter
|
|
283
|
+
// into unit tests that only exercise the monitors.
|
|
284
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
285
|
+
const reactNative = require('react-native') as {
|
|
286
|
+
DeviceEventEmitter?: DeviceEventEmitterLike;
|
|
287
|
+
};
|
|
288
|
+
const emitter = reactNative.DeviceEventEmitter;
|
|
289
|
+
if (!emitter || typeof emitter.addListener !== 'function') return null;
|
|
290
|
+
return emitter;
|
|
291
|
+
} catch {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function asPayload(event: unknown): GestureStatePayload | null {
|
|
297
|
+
if (!event || typeof event !== 'object') return null;
|
|
298
|
+
const raw = event as Record<string, unknown>;
|
|
299
|
+
const body =
|
|
300
|
+
raw.nativeEvent && typeof raw.nativeEvent === 'object'
|
|
301
|
+
? (raw.nativeEvent as Record<string, unknown>)
|
|
302
|
+
: raw;
|
|
303
|
+
if (typeof body.handlerTag !== 'number' || typeof body.state !== 'number') return null;
|
|
304
|
+
return {
|
|
305
|
+
handlerTag: body.handlerTag,
|
|
306
|
+
state: body.state,
|
|
307
|
+
...(typeof body.oldState === 'number' ? { oldState: body.oldState } : {}),
|
|
308
|
+
...(typeof body.pointerInside === 'boolean' ? { pointerInside: body.pointerInside } : {}),
|
|
309
|
+
...(typeof body.translationX === 'number' ? { translationX: body.translationX } : {}),
|
|
310
|
+
...(typeof body.translationY === 'number' ? { translationY: body.translationY } : {}),
|
|
311
|
+
...(typeof body.absoluteX === 'number' ? { absoluteX: body.absoluteX } : {}),
|
|
312
|
+
...(typeof body.absoluteY === 'number' ? { absoluteY: body.absoluteY } : {}),
|
|
313
|
+
hasDuration: 'duration' in body && body.duration != null,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function isTerminal(state: number): boolean {
|
|
318
|
+
return (
|
|
319
|
+
state === GESTURE_HANDLER_STATE.FAILED ||
|
|
320
|
+
state === GESTURE_HANDLER_STATE.CANCELLED ||
|
|
321
|
+
state === GESTURE_HANDLER_STATE.END
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function isCompletedTap(touch: PendingTouch, payload: GestureStatePayload): boolean {
|
|
326
|
+
if (payload.state !== GESTURE_HANDLER_STATE.END) return false;
|
|
327
|
+
if (payload.oldState !== GESTURE_HANDLER_STATE.ACTIVE) return false;
|
|
328
|
+
if (payload.pointerInside === false) return false;
|
|
329
|
+
if (payload.hasDuration) return false;
|
|
330
|
+
if (
|
|
331
|
+
payload.translationX !== undefined &&
|
|
332
|
+
payload.translationY !== undefined &&
|
|
333
|
+
!withinTapSlop(
|
|
334
|
+
{ pageX: 0, pageY: 0 },
|
|
335
|
+
{ pageX: payload.translationX, pageY: payload.translationY },
|
|
336
|
+
)
|
|
337
|
+
) {
|
|
338
|
+
return false;
|
|
339
|
+
}
|
|
340
|
+
if (payload.absoluteX !== undefined && payload.absoluteY !== undefined) {
|
|
341
|
+
if (
|
|
342
|
+
!withinTapSlop(
|
|
343
|
+
{ pageX: touch.pageX, pageY: touch.pageY },
|
|
344
|
+
{ pageX: payload.absoluteX, pageY: payload.absoluteY },
|
|
345
|
+
)
|
|
346
|
+
) {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function endPoint(payload: GestureStatePayload): TouchPoint | null {
|
|
354
|
+
if (payload.absoluteX === undefined || payload.absoluteY === undefined) return null;
|
|
355
|
+
return { pageX: payload.absoluteX, pageY: payload.absoluteY };
|
|
356
|
+
}
|
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
setCachedFiberAnchor,
|
|
16
16
|
} from './fiberAnchorCache';
|
|
17
17
|
|
|
18
|
+
export { subscribeAnchorScreenChange } from './fiberAnchorCache';
|
|
19
|
+
|
|
18
20
|
export type AnchorResolution =
|
|
19
21
|
| { status: 'found'; host: AnchorHost; fiber?: AnyFiber }
|
|
20
22
|
| { status: 'ambiguous' }
|