@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
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { FlowSessionEngine } from '@userflow-tech/core';
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
import {
|
|
3
|
+
decodeMobileAutoData,
|
|
4
|
+
type EndFlowReasonInput,
|
|
5
|
+
type FlowSessionFragment,
|
|
6
|
+
type FlowStepFragment,
|
|
7
|
+
type ThemeFragment,
|
|
7
8
|
} from '@userflow-tech/protocol';
|
|
8
9
|
import * as React from 'react';
|
|
9
10
|
import {
|
|
@@ -20,6 +21,7 @@ import {
|
|
|
20
21
|
import { anchorRegistry } from '../anchors/AnchorRegistry';
|
|
21
22
|
import { autoAnchorRegistry } from '../anchors/AutoAnchorRegistry';
|
|
22
23
|
import { addAnimationFrameCallback, removeAnimationFrameCallback } from '../anchors/animationFrame';
|
|
24
|
+
import { isAppActive, subscribeAppActive } from '../anchors/appActive';
|
|
23
25
|
import { fiberScanRoot } from '../anchors/fiber/FiberScanRoot';
|
|
24
26
|
import type { AnyFiber } from '../anchors/fiber/fiberInternals';
|
|
25
27
|
import { clipToScrollViewports } from '../anchors/fiber/visibleRect';
|
|
@@ -27,6 +29,7 @@ import {
|
|
|
27
29
|
clearFiberAnchorCache,
|
|
28
30
|
invalidateFiberAnchor,
|
|
29
31
|
resolveAnchorHost,
|
|
32
|
+
subscribeAnchorScreenChange,
|
|
30
33
|
} from '../anchors/resolveAnchorHost';
|
|
31
34
|
import { resolveAutoAnchorHost } from '../anchors/resolveAutoAnchorHost';
|
|
32
35
|
import { resolveManualAnchorId } from '../anchors/resolveManualAnchorId';
|
|
@@ -42,6 +45,7 @@ import { MADE_WITH_USERFLOW_GAP, MadeWithUserflow } from './MadeWithUserflow';
|
|
|
42
45
|
import { ProgressBar } from './ProgressBar';
|
|
43
46
|
import { ABSOLUTE_FILL } from './absoluteFill';
|
|
44
47
|
import { useCardTransitionPhase } from './cardTransition';
|
|
48
|
+
import { stepTrapsHostAccessibility } from './hostAccessibilityTrap';
|
|
45
49
|
import { parseBackdropOpacity } from './parseBackdropOpacity';
|
|
46
50
|
import { resolveStepProgress } from './progress';
|
|
47
51
|
import { resolveCloseButtonColor } from './resolveCloseButtonColor';
|
|
@@ -77,6 +81,7 @@ import {
|
|
|
77
81
|
tooltipSideGap,
|
|
78
82
|
tooltipVerticalGap,
|
|
79
83
|
} from './tooltip/resolveSideCardWidth';
|
|
84
|
+
import { TargetSearchSchedule } from './tooltip/targetSearchSchedule';
|
|
80
85
|
|
|
81
86
|
export interface TooltipSurfaceProps {
|
|
82
87
|
engine: FlowSessionEngine;
|
|
@@ -94,10 +99,6 @@ export interface TooltipSurfaceProps {
|
|
|
94
99
|
|
|
95
100
|
type MissingMode = 'waiting' | 'reported';
|
|
96
101
|
|
|
97
|
-
/** Interval for re-resolving a missing / remounted anchor (not for live measure). */
|
|
98
|
-
const RESOLVE_MS = 250;
|
|
99
|
-
/** Slower resolve poll after FLAG/ERROR report so a late-mounting target can recover. */
|
|
100
|
-
const RESOLVE_SLOW_MS = 1000;
|
|
101
102
|
/**
|
|
102
103
|
* One-frame delay before the first resolve after a step/reset so dialog
|
|
103
104
|
* teardown cannot arm the missing-target timer with a 0-size measure.
|
|
@@ -162,6 +163,11 @@ export function TooltipSurface({
|
|
|
162
163
|
const selector = currentStep.selector;
|
|
163
164
|
const isAuto = (selector?.type as string | null) === 'AUTO';
|
|
164
165
|
const autoData = selector?.autoData ?? null;
|
|
166
|
+
// Decoded once per step rather than on every search.
|
|
167
|
+
const decodedAutoData = React.useMemo(
|
|
168
|
+
() => (isAuto ? decodeMobileAutoData(autoData) : null),
|
|
169
|
+
[isAuto, autoData],
|
|
170
|
+
);
|
|
165
171
|
const anchorId = isAuto ? null : resolveManualAnchorId(selector?.css);
|
|
166
172
|
const targetKey = isAuto ? `auto:${autoData ?? ''}` : (anchorId ?? '');
|
|
167
173
|
const highlight = currentStep.backdrop ? resolveHighlightChrome(theme, backdropPadding) : null;
|
|
@@ -191,7 +197,6 @@ export function TooltipSurface({
|
|
|
191
197
|
desiredPlacement ?? 'BELOW',
|
|
192
198
|
);
|
|
193
199
|
const [missingMode, setMissingMode] = React.useState<MissingMode>('waiting');
|
|
194
|
-
const [registryTick, setRegistryTick] = React.useState(0);
|
|
195
200
|
/** True after at least one *failed* measure for the current step/anchor. */
|
|
196
201
|
const [measureAttempted, setMeasureAttempted] = React.useState(false);
|
|
197
202
|
/**
|
|
@@ -272,18 +277,6 @@ export function TooltipSurface({
|
|
|
272
277
|
};
|
|
273
278
|
}, []);
|
|
274
279
|
|
|
275
|
-
React.useEffect(() => {
|
|
276
|
-
const bump = () => setRegistryTick((n) => n + 1);
|
|
277
|
-
const unsubRegistry = anchorRegistry.subscribe(bump);
|
|
278
|
-
const unsubAuto = autoAnchorRegistry.subscribe(bump);
|
|
279
|
-
const unsubScan = fiberScanRoot.subscribe(bump);
|
|
280
|
-
return () => {
|
|
281
|
-
unsubRegistry();
|
|
282
|
-
unsubAuto();
|
|
283
|
-
unsubScan();
|
|
284
|
-
};
|
|
285
|
-
}, []);
|
|
286
|
-
|
|
287
280
|
React.useEffect(() => {
|
|
288
281
|
reportedRef.current = false;
|
|
289
282
|
scrollAttemptsRef.current = 0;
|
|
@@ -426,14 +419,14 @@ export function TooltipSurface({
|
|
|
426
419
|
);
|
|
427
420
|
|
|
428
421
|
/**
|
|
429
|
-
* Resolve the anchor host (registry / fiber scan).
|
|
430
|
-
*
|
|
422
|
+
* Resolve the anchor host (registry / fiber scan). Driven by
|
|
423
|
+
* {@link TargetSearchSchedule} — intentionally NOT on every animation frame.
|
|
431
424
|
*/
|
|
432
425
|
const resolveTarget = React.useCallback(() => {
|
|
433
426
|
if (!mountedRef.current) return;
|
|
434
427
|
|
|
435
428
|
const resolution = isAuto
|
|
436
|
-
? resolveAutoAnchorHost(
|
|
429
|
+
? resolveAutoAnchorHost(decodedAutoData)
|
|
437
430
|
: anchorId
|
|
438
431
|
? resolveAnchorHost(anchorId)
|
|
439
432
|
: null;
|
|
@@ -474,7 +467,7 @@ export function TooltipSurface({
|
|
|
474
467
|
maybeScrollIntoView(rect);
|
|
475
468
|
applyTarget(rect);
|
|
476
469
|
});
|
|
477
|
-
}, [anchorId, applyTarget,
|
|
470
|
+
}, [anchorId, applyTarget, decodedAutoData, dropHost, isAuto, maybeScrollIntoView]);
|
|
478
471
|
|
|
479
472
|
/**
|
|
480
473
|
* Live re-measure of an already-resolved host. Driven by the shared rAF hub
|
|
@@ -509,45 +502,60 @@ export function TooltipSurface({
|
|
|
509
502
|
});
|
|
510
503
|
}, [applyTarget, dropHost, maybeScrollIntoView]);
|
|
511
504
|
|
|
505
|
+
const resolveTargetRef = React.useRef(resolveTarget);
|
|
506
|
+
resolveTargetRef.current = resolveTarget;
|
|
507
|
+
const searchRef = React.useRef<TargetSearchSchedule | null>(null);
|
|
508
|
+
const searchAllowsRecovery =
|
|
509
|
+
behavior === 'FLAG' || behavior === 'ERROR' || (behavior === 'END' && session.draftMode);
|
|
510
|
+
const searchStopped = missingMode === 'reported' && !searchAllowsRecovery;
|
|
511
|
+
|
|
512
|
+
// Target search: steady poll while anchored, backoff while missing, nothing
|
|
513
|
+
// while the app is in the background. Live measurement is on rAF. A new step
|
|
514
|
+
// or target restarts it right away; a same-step restart (`resetNonce`) goes
|
|
515
|
+
// through `resolveArmed`, after the first-resolve delay.
|
|
512
516
|
React.useEffect(() => {
|
|
513
|
-
if (!resolveArmed)
|
|
514
|
-
resolveTarget();
|
|
515
|
-
}, [resolveTarget, registryTick, resolveArmed]);
|
|
516
|
-
|
|
517
|
-
// Resolve poll: recover missing / remounted anchors. Live measurement is on rAF.
|
|
518
|
-
React.useEffect(() => {
|
|
519
|
-
if (!resolveArmed) {
|
|
517
|
+
if (!resolveArmed || searchStopped) {
|
|
520
518
|
return;
|
|
521
519
|
}
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
520
|
+
const search = new TargetSearchSchedule(() => resolveTargetRef.current());
|
|
521
|
+
searchRef.current = search;
|
|
522
|
+
search.setMode(targetRef.current ? 'anchored' : 'missing');
|
|
523
|
+
|
|
524
|
+
const trigger = () => search.trigger();
|
|
525
|
+
const unsubscribers = [
|
|
526
|
+
anchorRegistry.subscribe(trigger),
|
|
527
|
+
autoAnchorRegistry.subscribe(trigger),
|
|
528
|
+
fiberScanRoot.subscribe(trigger),
|
|
529
|
+
subscribeAnchorScreenChange(() => search.rearm()),
|
|
530
|
+
subscribeAppActive((active) => (active ? search.resume() : search.pause())),
|
|
531
|
+
];
|
|
532
|
+
if (!isAppActive()) search.pause();
|
|
533
|
+
search.runNow();
|
|
534
|
+
|
|
535
|
+
const dimSub = Dimensions.addEventListener('change', () => search.runNow());
|
|
534
536
|
const onKeyboardShow = (event: { endCoordinates?: { height?: number } }) => {
|
|
535
537
|
setKeyboardBottomInset(Math.max(0, event.endCoordinates?.height ?? 0));
|
|
536
|
-
|
|
538
|
+
search.runNow();
|
|
537
539
|
};
|
|
538
540
|
const onKeyboardHide = () => {
|
|
539
541
|
setKeyboardBottomInset(0);
|
|
540
|
-
|
|
542
|
+
search.runNow();
|
|
541
543
|
};
|
|
542
544
|
const kbShow = Keyboard.addListener('keyboardDidShow', onKeyboardShow);
|
|
543
545
|
const kbHide = Keyboard.addListener('keyboardDidHide', onKeyboardHide);
|
|
544
546
|
return () => {
|
|
545
|
-
|
|
547
|
+
for (const unsubscribe of unsubscribers) unsubscribe();
|
|
546
548
|
dimSub.remove();
|
|
547
549
|
kbShow.remove();
|
|
548
550
|
kbHide.remove();
|
|
551
|
+
search.dispose();
|
|
552
|
+
if (searchRef.current === search) searchRef.current = null;
|
|
549
553
|
};
|
|
550
|
-
}, [
|
|
554
|
+
}, [resolveArmed, searchStopped, currentStep.id, targetKey]);
|
|
555
|
+
|
|
556
|
+
React.useEffect(() => {
|
|
557
|
+
searchRef.current?.setMode(target ? 'anchored' : 'missing');
|
|
558
|
+
}, [target]);
|
|
551
559
|
|
|
552
560
|
// rAF tracking while a host is resolved (and we are not paused after report).
|
|
553
561
|
React.useEffect(() => {
|
|
@@ -864,6 +872,9 @@ export function TooltipSurface({
|
|
|
864
872
|
|
|
865
873
|
const showBranding = session.flow.company.flowBranding;
|
|
866
874
|
const contentPadding = chrome.contentPadding;
|
|
875
|
+
// Modal only while the host is trapped too. Otherwise VoiceOver would skip
|
|
876
|
+
// the host app, including the target this step asks the user to press.
|
|
877
|
+
const cardIsModal = stepTrapsHostAccessibility(currentStep);
|
|
867
878
|
|
|
868
879
|
// Still pending the first measure attempt — only the blocker shows.
|
|
869
880
|
if (isMissing && missingMode === 'waiting' && !measureAttempted) {
|
|
@@ -877,7 +888,7 @@ export function TooltipSurface({
|
|
|
877
888
|
<View style={[styles.center, centerPaddingStyle]} pointerEvents="box-none">
|
|
878
889
|
<CardFade
|
|
879
890
|
testID="userflow-tooltip-waiting"
|
|
880
|
-
accessibilityViewIsModal
|
|
891
|
+
accessibilityViewIsModal={cardIsModal}
|
|
881
892
|
pointerEvents="auto"
|
|
882
893
|
style={[
|
|
883
894
|
chrome.shadowStyle,
|
|
@@ -990,7 +1001,7 @@ export function TooltipSurface({
|
|
|
990
1001
|
<View style={[styles.center, centerPaddingStyle]} pointerEvents="box-none">
|
|
991
1002
|
<CardFade
|
|
992
1003
|
testID="userflow-tooltip-card"
|
|
993
|
-
accessibilityViewIsModal
|
|
1004
|
+
accessibilityViewIsModal={cardIsModal}
|
|
994
1005
|
pointerEvents="auto"
|
|
995
1006
|
onLayout={onCardLayout}
|
|
996
1007
|
style={[
|
|
@@ -1032,6 +1043,7 @@ export function TooltipSurface({
|
|
|
1032
1043
|
onLayout={onCardLayout}
|
|
1033
1044
|
maxHeight={layout.maxCardHeight}
|
|
1034
1045
|
showNotch={layout.showNotch}
|
|
1046
|
+
accessibilityViewIsModal={cardIsModal}
|
|
1035
1047
|
style={{
|
|
1036
1048
|
position: 'absolute',
|
|
1037
1049
|
left: layout.cardPosition.x,
|
|
@@ -3,10 +3,12 @@ import { BannerContentLayout } from '@userflow-tech/protocol';
|
|
|
3
3
|
import type { ViewStyle } from 'react-native';
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
|
+
BANNER_CLOSE_SLOT_SIZE,
|
|
6
7
|
BANNER_INNER_COLUMN_GAP,
|
|
7
8
|
BANNER_INNER_MARGIN_HORIZONTAL,
|
|
8
9
|
BANNER_INNER_ROW_GAP,
|
|
9
10
|
BANNER_OVERLAY_SCREEN_INSET_HORIZONTAL,
|
|
11
|
+
BANNER_ROOT_PADDING,
|
|
10
12
|
} from './bannerBarTokens';
|
|
11
13
|
import type { EdgeInsets } from './safeArea';
|
|
12
14
|
|
|
@@ -16,13 +18,50 @@ function optionalMargin(value: number | null | undefined): number | undefined {
|
|
|
16
18
|
return value == null ? undefined : value;
|
|
17
19
|
}
|
|
18
20
|
|
|
21
|
+
/** Fixed horizontal chrome inside the embed width (padding, close/spacer slots, inner margins). */
|
|
22
|
+
export function bannerHorizontalChromeWidth(): number {
|
|
23
|
+
return BANNER_ROOT_PADDING * 2 + BANNER_CLOSE_SLOT_SIZE * 2 + BANNER_INNER_MARGIN_HORIZONTAL * 2;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Outer bar cap — max embed width only (max content width limits the inner row on web). */
|
|
27
|
+
export function resolveBannerFrameMaxWidth(banner: BannerFragment): number | null {
|
|
28
|
+
return banner.maxWidth;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function bannerHasMaxContentWidth(banner: BannerFragment): boolean {
|
|
32
|
+
return banner.maxContentWidth != null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Cap for the native inner row and WebView HTML `.userflowjs-banner-inner` only.
|
|
37
|
+
* Returns null when max content width is unset. May be 0 when embed width is narrower than chrome.
|
|
38
|
+
*/
|
|
39
|
+
export function resolveBannerInnerMaxWidth(banner: BannerFragment): number | null {
|
|
40
|
+
if (!bannerHasMaxContentWidth(banner)) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
let cap = banner.maxContentWidth!;
|
|
44
|
+
if (banner.maxWidth != null) {
|
|
45
|
+
const embedInnerCap = Math.max(0, banner.maxWidth - bannerHorizontalChromeWidth());
|
|
46
|
+
cap = Math.min(cap, embedInnerCap);
|
|
47
|
+
}
|
|
48
|
+
return cap;
|
|
49
|
+
}
|
|
50
|
+
|
|
19
51
|
/** Outer frame: max embed width and builder outer margin (web `userflowjs-banner-frame`). */
|
|
20
52
|
export function bannerFrameStyle(banner: BannerFragment): ViewStyle {
|
|
21
|
-
const
|
|
53
|
+
const frameMaxWidth = resolveBannerFrameMaxWidth(banner);
|
|
54
|
+
const hasFrameCap = frameMaxWidth != null;
|
|
22
55
|
|
|
23
56
|
return {
|
|
24
|
-
alignSelf:
|
|
25
|
-
...(
|
|
57
|
+
alignSelf: hasFrameCap ? 'center' : 'stretch',
|
|
58
|
+
...(hasFrameCap
|
|
59
|
+
? {
|
|
60
|
+
maxWidth: frameMaxWidth!,
|
|
61
|
+
width: '100%' as const,
|
|
62
|
+
overflow: 'hidden' as const,
|
|
63
|
+
}
|
|
64
|
+
: null),
|
|
26
65
|
marginTop: optionalMargin(banner.marginTop),
|
|
27
66
|
marginRight: optionalMargin(banner.marginRight),
|
|
28
67
|
marginBottom: optionalMargin(banner.marginBottom),
|
|
@@ -56,12 +95,45 @@ export function bannerInnerJustifyContent(
|
|
|
56
95
|
}
|
|
57
96
|
}
|
|
58
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Push-layout chrome: safe-area pad + bar background on the same node as the
|
|
100
|
+
* fade so an animated banner does not leave a visible status-bar strip at opacity 0.
|
|
101
|
+
*/
|
|
102
|
+
export function bannerPushLayoutChrome(
|
|
103
|
+
edge: 'top' | 'bottom',
|
|
104
|
+
insets: EdgeInsets,
|
|
105
|
+
backgroundColor: string,
|
|
106
|
+
): ViewStyle {
|
|
107
|
+
return {
|
|
108
|
+
backgroundColor,
|
|
109
|
+
paddingTop: edge === 'top' ? insets.top : 0,
|
|
110
|
+
paddingBottom: edge === 'bottom' ? insets.bottom : 0,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Matches web `.userflowjs-banner-inner { flex(1000000) }` vs start-spacer `flex(1)`. */
|
|
115
|
+
const BANNER_INNER_FLEX_GROW = 1_000_000;
|
|
116
|
+
|
|
117
|
+
/** Painted bar (`userflow-banner`) — fill the frame so max embed width clips chrome, not just margins. */
|
|
118
|
+
export function bannerBarRootStyle(banner: BannerFragment): ViewStyle {
|
|
119
|
+
if (resolveBannerFrameMaxWidth(banner) == null) {
|
|
120
|
+
return {};
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
width: '100%',
|
|
124
|
+
maxWidth: '100%',
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
59
128
|
export function bannerInnerStyle(banner: BannerFragment): ViewStyle {
|
|
60
|
-
const hasMaxContentWidth = banner
|
|
129
|
+
const hasMaxContentWidth = bannerHasMaxContentWidth(banner);
|
|
130
|
+
const innerMaxWidth = resolveBannerInnerMaxWidth(banner);
|
|
61
131
|
return {
|
|
62
|
-
|
|
132
|
+
// Web `.userflowjs-banner-inner { flex(1000000) }` — grow with shrink so text wraps and close stays visible.
|
|
133
|
+
flexGrow: BANNER_INNER_FLEX_GROW,
|
|
63
134
|
flexShrink: 1,
|
|
64
|
-
flexBasis: 0
|
|
135
|
+
// `auto` avoids the old `flexBasis: 0` + inner `width: 100%` sliver; `minWidth: 0` lets shrink wrap text.
|
|
136
|
+
flexBasis: hasMaxContentWidth ? ('auto' as const) : 0,
|
|
65
137
|
flexDirection: 'row',
|
|
66
138
|
flexWrap: 'wrap',
|
|
67
139
|
alignItems: 'center',
|
|
@@ -69,6 +141,7 @@ export function bannerInnerStyle(banner: BannerFragment): ViewStyle {
|
|
|
69
141
|
rowGap: BANNER_INNER_ROW_GAP,
|
|
70
142
|
marginHorizontal: BANNER_INNER_MARGIN_HORIZONTAL,
|
|
71
143
|
justifyContent: bannerInnerJustifyContent(banner.contentLayout),
|
|
72
|
-
|
|
144
|
+
minWidth: hasMaxContentWidth ? 0 : undefined,
|
|
145
|
+
...(innerMaxWidth != null && innerMaxWidth > 0 ? { maxWidth: innerMaxWidth } : null),
|
|
73
146
|
};
|
|
74
147
|
}
|
|
@@ -23,14 +23,18 @@ import { getEvalJsWebView, type EvalJsWebViewHandle } from '../../evalJs/getWebV
|
|
|
23
23
|
import { injectEvalPendingIntoWebView } from '../../evalJs/injectEvalPending';
|
|
24
24
|
import type { EvalJsContext } from '../../evalJs/types';
|
|
25
25
|
import { useEvalJsWebViewInjection } from '../../evalJs/useEvalJsWebViewInjection';
|
|
26
|
+
import { usePushBannerHostInsetRegistration } from '../../runtime/pushBannerLayoutContext';
|
|
27
|
+
import { setBannerFlowSpacerHeight } from '../../runtime/bannerFlowSpacer';
|
|
28
|
+
import { warnBannerOnce } from '../../runtime/bannerPlacement';
|
|
26
29
|
import { getUserflowCore } from '../../runtime/userflowCore';
|
|
27
30
|
import { getCurrentScreenLocation, subscribeScreenLocation } from '../../screenTracker';
|
|
28
31
|
import {
|
|
29
32
|
bannerFrameStyle,
|
|
30
|
-
|
|
33
|
+
bannerPushLayoutChrome,
|
|
31
34
|
resolveBannerBarBorderRadius,
|
|
32
35
|
} from '../bannerBarLayout';
|
|
33
36
|
import { bannerOverlayShadowStyle } from '../bannerBarTokens';
|
|
37
|
+
import { resolveBannerBackgroundColor } from '../resolveBannerTheme';
|
|
34
38
|
import { useUserflowSafeAreaInsets } from '../safeArea';
|
|
35
39
|
|
|
36
40
|
import { collectBannerAutoRunEvalCodes } from './bannerAutoRunEvalJs';
|
|
@@ -39,6 +43,8 @@ import { buildCustomBannerBodyHtml } from './buildCustomBannerHtml';
|
|
|
39
43
|
|
|
40
44
|
export interface BannerWebViewBarProps {
|
|
41
45
|
session: BannerSessionFragment;
|
|
46
|
+
pushLayoutEdge?: 'top' | 'bottom' | null;
|
|
47
|
+
overlaySafeEdge?: 'top' | 'bottom' | null;
|
|
42
48
|
onActions: (actions: FlowActionFragment[]) => void | Promise<void>;
|
|
43
49
|
onDismiss: (endReason: EndFlowReasonInput) => void;
|
|
44
50
|
}
|
|
@@ -68,14 +74,27 @@ function useEvalJsScreen(): { name?: string; path?: string } {
|
|
|
68
74
|
|
|
69
75
|
export function BannerWebViewBar({
|
|
70
76
|
session,
|
|
77
|
+
pushLayoutEdge = null,
|
|
78
|
+
overlaySafeEdge = null,
|
|
71
79
|
onActions,
|
|
72
80
|
onDismiss,
|
|
73
81
|
}: BannerWebViewBarProps): React.ReactElement | null {
|
|
74
82
|
const banner = session.version.banner;
|
|
83
|
+
const overlay = banner?.overlay !== false;
|
|
84
|
+
const hostInsetEdge = banner && !overlay && pushLayoutEdge ? pushLayoutEdge : null;
|
|
85
|
+
const pushLayoutRegistrationEdge =
|
|
86
|
+
hostInsetEdge ?? (!overlay && overlaySafeEdge ? overlaySafeEdge : null);
|
|
87
|
+
const safeAreaEdge = hostInsetEdge ?? overlaySafeEdge ?? null;
|
|
88
|
+
const onPushChromeLayout = usePushBannerHostInsetRegistration(
|
|
89
|
+
pushLayoutRegistrationEdge,
|
|
90
|
+
session.id,
|
|
91
|
+
pushLayoutRegistrationEdge != null,
|
|
92
|
+
);
|
|
75
93
|
const WebView = getEvalJsWebView();
|
|
76
94
|
const webRef = React.useRef<EvalJsWebViewHandle>(null);
|
|
77
95
|
const [pageReady, setPageReady] = React.useState(false);
|
|
78
96
|
const [contentHeight, setContentHeight] = React.useState(56);
|
|
97
|
+
const [webLayoutHeight, setWebLayoutHeight] = React.useState<number | null>(null);
|
|
79
98
|
const colorScheme = useColorScheme();
|
|
80
99
|
const viewport = useWindowDimensions();
|
|
81
100
|
const insets = useUserflowSafeAreaInsets();
|
|
@@ -104,18 +123,18 @@ export function BannerWebViewBar({
|
|
|
104
123
|
const serverEndpoint = getUserflowCore().getServerEndpoint() ?? DEFAULT_SERVER_ENDPOINT;
|
|
105
124
|
|
|
106
125
|
const skipAutoRunEval = shouldSkipBannerAutoRunEval(session.id);
|
|
107
|
-
const initialContext = React.useRef(context);
|
|
108
126
|
const html = React.useMemo(() => {
|
|
109
127
|
const body = buildCustomBannerBodyHtml(session, lookupAttribute, serverEndpoint, {
|
|
110
128
|
skipAutoRunEval,
|
|
111
129
|
});
|
|
112
|
-
return buildBootstrapHtml(
|
|
113
|
-
}, [lookupAttribute, serverEndpoint, session, skipAutoRunEval]);
|
|
130
|
+
return buildBootstrapHtml(context, body);
|
|
131
|
+
}, [context, lookupAttribute, serverEndpoint, session, skipAutoRunEval]);
|
|
114
132
|
|
|
115
|
-
const
|
|
133
|
+
const fadeIn = banner?.animate === true && hostInsetEdge == null;
|
|
134
|
+
const opacity = React.useRef(new Animated.Value(fadeIn ? 0 : 1)).current;
|
|
116
135
|
|
|
117
136
|
React.useEffect(() => {
|
|
118
|
-
if (!
|
|
137
|
+
if (!fadeIn) {
|
|
119
138
|
opacity.setValue(1);
|
|
120
139
|
return;
|
|
121
140
|
}
|
|
@@ -129,13 +148,39 @@ export function BannerWebViewBar({
|
|
|
129
148
|
return () => {
|
|
130
149
|
animation.stop();
|
|
131
150
|
};
|
|
132
|
-
}, [
|
|
151
|
+
}, [fadeIn, opacity, session.id]);
|
|
133
152
|
|
|
134
153
|
React.useEffect(() => {
|
|
135
154
|
setPageReady(false);
|
|
136
155
|
setContentHeight(56);
|
|
156
|
+
setWebLayoutHeight(null);
|
|
137
157
|
}, [session.id, html]);
|
|
138
158
|
|
|
159
|
+
const reserveFlowSpace = !overlay && overlaySafeEdge != null;
|
|
160
|
+
React.useEffect(() => {
|
|
161
|
+
if (!reserveFlowSpace || !overlaySafeEdge) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
return () => {
|
|
165
|
+
setBannerFlowSpacerHeight(overlaySafeEdge, 0);
|
|
166
|
+
};
|
|
167
|
+
}, [overlaySafeEdge, reserveFlowSpace, session.id]);
|
|
168
|
+
|
|
169
|
+
React.useEffect(() => {
|
|
170
|
+
if (pushLayoutRegistrationEdge == null) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const timer = setTimeout(() => {
|
|
174
|
+
if (webLayoutHeight == null) {
|
|
175
|
+
warnBannerOnce(
|
|
176
|
+
`push-webview-layout:${session.id}`,
|
|
177
|
+
'Userflow: push-layout WebView banner did not report a layout height; check custom CSS / WebView logs',
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
}, 4000);
|
|
181
|
+
return () => clearTimeout(timer);
|
|
182
|
+
}, [pushLayoutRegistrationEdge, session.id, webLayoutHeight]);
|
|
183
|
+
|
|
139
184
|
React.useEffect(() => {
|
|
140
185
|
if (!pageReady || skipAutoRunEval || !banner) {
|
|
141
186
|
return;
|
|
@@ -177,17 +222,23 @@ export function BannerWebViewBar({
|
|
|
177
222
|
const maxHeight = Math.floor(viewport.height * 0.92);
|
|
178
223
|
const next = Math.min(Math.ceil(height), maxHeight);
|
|
179
224
|
setContentHeight((prev) => (Math.abs(next - prev) <= 1 ? prev : next));
|
|
225
|
+
setWebLayoutHeight(next);
|
|
180
226
|
}
|
|
181
227
|
return;
|
|
182
228
|
}
|
|
183
229
|
if (record.__userflow === 1 && record.type === 'bannerButton') {
|
|
184
230
|
const buttonId = record.buttonId;
|
|
185
231
|
if (typeof buttonId === 'string' && banner) {
|
|
186
|
-
const button =
|
|
232
|
+
const button =
|
|
233
|
+
banner.buttons.find((entry) => entry.id === buttonId) ??
|
|
234
|
+
banner.buttons.find((entry) => entry.cvid === buttonId);
|
|
187
235
|
if (button) {
|
|
188
236
|
void Promise.resolve(onActions(button.actions)).then(() => {
|
|
189
237
|
injectEvalPendingIntoWebView(webRef.current);
|
|
190
238
|
});
|
|
239
|
+
} else if (__DEV__) {
|
|
240
|
+
// eslint-disable-next-line no-console -- intentional development diagnostic
|
|
241
|
+
console.warn('[Userflow] banner button not found for id', buttonId);
|
|
191
242
|
}
|
|
192
243
|
}
|
|
193
244
|
return;
|
|
@@ -241,72 +292,113 @@ export function BannerWebViewBar({
|
|
|
241
292
|
return;
|
|
242
293
|
}
|
|
243
294
|
},
|
|
244
|
-
[
|
|
295
|
+
[
|
|
296
|
+
banner,
|
|
297
|
+
insets.bottom,
|
|
298
|
+
insets.top,
|
|
299
|
+
onActions,
|
|
300
|
+
onDismiss,
|
|
301
|
+
overlaySafeEdge,
|
|
302
|
+
reserveFlowSpace,
|
|
303
|
+
viewport.height,
|
|
304
|
+
],
|
|
245
305
|
);
|
|
246
306
|
|
|
247
307
|
if (!banner || !WebView) {
|
|
248
308
|
return null;
|
|
249
309
|
}
|
|
250
310
|
|
|
251
|
-
const overlay = banner.overlay !== false;
|
|
252
311
|
const frameStyle = bannerFrameStyle(banner);
|
|
253
|
-
const innerStyle = bannerInnerStyle(banner);
|
|
254
312
|
const overlayEdge = banner.embedMode === BannerEmbedMode.BODY_LAST ? 'bottom' : 'top';
|
|
255
313
|
const barRadius = resolveBannerBarBorderRadius(banner);
|
|
314
|
+
const backgroundColor = resolveBannerBackgroundColor(session.version.theme);
|
|
256
315
|
const webStyle: ViewStyle = {
|
|
257
316
|
width: '100%',
|
|
258
317
|
height: contentHeight,
|
|
259
|
-
backgroundColor
|
|
318
|
+
backgroundColor,
|
|
260
319
|
};
|
|
261
320
|
const debugging = typeof __DEV__ !== 'undefined' && __DEV__ ? true : undefined;
|
|
321
|
+
const insetChrome = safeAreaEdge
|
|
322
|
+
? bannerPushLayoutChrome(safeAreaEdge, insets, backgroundColor)
|
|
323
|
+
: null;
|
|
324
|
+
const insetChromeOutsideFade = insetChrome != null && fadeIn;
|
|
325
|
+
|
|
326
|
+
const webViewBar = (
|
|
327
|
+
<View
|
|
328
|
+
testID="userflow-banner-webview"
|
|
329
|
+
pointerEvents="auto"
|
|
330
|
+
style={[{ borderRadius: barRadius ?? 0, overflow: 'hidden' as const }]}
|
|
331
|
+
>
|
|
332
|
+
<WebView
|
|
333
|
+
key={session.id}
|
|
334
|
+
ref={webRef}
|
|
335
|
+
source={{ html, baseUrl: EVAL_JS_BASE_URL }}
|
|
336
|
+
style={webStyle}
|
|
337
|
+
javaScriptEnabled
|
|
338
|
+
domStorageEnabled
|
|
339
|
+
scrollEnabled={false}
|
|
340
|
+
bounces={false}
|
|
341
|
+
showsHorizontalScrollIndicator={false}
|
|
342
|
+
showsVerticalScrollIndicator={false}
|
|
343
|
+
originWhitelist={['*']}
|
|
344
|
+
setSupportMultipleWindows={false}
|
|
345
|
+
androidLayerType="hardware"
|
|
346
|
+
{...(debugging !== undefined ? { webviewDebuggingEnabled: debugging } : {})}
|
|
347
|
+
onMessage={onMessage}
|
|
348
|
+
onLoadEnd={() => {
|
|
349
|
+
setPageReady(true);
|
|
350
|
+
webRef.current?.injectJavaScript(
|
|
351
|
+
'window.__userflowBannerLayout&&window.__userflowBannerLayout();true;',
|
|
352
|
+
);
|
|
353
|
+
}}
|
|
354
|
+
onShouldStartLoadWithRequest={(request) => {
|
|
355
|
+
if (isBootstrapUrl(request.url)) {
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
openExternal(request.url);
|
|
359
|
+
return false;
|
|
360
|
+
}}
|
|
361
|
+
onOpenWindow={(event) => {
|
|
362
|
+
const url = event.nativeEvent.targetUrl;
|
|
363
|
+
if (url) {
|
|
364
|
+
openExternal(url);
|
|
365
|
+
}
|
|
366
|
+
}}
|
|
367
|
+
/>
|
|
368
|
+
</View>
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
const animatedBar = (
|
|
372
|
+
<Animated.View
|
|
373
|
+
style={[
|
|
374
|
+
{ opacity },
|
|
375
|
+
overlay ? bannerOverlayShadowStyle(overlayEdge) : null,
|
|
376
|
+
insetChromeOutsideFade ? null : insetChrome,
|
|
377
|
+
]}
|
|
378
|
+
>
|
|
379
|
+
{webViewBar}
|
|
380
|
+
</Animated.View>
|
|
381
|
+
);
|
|
262
382
|
|
|
263
383
|
return (
|
|
264
|
-
<View
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
ref={webRef}
|
|
275
|
-
source={{ html, baseUrl: EVAL_JS_BASE_URL }}
|
|
276
|
-
style={webStyle}
|
|
277
|
-
javaScriptEnabled
|
|
278
|
-
domStorageEnabled
|
|
279
|
-
scrollEnabled={false}
|
|
280
|
-
bounces={false}
|
|
281
|
-
showsHorizontalScrollIndicator={false}
|
|
282
|
-
showsVerticalScrollIndicator={false}
|
|
283
|
-
originWhitelist={['*']}
|
|
284
|
-
setSupportMultipleWindows={false}
|
|
285
|
-
androidLayerType="hardware"
|
|
286
|
-
{...(debugging !== undefined ? { webviewDebuggingEnabled: debugging } : {})}
|
|
287
|
-
onMessage={onMessage}
|
|
288
|
-
onLoadEnd={() => {
|
|
289
|
-
setPageReady(true);
|
|
290
|
-
webRef.current?.injectJavaScript(
|
|
291
|
-
'window.__userflowBannerLayout&&window.__userflowBannerLayout();true;',
|
|
292
|
-
);
|
|
293
|
-
}}
|
|
294
|
-
onShouldStartLoadWithRequest={(request) => {
|
|
295
|
-
if (isBootstrapUrl(request.url)) {
|
|
296
|
-
return true;
|
|
384
|
+
<View
|
|
385
|
+
testID="userflow-banner-frame"
|
|
386
|
+
style={frameStyle}
|
|
387
|
+
onLayout={
|
|
388
|
+
reserveFlowSpace && overlaySafeEdge
|
|
389
|
+
? (event) => {
|
|
390
|
+
onPushChromeLayout(event);
|
|
391
|
+
const h = event.nativeEvent.layout.height;
|
|
392
|
+
if (h > 0) {
|
|
393
|
+
setBannerFlowSpacerHeight(overlaySafeEdge, h);
|
|
297
394
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
}
|
|
306
|
-
}}
|
|
307
|
-
/>
|
|
308
|
-
</View>
|
|
309
|
-
</Animated.View>
|
|
395
|
+
}
|
|
396
|
+
: pushLayoutRegistrationEdge
|
|
397
|
+
? onPushChromeLayout
|
|
398
|
+
: undefined
|
|
399
|
+
}
|
|
400
|
+
>
|
|
401
|
+
{insetChromeOutsideFade ? <View style={insetChrome}>{animatedBar}</View> : animatedBar}
|
|
310
402
|
</View>
|
|
311
403
|
);
|
|
312
404
|
}
|