@wireai/activation 0.1.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/AGENTS.md +73 -0
- package/CHANGELOG.md +572 -0
- package/INTEGRATION_PROMPT.md +55 -0
- package/LICENSE +21 -0
- package/README.md +923 -0
- package/dist/CenteredModal-C3qQBHsA.d.mts +38 -0
- package/dist/CenteredModal-Cdgns6--.d.ts +38 -0
- package/dist/coachmarks/index.d.mts +352 -0
- package/dist/coachmarks/index.d.ts +352 -0
- package/dist/coachmarks/index.js +944 -0
- package/dist/coachmarks/index.js.map +1 -0
- package/dist/coachmarks/index.mjs +918 -0
- package/dist/coachmarks/index.mjs.map +1 -0
- package/dist/decision-CFvGY6nP.d.ts +228 -0
- package/dist/decision-Cl8OFYzu.d.mts +228 -0
- package/dist/index.d.mts +1390 -0
- package/dist/index.d.ts +1390 -0
- package/dist/index.js +3260 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3186 -0
- package/dist/index.mjs.map +1 -0
- package/dist/questionnaire/index.d.mts +249 -0
- package/dist/questionnaire/index.d.ts +249 -0
- package/dist/questionnaire/index.js +922 -0
- package/dist/questionnaire/index.js.map +1 -0
- package/dist/questionnaire/index.mjs +906 -0
- package/dist/questionnaire/index.mjs.map +1 -0
- package/dist/reviews/index.d.mts +213 -0
- package/dist/reviews/index.d.ts +213 -0
- package/dist/reviews/index.js +1001 -0
- package/dist/reviews/index.js.map +1 -0
- package/dist/reviews/index.mjs +971 -0
- package/dist/reviews/index.mjs.map +1 -0
- package/dist/showcase/index.d.mts +119 -0
- package/dist/showcase/index.d.ts +119 -0
- package/dist/showcase/index.js +660 -0
- package/dist/showcase/index.js.map +1 -0
- package/dist/showcase/index.mjs +651 -0
- package/dist/showcase/index.mjs.map +1 -0
- package/dist/types-BKfpdZzX.d.mts +84 -0
- package/dist/types-BKfpdZzX.d.ts +84 -0
- package/dist/types-CMuOexw0.d.mts +116 -0
- package/dist/types-CMuOexw0.d.ts +116 -0
- package/dist/types-GL_hQ0TN.d.mts +46 -0
- package/dist/types-GL_hQ0TN.d.ts +46 -0
- package/llms.txt +36 -0
- package/metro/index.d.ts +32 -0
- package/metro/index.js +83 -0
- package/package.json +171 -0
- package/src/DemoOnboarding.tsx +133 -0
- package/src/OnboardingFlow.tsx +504 -0
- package/src/WireOnboarding.tsx +248 -0
- package/src/analytics/analyticsEvent.ts +69 -0
- package/src/analytics/reportClientEvent.ts +119 -0
- package/src/analytics/sendPreview.ts +73 -0
- package/src/attribution/attribution.ts +46 -0
- package/src/cards/ChipSelectCard.tsx +265 -0
- package/src/cards/InterstitialCard.tsx +299 -0
- package/src/cards/NumberStepperCard.tsx +121 -0
- package/src/cards/SelectionCard.tsx +270 -0
- package/src/cards/StatusCard.tsx +170 -0
- package/src/cards/TextInputCard.tsx +147 -0
- package/src/cards/index.ts +23 -0
- package/src/coachmarks/CoachmarkOverlayHost.tsx +44 -0
- package/src/coachmarks/CoachmarkProvider.tsx +108 -0
- package/src/coachmarks/GestureHint.tsx +331 -0
- package/src/coachmarks/SpotlightOverlay.tsx +254 -0
- package/src/coachmarks/coachmarkAnchorRegistry.ts +70 -0
- package/src/coachmarks/coachmarkOverlayStore.ts +63 -0
- package/src/coachmarks/index.ts +62 -0
- package/src/coachmarks/runtime.ts +103 -0
- package/src/coachmarks/selectTourSteps.ts +45 -0
- package/src/coachmarks/types.ts +58 -0
- package/src/coachmarks/useCoachmarkAnchor.ts +49 -0
- package/src/coachmarks/useCoachmarkTour.ts +200 -0
- package/src/components/AnimatedSparkle.tsx +101 -0
- package/src/components/Button.tsx +129 -0
- package/src/components/CardHandoff.tsx +212 -0
- package/src/components/CardLayout.tsx +103 -0
- package/src/components/CenteredModal.tsx +184 -0
- package/src/components/CompletionView.tsx +186 -0
- package/src/components/DoneBlock.tsx +37 -0
- package/src/components/ErrorBlock.tsx +42 -0
- package/src/components/Illustration.tsx +36 -0
- package/src/components/LoadingBlock.tsx +182 -0
- package/src/components/LoadingScreen.tsx +35 -0
- package/src/components/OnboardingScaffold.tsx +105 -0
- package/src/components/StepProgress.tsx +85 -0
- package/src/components/loaderChrome.ts +28 -0
- package/src/config/onboardingFlag.ts +39 -0
- package/src/config/wireConfigFromEnv.ts +52 -0
- package/src/device/deviceContext.ts +158 -0
- package/src/features/WireFeaturesProvider.tsx +81 -0
- package/src/features/cache.ts +92 -0
- package/src/features/defaults.ts +67 -0
- package/src/features/fetchWireFeatures.ts +86 -0
- package/src/features/index.ts +39 -0
- package/src/features/types.ts +61 -0
- package/src/features/useWireFeatures.ts +57 -0
- package/src/identity/userIdentity.ts +100 -0
- package/src/illustrations/defaultIllustrations.tsx +99 -0
- package/src/index.ts +159 -0
- package/src/motion/centeredModalMotion.ts +45 -0
- package/src/motion/motionSpec.ts +133 -0
- package/src/motion/useReducedMotion.ts +43 -0
- package/src/questionnaire/QuestionnaireGate.tsx +235 -0
- package/src/questionnaire/decision.ts +63 -0
- package/src/questionnaire/index.ts +54 -0
- package/src/questionnaire/runtime.ts +20 -0
- package/src/questionnaire/transport.ts +83 -0
- package/src/questionnaire/types.ts +182 -0
- package/src/questionnaire/useQuestionnaireGate.ts +159 -0
- package/src/reviews/ReviewGate.tsx +316 -0
- package/src/reviews/ReviewModal.tsx +13 -0
- package/src/reviews/decision.ts +120 -0
- package/src/reviews/equality.ts +54 -0
- package/src/reviews/expo-store-review.d.ts +16 -0
- package/src/reviews/feedbackForm.ts +41 -0
- package/src/reviews/index.ts +78 -0
- package/src/reviews/presentation.ts +29 -0
- package/src/reviews/runtime.ts +45 -0
- package/src/reviews/storeReview.ts +62 -0
- package/src/reviews/transport.ts +89 -0
- package/src/reviews/types.ts +172 -0
- package/src/reviews/useReviewGate.ts +137 -0
- package/src/session/persistedSession.ts +143 -0
- package/src/session-analytics/index.ts +18 -0
- package/src/session-analytics/reportSessionStart.ts +144 -0
- package/src/session-analytics/useSessionStart.ts +114 -0
- package/src/showcase/FeatureShowcase.tsx +233 -0
- package/src/showcase/index.ts +19 -0
- package/src/showcase/selectShowcaseSlides.ts +45 -0
- package/src/showcase/showcaseColors.ts +91 -0
- package/src/showcase/types.ts +74 -0
- package/src/theme/ThemeContext.tsx +38 -0
- package/src/theme/defaultTheme.ts +70 -0
- package/src/theme/themeFromBrand.ts +93 -0
- package/src/theme/types.ts +88 -0
- package/src/theme/typography.ts +46 -0
- package/src/types.ts +215 -0
- package/src/utils/deriveAnswers.ts +43 -0
- package/src/utils/readProgress.ts +33 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/theme/defaultTheme.ts","../../src/theme/ThemeContext.tsx","../../src/theme/typography.ts","../../src/motion/motionSpec.ts","../../src/motion/useReducedMotion.ts","../../src/components/Button.tsx","../../src/reviews/decision.ts","../../src/reviews/feedbackForm.ts","../../src/motion/centeredModalMotion.ts","../../src/reviews/presentation.ts","../../src/components/CenteredModal.tsx","../../src/reviews/storeReview.ts","../../src/reviews/transport.ts","../../src/reviews/ReviewGate.tsx","../../src/coachmarks/runtime.ts","../../src/features/defaults.ts","../../src/features/cache.ts","../../src/features/fetchWireFeatures.ts","../../src/features/useWireFeatures.ts","../../src/features/WireFeaturesProvider.tsx","../../src/reviews/equality.ts","../../src/reviews/runtime.ts","../../src/reviews/useReviewGate.ts"],"names":["jsx","React","useRef","Animated","useState","useEffect","useCallback","styles","Pressable","StyleSheet","useMemo","jsxs","Text","createContext","useContext","useStableValue","testing"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * defaultOnboardingTheme — a neutral, light, brand-agnostic baseline. Consumers\n * pass a `Partial<OnboardingTheme>` that is deep-merged over this (see\n * `mergeTheme`), so they only override what differs from neutral.\n */\nimport type { OnboardingTheme } from \"./types\";\n\nexport const defaultOnboardingTheme: OnboardingTheme = {\n colors: {\n background: \"#FFFFFF\",\n surface: \"#F7F7F8\",\n text: \"#101114\",\n textMuted: \"#6B7280\",\n primary: \"#111111\",\n onPrimary: \"#FFFFFF\",\n primarySoft: \"rgba(17,17,17,0.06)\",\n border: \"#E4E4E7\",\n borderStrong: \"#C7C7CC\",\n disabled: \"#EBEBEB\",\n success: \"#16A34A\",\n warning: \"#D97706\",\n error: \"#DC2626\",\n info: \"#2563EB\",\n },\n fonts: {\n regular: undefined,\n medium: undefined,\n bold: undefined,\n baseSize: 16,\n scale: 1,\n },\n radius: {\n sm: 8,\n md: 12,\n lg: 16,\n full: 9999,\n },\n spacing: {\n xs: 4,\n sm: 8,\n md: 16,\n lg: 24,\n xl: 32,\n },\n // Circled (pill) CTA by default; an app can pass `button: { radius: 12 }` for\n // a rounded rectangle, or override height/paddingX to match its design system.\n button: {\n radius: 9999,\n height: 52,\n paddingX: 24,\n },\n};\n\n/**\n * Deep-merge a partial theme over the default. Two levels deep is enough — the\n * theme is intentionally shallow (colors/fonts/radius/spacing), so we merge each\n * known sub-object and leave scalars to overwrite.\n */\nexport const mergeTheme = (\n override?: Partial<OnboardingTheme>,\n): OnboardingTheme => {\n if (!override) return defaultOnboardingTheme;\n return {\n colors: { ...defaultOnboardingTheme.colors, ...override.colors },\n fonts: { ...defaultOnboardingTheme.fonts, ...override.fonts },\n radius: { ...defaultOnboardingTheme.radius, ...override.radius },\n spacing: { ...defaultOnboardingTheme.spacing, ...override.spacing },\n button: { ...defaultOnboardingTheme.button, ...override.button },\n };\n};\n","/**\n * ThemeContext — provides the resolved OnboardingTheme to every shell element and\n * themed card via `useOnboardingTheme()`. WHY a context rather than prop-drilling:\n * the cards are mounted by the SDK's ComponentRenderer (not by us), so we can't\n * pass them props directly — they must read the theme from context.\n */\nimport React, { createContext, useContext, useMemo } from \"react\";\nimport { mergeTheme } from \"./defaultTheme\";\nimport type { OnboardingTheme } from \"./types\";\n\nconst OnboardingThemeContext = createContext<OnboardingTheme | null>(null);\n\nexport type OnboardingThemeProviderProps = {\n /** Partial overrides merged over the neutral default theme. */\n theme?: Partial<OnboardingTheme>;\n children: React.ReactNode;\n};\n\nexport const OnboardingThemeProvider: React.FC<OnboardingThemeProviderProps> = ({\n theme,\n children,\n}) => {\n const resolved = useMemo(() => mergeTheme(theme), [theme]);\n return (\n <OnboardingThemeContext.Provider value={resolved}>\n {children}\n </OnboardingThemeContext.Provider>\n );\n};\n\n/**\n * Read the active theme. Falls back to the default theme when used outside a\n * provider so a stray card never crashes — it just renders neutral.\n */\nexport const useOnboardingTheme = (): OnboardingTheme => {\n const ctx = useContext(OnboardingThemeContext);\n return ctx ?? mergeTheme();\n};\n","/**\n * typography — derive concrete RN text styles from the theme's font config, so\n * every label/title/caption in the kit scales from one `baseSize` + `scale` and\n * picks the right family. Cards call these instead of hardcoding fontSize/weight.\n */\nimport type { TextStyle } from \"react-native\";\nimport type { OnboardingFonts } from \"./types\";\n\nconst round = (n: number) => Math.round(n);\n\n/** Body text — the reference size. */\nexport const bodyStyle = (f: OnboardingFonts): TextStyle => ({\n fontFamily: f.regular,\n fontSize: round(f.baseSize * f.scale),\n lineHeight: round(f.baseSize * f.scale * 1.5),\n});\n\n/** Slightly smaller secondary/hint text. */\nexport const captionStyle = (f: OnboardingFonts): TextStyle => ({\n fontFamily: f.regular,\n fontSize: round(f.baseSize * 0.8125 * f.scale),\n lineHeight: round(f.baseSize * 0.8125 * f.scale * 1.5),\n});\n\n/** Emphasised label (e.g. a card question). */\nexport const labelStyle = (f: OnboardingFonts): TextStyle => ({\n fontFamily: f.medium ?? f.bold,\n fontSize: round(f.baseSize * f.scale),\n lineHeight: round(f.baseSize * f.scale * 1.4),\n fontWeight: f.medium ? undefined : (\"600\" as const),\n});\n\n/** Heading (status title, done block). */\nexport const headingStyle = (f: OnboardingFonts): TextStyle => ({\n fontFamily: f.bold,\n fontSize: round(f.baseSize * 1.25 * f.scale),\n lineHeight: round(f.baseSize * 1.25 * f.scale * 1.3),\n fontWeight: f.bold ? undefined : (\"700\" as const),\n});\n\n/** Button label. */\nexport const buttonLabelStyle = (f: OnboardingFonts): TextStyle => ({\n fontFamily: f.bold ?? f.medium,\n fontSize: round(f.baseSize * f.scale),\n fontWeight: f.bold ? undefined : (\"600\" as const),\n});\n","/**\n * motionSpec — the single source of truth for every animation value in the kit,\n * extracted 1:1 from the Wire Activation card-interactions design canvas\n * (see design-refs/EXTRACTED-SPEC.md for the row-by-row mapping).\n *\n * Pure constants + pure helpers only — NO react-native import — so the spec is\n * unit-testable under node:test and components can't drift from the design\n * without a red test.\n */\n\n/** The design system's \"ease-wire\" curve: cubic-bezier(0.16, 1, 0.3, 1). */\nexport const WIRE_BEZIER = [0.16, 1, 0.3, 1] as const;\n\n/** Design-system duration tokens (colors_and_type.css --duration-*). */\nexport const DURATION_FAST_MS = 150;\nexport const DURATION_BASE_MS = 300;\nexport const DURATION_SLOW_MS = 500;\n\n/** Reduce-motion fallback: a short fade to the final frame, never a frozen mid-state. */\nexport const REDUCED_FADE_MS = 120;\n\n// ── Press feedback (spec rows 1, 4, 20) ─────────────────────────────────────\nexport const ROW_PRESS_SCALE = 0.975;\nexport const CHIP_PRESS_SCALE = 0.94;\nexport const BUTTON_PRESS_SCALE = 0.975;\n/** Reduce-motion press feedback: opacity dip instead of scale. */\nexport const REDUCED_PRESS_OPACITY = 0.85;\nexport const PRESS_SPRING = { friction: 8, tension: 220 } as const;\nexport const BUTTON_PRESS_SPRING = { friction: 8, tension: 240 } as const;\n\n// ── Selection state changes (spec rows 2, 3, 5, 6) ──────────────────────────\nexport const SELECT_FILL_MS = 180;\nexport const CHIP_FILL_MS = 160;\nexport const CHECK_SPRING = { friction: 6, tension: 180 } as const;\nexport const CHECK_SCALE_FROM = 0.6;\n\n// ── Card-to-card handoff (spec rows 7-10) ───────────────────────────────────\nexport const CARD_ENTER_MS = 220;\nexport const CARD_EXIT_MS = 180;\n/** The exit starts this long BEFORE the enter (enter is delayed by this). */\nexport const CARD_EXIT_LEAD_MS = 80;\nexport const CARD_SLIDE_PX = 26;\nexport const CARD_ENTER_SCALE_FROM = 0.96;\n/** The interstitial keeps the shell's springier value-beat (AnimatedCard idiom). */\nexport const CARD_SPRING = { damping: 14, stiffness: 180, mass: 1 } as const;\n/** Loader-to-card handoff: the loader just fades out. */\nexport const LOADER_HANDOFF_FADE_MS = 120;\n\n// ── InterstitialCard reveal (spec rows 11-13) ───────────────────────────────\nexport const INTERSTITIAL_HEAD_MS = 500;\nexport const INTERSTITIAL_HEAD_STAGGER_MS = 100;\nexport const INTERSTITIAL_ITEM_MS = 300;\nexport const INTERSTITIAL_ITEM_STAGGER_MS = 90;\nexport const INTERSTITIAL_ITEM_BASE_DELAY_MS = 200;\nexport const INTERSTITIAL_CHECK_DELAY_MS = 200;\nexport const INTERSTITIAL_CHECK_SCALE_FROM = 0.4;\n\n/** Delay before work item `index` begins its reveal (row lands after the head). */\nexport const interstitialItemDelay = (index: number): number =>\n INTERSTITIAL_ITEM_BASE_DELAY_MS + index * INTERSTITIAL_ITEM_STAGGER_MS;\n\n/** Delay before work item `index`'s check pops (200ms after its row starts). */\nexport const interstitialCheckDelay = (index: number): number =>\n interstitialItemDelay(index) + INTERSTITIAL_CHECK_DELAY_MS;\n\n// ── Status / completion payoff (spec rows 14-16) ────────────────────────────\nexport const STATUS_POP_SPRING = { friction: 6, tension: 200 } as const;\nexport const STATUS_POP_SCALE_FROM = 0.4;\nexport const STATUS_BURST_MS = 500;\nexport const STATUS_BURST_SCALE_FROM = 0.4;\nexport const STATUS_BURST_SCALE_TO = 1.5;\nexport const STATUS_BURST_OPACITY_FROM = 0.5;\nexport const STATUS_COPY_MS = 300;\nexport const STATUS_COPY_STAGGER_MS = 80;\n/** Copy starts settling once the badge pop is visibly underway. */\nexport const STATUS_COPY_BASE_DELAY_MS = 200;\n\n/** Delay before payoff copy line `index` fades up. */\nexport const statusCopyDelay = (index: number): number =>\n STATUS_COPY_BASE_DELAY_MS + index * STATUS_COPY_STAGGER_MS;\n\n// ── Loading treatment (spec rows 17-19) ─────────────────────────────────────\nexport const LOADER_PULSE_MS = 1600;\nexport const LOADER_PULSE_MIN_OPACITY = 0.35;\nexport const LOADER_ORBIT_MS = 2400;\n/** Skeleton lines appear only when the fetch runs past this. */\nexport const SKELETON_DELAY_MS = 300;\nexport const SKELETON_PULSE_MS = 1500;\nexport const SKELETON_MIN_OPACITY = 0.45;\n\n// ── Progress bar (spec row 21) ──────────────────────────────────────────────\nexport const PROGRESS_MS = 400;\n\n// ── Dev/QA duration scale (slow-mo for judging motion) ──────────────────────\n// A module-level multiplier applied to every timing DURATION (and stagger\n// delay) at animation start. 1 = design speed; 2 = half-speed slow-mo. Springs\n// are physics-based (friction/tension) and do NOT scale. This is a dev/QA tool\n// (the playground's tweak panel drives it) — production code never calls the\n// setter, so the default 1 keeps shipped motion exactly on spec.\n\nlet durationScale = 1;\n\n/** Set the global duration multiplier (clamped to 0.1-10; invalid resets to 1). */\nexport const setMotionDurationScale = (scale: number): void => {\n durationScale = Number.isFinite(scale) && scale > 0 ? Math.min(10, Math.max(0.1, scale)) : 1;\n};\n\nexport const getMotionDurationScale = (): number => durationScale;\n\n/** Apply the current duration scale to a spec duration/delay (rounded ms). */\nexport const scaledMs = (ms: number): number => Math.round(ms * durationScale);\n\n// ── Reduce-motion helpers (pure, shared by every animated component) ────────\n\nexport type EntranceSpec = {\n /** Duration to animate with (falls back to the short fade under reduce motion). */\n duration: number;\n /** Whether transforms (translate/scale) may move; false = opacity-only fade. */\n useTransform: boolean;\n};\n\n/** Entrances: full motion normally; a 120ms opacity-only fade under reduce motion. */\nexport const entranceSpec = (reduced: boolean, durationMs: number): EntranceSpec =>\n reduced\n ? { duration: REDUCED_FADE_MS, useTransform: false }\n : { duration: durationMs, useTransform: true };\n\n/** State changes (select fills etc.): snap instantly under reduce motion. */\nexport const stateChangeDuration = (reduced: boolean, durationMs: number): number =>\n reduced ? 0 : durationMs;\n\n/** Looping/ambient animations run only when motion is allowed. */\nexport const loopEnabled = (reduced: boolean): boolean => !reduced;\n","/**\n * useReducedMotion — the kit's ONE reduce-motion gate. Reads the OS setting via\n * AccessibilityInfo and stays subscribed to `reduceMotionChanged`, so every\n * animated component lands on its final frame (or a ~120ms fade) the moment the\n * user flips the setting — no restart needed.\n *\n * This closes the gap documented in ai_rules/rules/performance.md §3: every\n * animation in the kit must consume this hook (usually through the pure helpers\n * in motionSpec.ts: `entranceSpec` / `stateChangeDuration` / `loopEnabled`).\n */\nimport { useEffect, useState } from \"react\";\nimport { AccessibilityInfo } from \"react-native\";\n\n// Last-known value, module-level: cards remount every turn (keyed on message id),\n// and a `useState(false)` default would flash one animated frame per turn for a\n// reduce-motion user before the async read resolves.\nlet lastKnown = false;\n\nexport const useReducedMotion = (): boolean => {\n const [reduced, setReduced] = useState(lastKnown);\n\n useEffect(() => {\n let mounted = true;\n AccessibilityInfo.isReduceMotionEnabled()\n .then((value) => {\n lastKnown = value;\n if (mounted) setReduced(value);\n })\n .catch(() => {\n // Unavailable (e.g. some test envs): keep the last-known value.\n });\n const sub = AccessibilityInfo.addEventListener(\"reduceMotionChanged\", (value) => {\n lastKnown = value;\n setReduced(value);\n });\n return () => {\n mounted = false;\n sub?.remove();\n };\n }, []);\n\n return reduced;\n};\n","/**\n * Button — the kit's themed primary/outline button, replacing the SDK's hardcoded\n * `Btn`. Every color/radius/font comes from the active theme.\n *\n * Motion (design-refs/EXTRACTED-SPEC.md row 20): \"a footer button that answers\n * the tap\" — scale 1→0.975 spring on press-in, back on release, native driver.\n * Reduce motion: an opacity dip to 0.85 while pressed, no scale. Disabled keeps\n * the `disabled` token fill with no press handler (unchanged).\n */\nimport React, { useCallback, useRef } from \"react\";\nimport { Animated, Pressable, StyleSheet, Text } from \"react-native\";\nimport { useOnboardingTheme } from \"../theme/ThemeContext\";\nimport { buttonLabelStyle } from \"../theme/typography\";\nimport {\n BUTTON_PRESS_SCALE,\n BUTTON_PRESS_SPRING,\n REDUCED_FADE_MS,\n REDUCED_PRESS_OPACITY,\n scaledMs,\n} from \"../motion/motionSpec\";\nimport { useReducedMotion } from \"../motion/useReducedMotion\";\n\ntype Variant = \"primary\" | \"outline\";\n\ntype ButtonProps = {\n title: string;\n onPress?: () => void;\n variant?: Variant;\n disabled?: boolean;\n /** Stretch to the container width — for form-style CTAs (e.g. the review form's\n * \"Send feedback\"). Default false keeps the content-width pill used by cards. */\n fullWidth?: boolean;\n};\n\nconst _Button: React.FC<ButtonProps> = ({\n title,\n onPress,\n variant = \"primary\",\n disabled = false,\n fullWidth = false,\n}) => {\n const t = useOnboardingTheme();\n const reduced = useReducedMotion();\n const isPrimary = variant === \"primary\";\n\n // 0 = at rest, 1 = pressed. Scale normally; opacity dip under reduce motion.\n const pressT = useRef(new Animated.Value(0)).current;\n const pressTo = useCallback(\n (to: number) => {\n if (reduced) {\n Animated.timing(pressT, {\n toValue: to,\n duration: scaledMs(REDUCED_FADE_MS),\n useNativeDriver: true,\n }).start();\n return;\n }\n Animated.spring(pressT, {\n toValue: to,\n friction: BUTTON_PRESS_SPRING.friction,\n tension: BUTTON_PRESS_SPRING.tension,\n useNativeDriver: true,\n }).start();\n },\n [pressT, reduced],\n );\n const handlePressIn = useCallback(() => pressTo(1), [pressTo]);\n const handlePressOut = useCallback(() => pressTo(0), [pressTo]);\n\n const scale = pressT.interpolate({\n inputRange: [0, 1],\n outputRange: [1, reduced ? 1 : BUTTON_PRESS_SCALE],\n });\n const opacity = pressT.interpolate({\n inputRange: [0, 1],\n outputRange: [1, reduced ? REDUCED_PRESS_OPACITY : 1],\n });\n\n return (\n <Pressable\n onPress={disabled ? undefined : onPress}\n onPressIn={disabled ? undefined : handlePressIn}\n onPressOut={disabled ? undefined : handlePressOut}\n disabled={disabled}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled }}\n style={fullWidth ? styles.fill : undefined}\n >\n <Animated.View\n style={[\n styles.base,\n fullWidth && styles.fill,\n {\n borderRadius: t.button.radius,\n minHeight: t.button.height,\n paddingHorizontal: t.button.paddingX,\n opacity,\n transform: [{ scale }],\n },\n isPrimary\n ? { backgroundColor: disabled ? t.colors.disabled : t.colors.primary }\n : { borderWidth: 1.5, borderColor: disabled ? t.colors.border : t.colors.primary },\n ]}\n >\n <Text\n style={[\n buttonLabelStyle(t.fonts),\n {\n color: disabled\n ? t.colors.textMuted\n : isPrimary\n ? t.colors.onPrimary\n : t.colors.primary,\n },\n ]}\n >\n {title}\n </Text>\n </Animated.View>\n </Pressable>\n );\n};\n\nexport const Button = React.memo(_Button);\n\nconst styles = StyleSheet.create({\n base: { alignItems: \"center\", justifyContent: \"center\" },\n fill: { width: \"100%\" },\n});\n","/**\n * decision.ts — the pure decision logic for the review gate.\n *\n * `decideReview` is the AI seam, mirroring `selectTourSteps` exactly: ONE function that\n * turns an optional injected decision into the live verdict. No injected decision → the\n * local rules stand; a server decision present → it OVERRIDES them. Building the seam now\n * is the whole cost of being AI-ready: today the host passes local rules only; later the\n * Wire server computes `{fire, reason}` from the onboarding learnings and the host passes\n * it straight in — zero app changes.\n *\n * // v1 (local rules only)\n * const verdict = decideReview(evaluateGate(rules, signals));\n *\n * // AI on (server-provided decision wins)\n * const verdict = decideReview(evaluateGate(rules, signals), serverDecision);\n *\n * `evaluateGate`, `routeRating`, and `buildReviewSubmission` are pure so the gate logic\n * is verifiable in isolation (the kit has no test runner; correctness lives in these).\n */\nimport type {\n RatingRoute,\n ReviewDecision,\n ReviewSubmission,\n} from \"./types\";\n\n/**\n * The seam. `decision` absent → the local verdict stands; present → it overrides.\n * Same contract philosophy as `selectTourSteps(catalog, selection?)`.\n */\nexport const decideReview = (\n local: ReviewDecision,\n decision?: ReviewDecision,\n): ReviewDecision => decision ?? local;\n\n/** The signals the local rules evaluate against (read from injected sync storage + host). */\nexport interface GateSignals {\n /** Sessions observed on this device (incl. the current one). */\n sessions: number;\n /** Tracked app events observed (host-owned). */\n events: number;\n /** Epoch ms of the last time the gate was shown, or null if never. */\n lastShownAt: number | null;\n /** Whether the once-per-version gate is already satisfied. */\n seen: boolean;\n /** Current epoch ms (injected so the function stays pure). */\n now: number;\n}\n\n/** The resolved local rules (defaults applied). */\nexport interface GateRules {\n enabled: boolean;\n minSessions: number;\n minEvents: number;\n cooldownDays: number;\n oncePerVersion: boolean;\n}\n\n/** Fill a partial `ReviewConfig` with the rule defaults. */\nexport const resolveRules = (config: {\n enabled?: boolean;\n minSessions?: number;\n minEvents?: number;\n cooldownDays?: number;\n oncePerVersion?: boolean;\n}): GateRules => ({\n enabled: config.enabled ?? true,\n minSessions: config.minSessions ?? 0,\n minEvents: config.minEvents ?? 0,\n cooldownDays: config.cooldownDays ?? 0,\n oncePerVersion: config.oncePerVersion ?? true,\n});\n\nconst MS_PER_DAY = 86_400_000;\n\n/**\n * Deterministic v1 local decision. Order: disabled → once-gate → min-sessions →\n * min-events → cooldown → fire. Reasons mirror the server evaluator's vocabulary.\n */\nexport const evaluateGate = (rules: GateRules, s: GateSignals): ReviewDecision => {\n if (!rules.enabled) return { fire: false, reason: \"disabled\" };\n if (rules.oncePerVersion && s.seen) return { fire: false, reason: \"already_shown\" };\n if (s.sessions < rules.minSessions) return { fire: false, reason: \"min_sessions\" };\n if (s.events < rules.minEvents) return { fire: false, reason: \"min_events\" };\n if (rules.cooldownDays > 0 && s.lastShownAt != null) {\n const elapsedDays = (s.now - s.lastShownAt) / MS_PER_DAY;\n if (elapsedDays < rules.cooldownDays) return { fire: false, reason: \"cooldown\" };\n }\n return { fire: true, reason: \"local_rules_met\" };\n};\n\n/**\n * Route a rating. 5 stars → the native store review; 1-4 → the feedback form. The\n * sentiment question itself is neutral — this is the ONLY place 5 is treated specially,\n * never a button labeled \"rate us 5 stars\" (see the store-policy notes in the README).\n */\nexport const routeRating = (stars: number): RatingRoute =>\n stars >= 5 ? \"store\" : \"feedback\";\n\n/** Build the `POST /v1/reviews` body. Feedback text/contact belong ONLY here, never in events. */\nexport const buildReviewSubmission = (input: {\n stars: number;\n feedbackText?: string;\n suggestion?: string;\n contact?: string;\n sessionId?: string;\n source?: string;\n meta?: Record<string, unknown>;\n}): ReviewSubmission => {\n const body: ReviewSubmission = { stars: input.stars };\n const text = input.feedbackText?.trim();\n if (text) body.feedback_text = text;\n const suggestion = input.suggestion?.trim();\n if (suggestion) body.suggestion = suggestion;\n const contact = input.contact?.trim();\n if (contact) body.contact = contact;\n if (input.sessionId) body.session_id = input.sessionId;\n if (input.source) body.source = input.source;\n if (input.meta && Object.keys(input.meta).length > 0) body.meta = input.meta;\n return body;\n};\n","/**\n * feedbackForm — pure sizing helpers for the 1–4★ feedback form, kept out of the\n * React component so the \"≈4 lines tall, capped at 5\" rule is verifiable in\n * isolation (the reviews module's correctness lives in these pure functions, the\n * same convention as `decision.ts` / `presentation.ts`).\n *\n * Malik's ask: the feedback field should be a multiline box \"around 3 to 5 lines\"\n * tall, not a single-line input. We size it from the theme's body line-height so\n * it scales with the app's font config.\n */\nimport type { OnboardingFonts } from \"../theme/types\";\nimport { bodyStyle } from \"../theme/typography\";\n\n/** Lines the box shows at rest, and the cap it can grow to. */\nexport const FEEDBACK_MIN_LINES = 4;\nexport const FEEDBACK_MAX_LINES = 5;\n\nexport interface FeedbackInputHeights {\n /** Resting height — the box opens at ~4 lines so it reads as \"write a few words\". */\n minHeight: number;\n /** Growth cap — it never balloons past ~5 lines; scroll takes over beyond that. */\n maxHeight: number;\n}\n\n/**\n * Derive the multiline feedback box's min/max height from the theme's body\n * line-height. `minHeight = MIN_LINES × lineHeight`, capped at\n * `MAX_LINES × lineHeight`, plus the field's vertical padding (top + bottom) so\n * the requested number of text lines is actually visible inside the padding.\n */\nexport const feedbackInputHeights = (\n fonts: OnboardingFonts,\n verticalPadding = 0,\n): FeedbackInputHeights => {\n const lineHeight = bodyStyle(fonts).lineHeight ?? Math.round(fonts.baseSize * fonts.scale * 1.5);\n const pad = verticalPadding * 2;\n return {\n minHeight: lineHeight * FEEDBACK_MIN_LINES + pad,\n maxHeight: lineHeight * FEEDBACK_MAX_LINES + pad,\n };\n};\n","/**\n * centeredModalMotion - the pure entrance spec for the content-agnostic CenteredModal shell.\n *\n * WHY split out: the shell UI (CenteredModal) is RN and cannot run under the kit's node:test\n * runner, but the entrance math is pure and worth pinning. Keep the animation spec here so both\n * the component and the tests read one source, and so any module (reviews, questionnaire) can\n * reuse the shell without depending on another feature module.\n *\n * The centered popup uses RN's built-in `Animated` (native driver), the same idiom as\n * CompletionView/CardHandoff, so the shell stays dependency-free (no reanimated).\n *\n * NOTE: `reviews/presentation.ts` re-exports these under their original names\n * (`REVIEW_MODAL_ANIM`, `entranceFrame`) for back-compat with the reviews public API.\n */\n\n/**\n * The entrance spec for the centered popup. Values chosen to match the boilerplate's\n * `animated-modal` feel (a springy zoom-in) with the kit's own native-driver idiom:\n * • backdrop fades 0 -> `backdropOpacity`\n * • card springs from `scaleFrom` -> 1 and fades 0 -> 1\n */\nexport const CENTERED_MODAL_ANIM = {\n /** Spring config for the card scale/opacity entrance (mirrors CompletionView's pop). */\n enterSpring: { friction: 7, tension: 80 },\n /** Exit timing in ms (fade + shrink back out before unmount). */\n exitMs: 160,\n /** Card scale at rest (progress 0); grows to 1 at progress 1. */\n scaleFrom: 0.9,\n /** Peak dim of the backdrop at progress 1. */\n backdropOpacity: 0.55,\n} as const;\n\n/** A single frame of the entrance, for a progress value in [0..1] (clamped). Pure, testable. */\nexport const centeredModalFrame = (\n progress: number,\n backdropMax: number = CENTERED_MODAL_ANIM.backdropOpacity,\n): { cardOpacity: number; cardScale: number; backdropOpacity: number } => {\n const p = progress < 0 ? 0 : progress > 1 ? 1 : progress;\n const { scaleFrom } = CENTERED_MODAL_ANIM;\n return {\n cardOpacity: p,\n cardScale: scaleFrom + (1 - scaleFrom) * p,\n backdropOpacity: backdropMax * p,\n };\n};\n","/**\n * presentation.ts — the pure presentation + entrance-animation logic for the review gate.\n *\n * WHY split out: the gate UI (ReviewGate/ReviewModal) is RN and can't run under the kit's\n * node:test runner, but the presentation DEFAULT and the entrance math are pure and worth\n * pinning. Keep the animation spec here so both the component and the tests read one source.\n *\n * The centered popup uses RN's built-in `Animated` (native driver) — the same idiom as\n * CompletionView/CardHandoff — so the reviews module stays dependency-free (no reanimated).\n */\nimport type { ReviewPresentation } from \"./types\";\n\n/** Centered popup by default (Malik's ruling: the bottom-anchored inline card read as wrong). */\nexport const DEFAULT_PRESENTATION: ReviewPresentation = \"modal\";\n\n/** Resolve the presentation prop → the effective mode. Absent → the centered modal. */\nexport const resolvePresentation = (p?: ReviewPresentation): ReviewPresentation =>\n p ?? DEFAULT_PRESENTATION;\n\n/**\n * The centered-popup entrance spec + pure entrance-frame math now live in the neutral\n * `../motion/centeredModalMotion` (the shell was extracted to `components/CenteredModal` so the\n * questionnaire module can reuse it without importing reviews/). Re-exported here under their\n * original names so the reviews public API (reviews/index.ts) and presentation.test.ts stay valid.\n */\nexport {\n CENTERED_MODAL_ANIM as REVIEW_MODAL_ANIM,\n centeredModalFrame as entranceFrame,\n} from \"../motion/centeredModalMotion\";\n","/**\n * CenteredModal - the content-agnostic centered-popup shell.\n *\n * Renders its children as an alert-style card in the MIDDLE of the screen, over a dimmed\n * backdrop, with a spring/fade entrance. It knows nothing about what it presents: the reviews\n * gate uses it (via the `ReviewModal` back-compat re-export) and the questionnaire module reuses\n * the same shell, neither depends on the other.\n *\n * Adapted from the mobile-launcher boilerplate's `animated-modal` (transparent RN Modal +\n * centered container + backdrop-tap dismiss + content stop-propagation + springy zoom-in), but\n * reimplemented on RN's built-in `Animated` (native driver) so the shell stays dependency-free:\n * the boilerplate uses react-native-reanimated, which the kit does not.\n *\n * Dismissal (backdrop tap / Android back / the imperative `close()` handle) animates the card\n * out, then fires `onDismiss`. It never auto-closes on resolve: the host keeps the card showing\n * whatever payoff it wants (a thank-you, a summary) until the user taps the backdrop to close,\n * exactly like CompletionView.\n */\nimport React, {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from \"react\";\nimport { Animated, BackHandler, Easing, Modal, Pressable, StyleSheet } from \"react-native\";\nimport { SafeAreaView } from \"react-native-safe-area-context\";\n\nimport type { OnboardingTheme } from \"../theme/types\";\nimport { REDUCED_FADE_MS } from \"../motion/motionSpec\";\nimport { useReducedMotion } from \"../motion/useReducedMotion\";\nimport { CENTERED_MODAL_ANIM } from \"../motion/centeredModalMotion\";\n\n/** Imperative handle so an in-card \"Not now\"/\"Skip\" link can trigger the exit animation. */\nexport type CenteredModalHandle = { close: () => void };\n\nexport type CenteredModalProps = {\n /** The (already merged) active theme - card surface/radius/spacing come from here. */\n theme: OnboardingTheme;\n /** Whether a backdrop tap / back press dismisses. Default true (never a dead end). */\n dismissible?: boolean;\n /** Fired once, AFTER the exit animation, on any dismiss. Wire to the host's resolve. */\n onDismiss: () => void;\n children: React.ReactNode;\n};\n\nconst _CenteredModal = forwardRef<CenteredModalHandle, CenteredModalProps>(\n ({ theme, dismissible = true, onDismiss, children }, ref) => {\n const progress = useRef(new Animated.Value(0)).current;\n const [open, setOpen] = useState(true);\n const closingRef = useRef(false);\n const reduced = useReducedMotion();\n\n // Entrance: spring the card in (scale + fade); the backdrop fades up in lock-step.\n // Reduce motion: a short fade to the settled card, no spring/scale.\n useEffect(() => {\n const anim = reduced\n ? Animated.timing(progress, {\n toValue: 1,\n duration: REDUCED_FADE_MS,\n useNativeDriver: true,\n })\n : Animated.spring(progress, {\n toValue: 1,\n useNativeDriver: true,\n friction: CENTERED_MODAL_ANIM.enterSpring.friction,\n tension: CENTERED_MODAL_ANIM.enterSpring.tension,\n });\n anim.start();\n return () => anim.stop();\n }, [progress, reduced]);\n\n const close = useCallback(() => {\n if (closingRef.current) return;\n closingRef.current = true;\n Animated.timing(progress, {\n toValue: 0,\n duration: reduced ? REDUCED_FADE_MS : CENTERED_MODAL_ANIM.exitMs,\n easing: Easing.in(Easing.cubic),\n useNativeDriver: true,\n }).start(() => {\n setOpen(false);\n onDismiss();\n });\n }, [progress, onDismiss, reduced]);\n\n useImperativeHandle(ref, () => ({ close }), [close]);\n\n // Android hardware back -> dismiss (when allowed). Always consume so it never pops a route.\n useEffect(() => {\n const sub = BackHandler.addEventListener(\"hardwareBackPress\", () => {\n if (dismissible) close();\n return true;\n });\n return () => sub.remove();\n }, [dismissible, close]);\n\n const onBackdrop = useCallback(() => {\n if (dismissible) close();\n }, [dismissible, close]);\n\n const backdropOpacity = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [0, CENTERED_MODAL_ANIM.backdropOpacity],\n });\n // Reduce motion: the card fades in already settled (no zoom).\n const cardScale = reduced\n ? 1\n : progress.interpolate({\n inputRange: [0, 1],\n outputRange: [CENTERED_MODAL_ANIM.scaleFrom, 1],\n });\n\n return (\n <Modal\n visible={open}\n transparent\n animationType=\"none\"\n statusBarTranslucent\n onRequestClose={dismissible ? close : undefined}\n >\n {/* Dimmed backdrop (fades with the entrance). */}\n <Animated.View\n style={[styles.backdrop, { opacity: backdropOpacity }]}\n pointerEvents=\"none\"\n />\n\n {/* Full-screen tap target: a tap outside the card dismisses. */}\n <Pressable\n style={styles.fill}\n onPress={onBackdrop}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Dismiss\"\n >\n <SafeAreaView style={styles.center} pointerEvents=\"box-none\">\n {/* Inner Pressable swallows taps so the card itself never dismisses. */}\n <Pressable onPress={() => {}} style={styles.cardWrap}>\n <Animated.View\n accessibilityViewIsModal\n accessibilityRole=\"alert\"\n style={[\n styles.card,\n {\n backgroundColor: theme.colors.surface,\n borderRadius: theme.radius.lg,\n padding: theme.spacing.lg,\n gap: theme.spacing.md,\n opacity: progress,\n transform: [{ scale: cardScale }],\n },\n ]}\n >\n {children}\n </Animated.View>\n </Pressable>\n </SafeAreaView>\n </Pressable>\n </Modal>\n );\n },\n);\n\n_CenteredModal.displayName = \"CenteredModal\";\n\nexport const CenteredModal = React.memo(_CenteredModal);\n\nconst styles = StyleSheet.create({\n fill: { flex: 1 },\n backdrop: { position: \"absolute\", top: 0, left: 0, right: 0, bottom: 0, backgroundColor: \"#000\" },\n center: { flex: 1, alignItems: \"center\", justifyContent: \"center\", padding: 24 },\n cardWrap: { width: \"100%\", maxWidth: 420 },\n card: {\n width: \"100%\",\n // Soft elevation so the popup reads as lifted above the app in both themes.\n shadowColor: \"#000\",\n shadowOffset: { width: 0, height: 8 },\n shadowOpacity: 0.28,\n shadowRadius: 24,\n elevation: 12,\n },\n});\n\nexport default CenteredModal;\n","/**\n * storeReview.ts — the 5-star route: ask for a native in-app review, or fall back to\n * opening the store listing.\n *\n * `expo-store-review` is an OPTIONAL peer, loaded lazily via a guarded dynamic import so\n * the core kit never depends on it. When present + available, we call\n * `StoreReview.requestReview()` (the OS decides whether to actually show the dialog — it is\n * quota'd, ~3/year on iOS, and NEVER guaranteed). When absent/unavailable, we open the\n * configured store URL as a graceful fallback. Both paths are best-effort and never throw.\n *\n * Store-policy: call this ONLY on the honest 5-star path, from a NEUTRAL prompt — never a\n * button labeled \"rate us 5 stars\". See the README store-policy notes.\n */\nimport { Linking, Platform } from \"react-native\";\n\nimport type { ReviewStoreConfig } from \"./types\";\n\n/** The store deep-link for the current platform, or null if none is configured. */\nexport const storeUrl = (store: ReviewStoreConfig | undefined): string | null => {\n if (!store) return null;\n if (Platform.OS === \"ios\" && store.iosAppId) {\n return `itms-apps://itunes.apple.com/app/id${store.iosAppId}?action=write-review`;\n }\n if (Platform.OS === \"android\" && store.androidPackage) {\n return `market://details?id=${store.androidPackage}`;\n }\n return store.storeUrl ?? null;\n};\n\n/** Open the configured store listing. Best-effort; resolves false if there was nothing to open. */\nexport const openStoreListing = async (\n store: ReviewStoreConfig | undefined,\n): Promise<boolean> => {\n const url = storeUrl(store);\n if (!url) return false;\n try {\n await Linking.openURL(url);\n return true;\n } catch {\n return false;\n }\n};\n\n/**\n * Request the native in-app review when the optional peer is available; otherwise open the\n * store listing. Returns the path taken so the caller can fire the right analytics.\n */\nexport const requestStoreReview = async (\n store?: ReviewStoreConfig,\n): Promise<\"native\" | \"store_url\" | \"none\"> => {\n try {\n const mod = await import(\"expo-store-review\");\n if (mod && (await mod.isAvailableAsync())) {\n await mod.requestReview();\n return \"native\";\n }\n } catch {\n // Peer not installed or the native call failed — fall through to the URL fallback.\n }\n const opened = await openStoreListing(store);\n return opened ? \"store_url\" : \"none\";\n};\n","/**\n * transport.ts — kit → Wire server requests for the review module, all fire-and-forget\n * (analytics/reviews must never break the app). Mirrors analytics/reportClientEvent: a\n * thin fetch wrapper, Bearer tenant key, swallow every error.\n *\n * • submitReview → POST {serverUrl}/v1/reviews (the 1-4 feedback body)\n * • reportAppEvent → POST {serverUrl}/v1/events (generic app.* namespace)\n *\n * `reportAppEvent` is the strategic extension: it lets a host report arbitrary in-app\n * events through the SAME transport (stored server-side as event_type='app_event',\n * question_key=<name>), which is what the backend review-firing rules evaluate on — and\n * it seeds the broader app-analytics stream. Keep payloads minimal + non-PII.\n */\nimport type { ReviewSubmission, ReviewTarget } from \"./types\";\n\n/**\n * POST a review (the 1-4 feedback path). Fire-and-forget: a missing target, a build error,\n * a missing `fetch`, or a network failure is swallowed and the call returns immediately.\n */\nexport const submitReview = (\n target: ReviewTarget | undefined,\n review: ReviewSubmission,\n): void => {\n if (!target?.serverUrl) return;\n try {\n const url = `${target.serverUrl.replace(/\\/$/, \"\")}/v1/reviews`;\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;\n void fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(review),\n }).catch(() => {\n /* best-effort, swallow */\n });\n } catch {\n /* URL/JSON/missing-fetch — swallow */\n }\n};\n\n/** Options for a reported app event. `deviceKey` groups a device's sessions server-side. */\nexport interface ReportAppEventOptions {\n /** The onboarding/session id to correlate with, when known. */\n sessionId?: string;\n /** A stable, non-PII device id — the review-decision endpoint reads it for min-sessions. */\n deviceKey?: string;\n /** Small non-PII extras. */\n meta?: Record<string, unknown>;\n}\n\n/**\n * Report a generic in-app event through the existing events transport. Stored server-side\n * as `event_type='app_event'`, `question_key=<name>`. Fire-and-forget. Keep `name` a short\n * stable identifier and `meta` small + non-PII.\n *\n * reportAppEvent(target, \"content_share\", { sessionId, deviceKey });\n */\nexport const reportAppEvent = (\n target: ReviewTarget | undefined,\n name: string,\n options: ReportAppEventOptions = {},\n): void => {\n if (!target?.serverUrl || !name) return;\n try {\n const url = `${target.serverUrl.replace(/\\/$/, \"\")}/v1/events`;\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;\n const event: Record<string, unknown> = {\n event_type: \"app_event\",\n question_key: name,\n };\n if (options.sessionId) event.session_id = options.sessionId;\n // device_key rides in the non-PII user_context bucket the server sanitizes; the\n // review-decision endpoint reads it to group a device's sessions.\n if (options.deviceKey) event.user_context = { device_key: options.deviceKey };\n if (options.meta && Object.keys(options.meta).length > 0) {\n event.meta = JSON.stringify(options.meta);\n }\n void fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify({ events: [event] }),\n }).catch(() => {\n /* best-effort, swallow */\n });\n } catch {\n /* swallow */\n }\n};\n","/**\n * ReviewGate — the in-app review sentiment gate UI.\n *\n * A NEUTRAL prompt (\"Enjoying <app>?\") over five tappable stars. The honest split:\n * • 5 stars → the native store review (expo-store-review when available, else the\n * configured store URL). Fires `store_review_requested`.\n * • 1-4 stars → a feather-light feedback form: TWO optional inputs, the \"why this\n * rating\" opinion box (~4 lines) and a smaller \"what can you suggest\" box, POSTed\n * to the Wire server (`feedback_text` + `suggestion`). The rating is the only\n * mandatory action, so Send works with either box, both, or neither. Warm\n * thank-you state, never a dead end.\n *\n * The form NEVER asks for identity (no name / email / contact field): the host already\n * identifies the user via `sessionId` + `meta` (e.g. user_id, device_key), which is all\n * the POST body carries alongside the rating and the free text.\n *\n * Every visual value comes from the OnboardingTheme (useOnboardingTheme). Analytics are\n * callback-based (mirroring the coachmark convention): the host wires `onEvent` to its own\n * analytics. No PII in events — feedback text goes ONLY in the POST body.\n *\n * Store-policy: `requestReview` is quota'd and never guaranteed to show; it is called ONLY\n * on the 5-star path, never from a \"rate us 5 stars\" button. See the README notes.\n */\nimport React, { useCallback, useMemo, useState } from \"react\";\nimport { Pressable, StyleSheet, Text, TextInput, View } from \"react-native\";\n\nimport { Button } from \"../components/Button\";\nimport { useOnboardingTheme } from \"../theme/ThemeContext\";\nimport type { OnboardingTheme } from \"../theme/types\";\nimport { bodyStyle, captionStyle, headingStyle } from \"../theme/typography\";\nimport { buildReviewSubmission, routeRating } from \"./decision\";\nimport { feedbackInputHeights } from \"./feedbackForm\";\nimport { resolvePresentation } from \"./presentation\";\nimport { ReviewModal, type ReviewModalHandle } from \"./ReviewModal\";\nimport { requestStoreReview } from \"./storeReview\";\nimport { submitReview } from \"./transport\";\nimport type { ReviewGateProps } from \"./types\";\n\nconst mergeThemeOver = (\n base: OnboardingTheme,\n override?: Partial<OnboardingTheme>,\n): OnboardingTheme => {\n if (!override) return base;\n return {\n colors: { ...base.colors, ...override.colors },\n fonts: { ...base.fonts, ...override.fonts },\n radius: { ...base.radius, ...override.radius },\n spacing: { ...base.spacing, ...override.spacing },\n button: { ...base.button, ...override.button },\n };\n};\n\ntype Phase = \"rating\" | \"feedback\" | \"thanks\";\n\nexport const ReviewGate: React.FC<ReviewGateProps> = ({\n appName,\n store,\n target,\n sessionId,\n meta,\n onEvent,\n onResolved,\n onShown,\n theme: themeOverride,\n dismissible = true,\n presentation,\n id = \"default\",\n}) => {\n const active = useOnboardingTheme();\n const t = useMemo(() => mergeThemeOver(active, themeOverride), [active, themeOverride]);\n const mode = resolvePresentation(presentation);\n const modalRef = React.useRef<ReviewModalHandle>(null);\n\n const [phase, setPhase] = useState<Phase>(\"rating\");\n const [stars, setStars] = useState(0);\n const [hovered, setHovered] = useState(0);\n const [feedback, setFeedback] = useState(\"\");\n const [suggestion, setSuggestion] = useState(\"\");\n\n // Fire review_prompt_shown exactly ONCE per mount. The callbacks are read through\n // refs (not effect deps) so an inline `onShown`/`onEvent` — recreated every render,\n // as real hosts idiomatically pass — never re-runs the effect. Empty deps + the\n // shownRef guard make this a true fire-once, immune to prop-identity churn.\n const shownRef = React.useRef(false);\n const onShownRef = React.useRef(onShown);\n const onEventRef = React.useRef(onEvent);\n onShownRef.current = onShown;\n onEventRef.current = onEvent;\n React.useEffect(() => {\n if (shownRef.current) return;\n shownRef.current = true;\n onShownRef.current?.();\n onEventRef.current?.({ name: \"review_prompt_shown\", id });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const resolve = useCallback(() => {\n onResolved?.();\n }, [onResolved]);\n\n const pickRating = useCallback(\n async (value: number) => {\n setStars(value);\n onEvent?.({ name: \"review_rating_selected\", id, stars: value });\n if (routeRating(value) === \"store\") {\n onEvent?.({ name: \"store_review_requested\", id, stars: value });\n await requestStoreReview(store);\n setPhase(\"thanks\");\n resolve();\n } else {\n setPhase(\"feedback\");\n }\n },\n [onEvent, id, store, resolve],\n );\n\n const sendFeedback = useCallback(() => {\n // Identity is NEVER collected here — the host already sends user_id / device /\n // session via `sessionId` + `meta`. We POST only the rating + the free text.\n const body = buildReviewSubmission({\n stars,\n feedbackText: feedback,\n suggestion,\n sessionId,\n meta,\n });\n submitReview(target, body);\n onEvent?.({ name: \"review_feedback_submitted\", id, stars });\n setPhase(\"thanks\");\n resolve();\n }, [stars, feedback, suggestion, sessionId, meta, target, onEvent, id, resolve]);\n\n const dismiss = useCallback(() => {\n setPhase(\"thanks\");\n resolve();\n }, [resolve]);\n\n // The in-card \"Not now\"/\"Skip\" link. In modal mode it plays the popup's exit animation\n // (which resolves on completion, the same path a backdrop tap takes); inline keeps the\n // legacy behavior of swapping to the thank-you in place.\n const handleDismissTap = useCallback(() => {\n if (mode === \"modal\") modalRef.current?.close();\n else dismiss();\n }, [mode, dismiss]);\n\n const title =\n phase === \"feedback\"\n ? \"What should we improve?\"\n : phase === \"thanks\"\n ? \"Thank you\"\n : appName\n ? `Enjoying ${appName}?`\n : \"How's your experience?\";\n\n const surface = {\n backgroundColor: t.colors.surface,\n borderRadius: t.radius.lg,\n padding: t.spacing.lg,\n gap: t.spacing.md,\n } as const;\n\n // A multiline box ~4 lines tall (capped at ~5), derived from the theme's body\n // line-height so it scales with the app's font config. The field's own vertical\n // padding is folded in so the requested lines are visible inside the padding.\n const feedbackBox = feedbackInputHeights(t.fonts, t.spacing.md);\n\n // The phase content, shared by both presentations. In \"modal\" mode ReviewModal supplies\n // the centered, dimmed, animated card surface; in \"inline\" mode the bare card below does.\n const content = (\n <>\n <Text style={[headingStyle(t.fonts), { color: t.colors.text }]}>{title}</Text>\n\n {phase === \"rating\" && (\n <>\n <Text style={[captionStyle(t.fonts), { color: t.colors.textMuted }]}>\n Tap a star to let us know.\n </Text>\n <View style={styles.stars}>\n {[1, 2, 3, 4, 5].map((n) => {\n const filled = n <= (hovered || stars);\n return (\n <Pressable\n key={n}\n onPressIn={() => setHovered(n)}\n onPressOut={() => setHovered(0)}\n onPress={() => void pickRating(n)}\n accessibilityRole=\"button\"\n accessibilityLabel={`${n} star${n > 1 ? \"s\" : \"\"}`}\n hitSlop={8}\n >\n <Text\n style={[\n styles.star,\n { color: filled ? t.colors.primary : t.colors.border },\n ]}\n >\n {filled ? \"★\" : \"☆\"}\n </Text>\n </Pressable>\n );\n })}\n </View>\n {dismissible && (\n <Pressable onPress={handleDismissTap} hitSlop={8}>\n <Text style={[captionStyle(t.fonts), styles.link, { color: t.colors.textMuted }]}>\n Not now\n </Text>\n </Pressable>\n )}\n </>\n )}\n\n {phase === \"feedback\" && (\n <>\n <Text style={[captionStyle(t.fonts), { color: t.colors.textMuted }]}>\n A few words is plenty. Both boxes are optional, so send whatever you like.\n </Text>\n {/* Two OPTIONAL inputs. No name / email / contact field: the host already\n identifies the user via sessionId + meta, so we never ask for identity\n here. Send stays enabled whether the user fills one box, both, or neither\n (the rating is the only mandatory action). */}\n <Text style={[captionStyle(t.fonts), { color: t.colors.textMuted }]}>\n Why this rating? (optional)\n </Text>\n <TextInput\n value={feedback}\n onChangeText={setFeedback}\n placeholder=\"e.g. too many steps\"\n placeholderTextColor={t.colors.textMuted}\n multiline\n textAlignVertical=\"top\"\n style={[\n bodyStyle(t.fonts),\n styles.input,\n {\n color: t.colors.text,\n backgroundColor: t.colors.background,\n borderRadius: t.radius.md,\n borderColor: t.colors.border,\n padding: t.spacing.md,\n minHeight: feedbackBox.minHeight,\n maxHeight: feedbackBox.maxHeight,\n },\n ]}\n />\n <Text style={[captionStyle(t.fonts), { color: t.colors.textMuted }]}>\n What can we do better? (optional)\n </Text>\n <TextInput\n value={suggestion}\n onChangeText={setSuggestion}\n placeholder=\"e.g. save my place between steps\"\n placeholderTextColor={t.colors.textMuted}\n style={[\n bodyStyle(t.fonts),\n styles.input,\n {\n color: t.colors.text,\n backgroundColor: t.colors.background,\n borderRadius: t.radius.md,\n borderColor: t.colors.border,\n paddingHorizontal: t.spacing.md,\n paddingVertical: t.spacing.sm,\n minHeight: t.button.height,\n },\n ]}\n />\n <Button\n title=\"Send feedback\"\n onPress={sendFeedback}\n variant=\"primary\"\n fullWidth\n />\n {dismissible && (\n <Pressable onPress={handleDismissTap} hitSlop={8}>\n <Text style={[captionStyle(t.fonts), styles.link, { color: t.colors.textMuted }]}>\n Skip\n </Text>\n </Pressable>\n )}\n </>\n )}\n\n {phase === \"thanks\" && (\n <Text style={[bodyStyle(t.fonts), { color: t.colors.textMuted }]}>\n We appreciate you. This helps us make it better.\n </Text>\n )}\n </>\n );\n\n // Default: a centered popup over a dimmed backdrop with a spring/fade entrance. A backdrop\n // tap (or Android back) resolves via the same path onResolved already tracks.\n if (mode === \"modal\") {\n return (\n <ReviewModal ref={modalRef} theme={t} dismissible={dismissible} onDismiss={resolve}>\n {content}\n </ReviewModal>\n );\n }\n\n // Legacy: the bare card, positioned by wherever the host mounts it.\n return <View style={[styles.card, surface]}>{content}</View>;\n};\n\nReviewGate.displayName = \"ReviewGate\";\n\nconst styles = StyleSheet.create({\n card: { width: \"100%\" },\n stars: { flexDirection: \"row\", justifyContent: \"center\", gap: 8 },\n star: { fontSize: 40, lineHeight: 46 },\n input: { borderWidth: 1 },\n link: { textAlign: \"center\", textDecorationLine: \"underline\" },\n});\n\nexport default ReviewGate;\n","/**\n * Module-level runtime for the coachmark engine: the injected sync storage, the\n * global `isTestingCoachmark` flag, and the seen-gate helpers built on top.\n *\n * WHY module singletons (not just context): the tour hooks and the anchor\n * registry are driven imperatively from anywhere in the tree (a screen deep\n * under the navigator), while the storage + testing flag are configured once at\n * the root by `CoachmarkProvider`. Singletons let a hook read the gate without\n * every call site threading the storage through props. The provider is still the\n * single writer — it calls `setCoachmarkStorage` / `setCoachmarkTesting` on mount.\n */\nimport type { CoachmarkStorage } from \"./types\";\n\nlet storageSingleton: CoachmarkStorage | null = null;\nlet testing = false;\n// The coachmarks feature kill switch (from GET /v1/features). Default true = fail-open: with no\n// flags fetched, coachmarks behave exactly as before. CoachmarkProvider writes it from the\n// resolved flags. Disabled → the tour never ARMS and the overlay `show()` is a no-op, so nothing\n// paints and — critically — no once-gate is written, so re-enabling replays the tour correctly.\nlet coachmarksEnabled = true;\n\n/**\n * Set the coachmarks master switch (from the resolved feature flags). Default true (fail-open).\n * Written by CoachmarkProvider; read imperatively by the tour arm + the overlay store.\n */\nexport const setCoachmarksEnabled = (value: boolean): void => {\n coachmarksEnabled = value;\n};\n\n/** Whether the coachmarks module is enabled. False → tours/overlays are silently skipped. */\nexport const areCoachmarksEnabled = (): boolean => coachmarksEnabled;\n\n/** Set (or clear) the injected sync gate storage. Called by CoachmarkProvider. */\nexport const setCoachmarkStorage = (storage: CoachmarkStorage | null): void => {\n storageSingleton = storage;\n};\n\n/** The currently-injected gate storage, or null if no provider is mounted. */\nexport const getCoachmarkStorage = (): CoachmarkStorage | null => storageSingleton;\n\n/**\n * Toggle the global QA replay flag. When true, EVERY seen-gate reads as unseen\n * and every seen-write is suppressed, so every tour + showcase replays on each\n * mount. The app flips ONE boolean to QA the whole coachmark surface.\n */\nexport const setCoachmarkTesting = (value: boolean): void => {\n testing = value;\n};\n\n/** Whether replay-everything QA mode is on. */\nexport const isCoachmarkTesting = (): boolean => testing;\n\n/** Gate key for a tour: `wire_coachmark_<tourId>_seen`. */\nexport const coachmarkGateKey = (tourId: string): string =>\n `wire_coachmark_${tourId}_seen`;\n\n/** Gate key for a feature showcase: `wire_showcase_<id>_seen`. */\nexport const showcaseGateKey = (showcaseId: string): string =>\n `wire_showcase_${showcaseId}_seen`;\n\n/** Marker value written for a seen gate. */\nconst SEEN_VALUE = \"1\";\n\n/**\n * Read a seen gate. Testing mode ALWAYS reports unseen (so everything replays).\n * A missing storage (no provider) also reports unseen — the safe default is to\n * show the hint rather than silently swallow it. `storageOverride` /\n * `testingOverride` let a self-contained component (e.g. FeatureShowcase) pass\n * its own storage without relying on the provider singleton.\n */\nexport const hasSeenGate = (\n key: string,\n storageOverride?: CoachmarkStorage | null,\n testingOverride?: boolean,\n): boolean => {\n if (testingOverride ?? testing) return false;\n const storage = storageOverride ?? storageSingleton;\n if (!storage) return false;\n try {\n return storage.getItem(key) === SEEN_VALUE;\n } catch {\n return false;\n }\n};\n\n/**\n * Mark a gate seen. Suppressed in testing mode (so replay never persists) and a\n * no-op without storage. Best-effort — a throwing adapter never breaks the tour.\n */\nexport const markSeenGate = (\n key: string,\n storageOverride?: CoachmarkStorage | null,\n testingOverride?: boolean,\n): void => {\n if (testingOverride ?? testing) return;\n const storage = storageOverride ?? storageSingleton;\n if (!storage) return;\n try {\n storage.setItem(key, SEEN_VALUE);\n } catch {\n // Best-effort: a failed write just means the hint may show once more.\n }\n};\n","/**\n * The fail-open defaults + a tolerant parser for the server's flags payload.\n *\n * THE CONTRACT: fail-open, never fail-dark. An absent server object, a missing module key, a\n * malformed value, or an unreachable endpoint must resolve to the MOST PERMISSIVE state — every\n * module ON, onboarding `ai` — so a control-plane outage can only ever be more permissive than\n * the dashboard intends. It can never dark a tenant's activation. Matches the server's own\n * documented fail-open default (FEATURES.md: \"local defaults — every module on, onboarding ai\").\n */\nimport type { OnboardingMode, WireFeatures } from \"./types\";\n\n/**\n * ALL ON, onboarding `ai`. Frozen + module-level so its reference is stable across every call\n * (consumers that list resolved flags in an effect dep array never loop on a fresh identity).\n */\nexport const defaultWireFeatures: WireFeatures = Object.freeze({\n coachmarks: Object.freeze({ enabled: true }),\n showcase: Object.freeze({ enabled: true }),\n onboarding: Object.freeze({ mode: \"ai\" as OnboardingMode }),\n review: Object.freeze({ enabled: true }),\n questionnaire: Object.freeze({ enabled: true }),\n announcements: Object.freeze({ enabled: true }),\n}) as WireFeatures;\n\nconst VALID_MODES: readonly OnboardingMode[] = [\"ai\", \"static\", \"off\"];\n\n/** Read a `{ enabled: boolean }` module, defaulting to ON when absent/malformed (fail-open). */\nconst readEnabled = (raw: unknown): { enabled: boolean } => {\n if (raw && typeof raw === \"object\" && typeof (raw as { enabled?: unknown }).enabled === \"boolean\") {\n return { enabled: (raw as { enabled: boolean }).enabled };\n }\n return { enabled: true };\n};\n\n/**\n * Narrow the server's (untrusted) JSON to a complete `WireFeatures`. Every field is filled with\n * its fail-open default when the server omits it or sends the wrong type, so a partial or\n * malformed payload degrades PERMISSIVELY rather than throwing. A non-object input (null, array,\n * string) → the full defaults. Never throws.\n */\nexport const parseWireFeatures = (raw: unknown): WireFeatures => {\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return defaultWireFeatures;\n const src = raw as Record<string, unknown>;\n const modeRaw = (src.onboarding as { mode?: unknown } | undefined)?.mode;\n const mode: OnboardingMode =\n typeof modeRaw === \"string\" && VALID_MODES.includes(modeRaw as OnboardingMode)\n ? (modeRaw as OnboardingMode)\n : \"ai\";\n return {\n coachmarks: readEnabled(src.coachmarks),\n showcase: readEnabled(src.showcase),\n onboarding: { mode },\n review: readEnabled(src.review),\n questionnaire: readEnabled(src.questionnaire),\n announcements: readEnabled(src.announcements),\n };\n};\n\n/** Value-equality of two resolved flags — lets the hook hold a stable reference across churn. */\nexport const featuresEqual = (a: WireFeatures, b: WireFeatures): boolean =>\n a === b ||\n (a.coachmarks.enabled === b.coachmarks.enabled &&\n a.showcase.enabled === b.showcase.enabled &&\n a.onboarding.mode === b.onboarding.mode &&\n a.review.enabled === b.review.enabled &&\n a.questionnaire.enabled === b.questionnaire.enabled &&\n a.announcements.enabled === b.announcements.enabled);\n","/**\n * The last-known-flags cache — persisted through the host's optional `storage`.\n *\n * WHY: the fetch is best-effort and fail-open. When the control plane is unreachable we serve the\n * LAST value we saw rather than snapping to defaults, so a tenant that intentionally disabled a\n * module during an incident keeps it disabled across an outage instead of flapping back on. That\n * is \"stale-while-error\": a fresh cache (< TTL) is trusted, and on a fetch error even a STALE\n * cache is served (any age beats a guess). Every operation is best-effort — a broken/hung storage\n * degrades to defaults, never a throw and never a stuck gate (mirrors persistedSession).\n */\nimport { parseWireFeatures } from \"./defaults\";\nimport type { WireFeatures } from \"./types\";\nimport type { WireOnboardingStorage } from \"../session/persistedSession\";\n\n/**\n * TTL for a cached flags entry, aligned to the server's `Cache-Control: private, max-age=300`.\n * A cache younger than this is \"fresh\"; older is \"stale\" (still served on a fetch error).\n */\nexport const DEFAULT_FEATURES_TTL_MS = 300_000;\n\n/** Ceiling on the storage read so a hung adapter degrades to defaults, never a stuck resolve. */\nconst READ_TIMEOUT_MS = 1_500;\n\n/** Storage key for a tenant's cached flags, e.g. `wireai:features:myelino`. */\nexport const featuresCacheKey = (appId?: string): string =>\n `wireai:features:${appId && appId.length > 0 ? appId : \"default\"}`;\n\n/** A cached flags entry: the resolved flags + the epoch-ms they were fetched. */\nexport interface CachedFeatures {\n features: WireFeatures;\n /** Age of the entry in ms at read time. */\n ageMs: number;\n}\n\nconst withTimeout = <T>(p: Promise<T>, ms: number): Promise<T | undefined> => {\n let timer: ReturnType<typeof setTimeout>;\n const timeout = new Promise<undefined>((resolve) => {\n timer = setTimeout(() => resolve(undefined), ms);\n });\n return Promise.race([p, timeout]).finally(() => clearTimeout(timer));\n};\n\n/** Whether a cache entry is still within the TTL (server max-age aligned). */\nexport const isFeaturesFresh = (ageMs: number, ttlMs: number = DEFAULT_FEATURES_TTL_MS): boolean =>\n ageMs >= 0 && ageMs < ttlMs;\n\n/**\n * Read the cached flags for `key`, or `undefined` when absent/corrupt/timed-out. Returns the\n * value AND its age so a caller can apply a TTL (fresh) or ignore it (stale-while-error). Never\n * throws; never hangs past the read timeout. The stored value is re-parsed through\n * `parseWireFeatures`, so a partially-corrupt entry still degrades fail-open.\n */\nexport const readCachedFeatures = async (\n storage: WireOnboardingStorage,\n key: string,\n): Promise<CachedFeatures | undefined> => {\n let raw: string | null | undefined;\n try {\n raw = await withTimeout(storage.getItem(key), READ_TIMEOUT_MS);\n } catch {\n return undefined;\n }\n if (!raw) return undefined;\n try {\n const parsed: unknown = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\" && typeof (parsed as { ts?: unknown }).ts === \"number\") {\n const ts = (parsed as { ts: number }).ts;\n return {\n features: parseWireFeatures((parsed as { features?: unknown }).features),\n ageMs: Date.now() - ts,\n };\n }\n } catch {\n // Corrupt entry → treat as absent; the caller falls back to defaults / a fresh fetch.\n }\n return undefined;\n};\n\n/** Persist `{ features, ts }` under `key` — fire-and-forget, all errors swallowed. */\nexport const writeCachedFeatures = (\n storage: WireOnboardingStorage,\n key: string,\n features: WireFeatures,\n): void => {\n try {\n void storage\n .setItem(key, JSON.stringify({ features, ts: Date.now() }))\n .catch(() => {});\n } catch {\n // Best-effort: a failed write just means the next launch has no cache to fall back to.\n }\n};\n","/**\n * fetchWireFeatures — read the per-module kill switches from `GET {serverUrl}/v1/features`.\n *\n * Contract (server: chohra-med/wireai-onboarding-server, routers/onboarding.py):\n * GET {serverUrl}/v1/features\n * Authorization: Bearer {apiKey}\n * 200 → { coachmarks:{enabled}, showcase:{enabled}, onboarding:{mode}, review:{enabled},\n * announcements:{enabled} } (Cache-Control: private, max-age=300)\n *\n * THE CONTRACT — fail-open, never fail-dark. Unreachable, timed-out, unauthorized (401),\n * server-error (5xx), or malformed JSON all resolve to the LAST CACHED value, else the\n * all-on defaults. No throw ever reaches the host: this is timeout-capped and swallows every\n * error, exactly like the session's best-effort reads. A control-plane outage can only ever be\n * MORE permissive than the dashboard intends — it can never dark a tenant's activation.\n */\nimport { readCachedFeatures, featuresCacheKey, writeCachedFeatures } from \"./cache\";\nimport { defaultWireFeatures, parseWireFeatures } from \"./defaults\";\nimport type { WireFeatures, WireFeaturesConfig } from \"./types\";\nimport type { WireOnboardingStorage } from \"../session/persistedSession\";\n\nconst DEFAULT_TIMEOUT_MS = 4_000;\n\n/** The fail-open fallback: last cached value (any age — stale-while-error), else defaults. */\nconst failOpen = async (\n storage: WireOnboardingStorage | undefined,\n key: string,\n): Promise<WireFeatures> => {\n if (!storage) return defaultWireFeatures;\n const cached = await readCachedFeatures(storage, key);\n return cached?.features ?? defaultWireFeatures;\n};\n\n/** GET with a hard timeout via AbortController (falls back to no-abort where unsupported). */\nconst fetchWithTimeout = async (\n url: string,\n apiKey: string,\n timeoutMs: number,\n): Promise<Response> => {\n const controller =\n typeof AbortController !== \"undefined\" ? new AbortController() : undefined;\n const timer = setTimeout(() => controller?.abort(), timeoutMs);\n try {\n return await fetch(url, {\n method: \"GET\",\n headers: { Accept: \"application/json\", Authorization: `Bearer ${apiKey}` },\n signal: controller?.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n};\n\n/**\n * Resolve the tenant's feature flags. Attempts the network once; on ANY failure returns the\n * cached value (any age), else the all-on defaults. On success, persists the fresh value to the\n * cache (fire-and-forget) and returns it. Never throws.\n *\n * const flags = await fetchWireFeatures({ serverUrl, apiKey, appId, storage });\n * if (!flags.coachmarks.enabled) { /* skip the tour * / }\n */\nexport const fetchWireFeatures = async (\n config?: WireFeaturesConfig,\n): Promise<WireFeatures> => {\n const key = featuresCacheKey(config?.appId);\n const storage = config?.storage;\n\n // Nothing to fetch (no creds) → cache, else defaults. Lets a host mount the provider before\n // its config is ready without ever darking.\n if (!config?.serverUrl || !config?.apiKey) return failOpen(storage, key);\n\n const url = `${config.serverUrl.replace(/\\/$/, \"\")}/v1/features`;\n const timeoutMs = config.timeoutMs && config.timeoutMs > 0 ? config.timeoutMs : DEFAULT_TIMEOUT_MS;\n\n try {\n const res = await fetchWithTimeout(url, config.apiKey, timeoutMs);\n // 401 (bad/rotated key), 5xx, any non-2xx → last-known, never dark.\n if (!res || !res.ok) return failOpen(storage, key);\n const json: unknown = await res.json(); // throws on malformed body → caught below\n const features = parseWireFeatures(json);\n if (storage) writeCachedFeatures(storage, key, features);\n return features;\n } catch {\n // Timeout/abort, network error, missing fetch, or malformed JSON — all fail open.\n return failOpen(storage, key);\n }\n};\n","/**\n * useWireFeatures — resolve the tenant's feature flags for a component tree, once per mount.\n *\n * Returns the all-on defaults immediately (fail-open), then swaps to the fetched value when the\n * network resolves. When no `serverUrl`+`apiKey` is supplied it never fetches and just returns\n * defaults, so a host that never adopts flags sees ZERO behavior change and ZERO network calls.\n *\n * frequent_rules #11 (unstable inline props): the config is idiomatically passed inline\n * (`config={{ serverUrl, apiKey }}`), a fresh identity every render. We stabilize the fetch key\n * by VALUE so the effect fires once per real credential change, and we only swap the returned\n * flags object when its VALUES change — so a host that lists the returned flags in an effect dep\n * array settles in a bounded number of commits instead of looping (enforced by the canary suite).\n */\nimport { useEffect, useRef, useState } from \"react\";\n\nimport { defaultWireFeatures, featuresEqual } from \"./defaults\";\nimport { fetchWireFeatures } from \"./fetchWireFeatures\";\nimport type { WireFeatures, WireFeaturesConfig } from \"./types\";\n\n/** Hold a stable reference for a value while its CONTENT is unchanged (see reviews/equality.ts). */\nconst useStableValue = <T,>(value: T, isEqual: (a: T, b: T) => boolean): T => {\n const ref = useRef(value);\n if (!isEqual(ref.current, value)) ref.current = value;\n return ref.current;\n};\n\n/** The credential slice that actually drives a refetch — stabilized by value. */\nconst sameFetchKey = (a: WireFeaturesConfig | undefined, b: WireFeaturesConfig | undefined): boolean =>\n a?.serverUrl === b?.serverUrl && a?.apiKey === b?.apiKey && a?.appId === b?.appId;\n\nexport const useWireFeatures = (config?: WireFeaturesConfig): WireFeatures => {\n const stableConfig = useStableValue(config, sameFetchKey);\n const [flags, setFlags] = useState<WireFeatures>(defaultWireFeatures);\n\n const canFetch = !!stableConfig?.serverUrl && !!stableConfig?.apiKey;\n\n useEffect(() => {\n if (!canFetch) {\n // No creds → stay on the all-on defaults (and reset if creds were removed).\n setFlags((prev) => (featuresEqual(prev, defaultWireFeatures) ? prev : defaultWireFeatures));\n return;\n }\n let alive = true;\n // fetchWireFeatures never throws — the .then always runs with a fail-open value.\n void fetchWireFeatures(stableConfig).then((next) => {\n if (!alive) return;\n // Only adopt a NEW reference on a real value change, so a host effect keyed on the\n // returned flags doesn't loop (frequent_rules #11).\n setFlags((prev) => (featuresEqual(prev, next) ? prev : next));\n });\n return () => {\n alive = false;\n };\n }, [canFetch, stableConfig]);\n\n return flags;\n};\n","/**\n * WireFeaturesProvider + useResolvedFeatures — the OPTIONAL one-fetch-serves-all-modules layer.\n *\n * Mount `WireFeaturesProvider` once near your app root with the tenant `config` and it fetches the\n * flags ONCE; every gated surface below it (coachmarks / showcase / reviews) reads the same\n * resolved value from context instead of each fetching its own. It is entirely optional: with no\n * provider, every surface falls back to its own lazy fetch (when given a config) or to the all-on\n * defaults, so modules work with zero setup and a host that never adopts flags is unchanged.\n *\n * NOTE on the built npm subpaths: React context is shared across the kit's subpath entries in the\n * SOURCE-consumption path (the monorepo apps) and in this test suite — one module instance, one\n * context. If you consume the built `wireai-onboarding/*` subpaths as separate bundles, pass the\n * resolved flags (or a `featuresConfig`) to each surface explicitly instead of relying on context.\n */\nimport React, { createContext, useContext, useMemo } from \"react\";\n\nimport { defaultWireFeatures } from \"./defaults\";\nimport { useWireFeatures } from \"./useWireFeatures\";\nimport type { WireFeatures, WireFeaturesConfig } from \"./types\";\n\n/** null = no provider mounted (the signal `useResolvedFeatures` uses to fall back). */\nconst WireFeaturesContext = createContext<WireFeatures | null>(null);\n\nexport interface WireFeaturesProviderProps {\n /** Tenant creds (+ optional storage) to fetch the flags once for the whole tree. */\n config?: WireFeaturesConfig;\n /**\n * Pre-resolved flags to provide directly instead of fetching (e.g. you already have them, or\n * you drive them from your own remote-config). Takes precedence over `config`.\n */\n flags?: WireFeatures;\n children: React.ReactNode;\n}\n\n/**\n * Fetch (or accept) the flags once and broadcast them to descendants. The provider VALUE is\n * memoized (performance rule #6) so a fresh object each render doesn't re-render every consumer.\n */\nexport const WireFeaturesProvider: React.FC<WireFeaturesProviderProps> = ({\n config,\n flags,\n children,\n}) => {\n // Always call the hook (rules of hooks); when `flags` is supplied we pass no config so it\n // never fetches and the explicit flags win below.\n const fetched = useWireFeatures(flags ? undefined : config);\n const value = flags ?? fetched;\n const stable = useMemo(() => value, [value]);\n return <WireFeaturesContext.Provider value={stable}>{children}</WireFeaturesContext.Provider>;\n};\n\nWireFeaturesProvider.displayName = \"WireFeaturesProvider\";\n\n/** Raw context read — `null` when no `WireFeaturesProvider` is mounted above. */\nexport const useWireFeaturesContext = (): WireFeatures | null =>\n useContext(WireFeaturesContext);\n\n/** Options for `useResolvedFeatures`: an explicit override and/or a lazy-fetch config. */\nexport interface ResolveFeaturesOptions {\n /** Explicit flags — win over everything (a surface's own `features` prop). */\n flags?: WireFeatures;\n /** Lazy-fetch config, used ONLY when there is no explicit `flags` and no provider above. */\n config?: WireFeaturesConfig;\n}\n\n/**\n * The resolution every gated surface uses. Precedence:\n * 1. explicit `options.flags` (a surface's own prop) →\n * 2. the nearest `WireFeaturesProvider` (context) →\n * 3. a lazy fetch from `options.config` (serverUrl + apiKey) →\n * 4. the all-on defaults.\n * Always fail-open, always a stable reference for a given value (so it's safe in effect deps).\n */\nexport const useResolvedFeatures = (options?: ResolveFeaturesOptions): WireFeatures => {\n const ctx = useWireFeaturesContext();\n // Only lazily fetch when nothing else provides the flags — otherwise pass no config so the\n // hook stays inert (defaults, no network). The hook is always called (rules of hooks).\n const shouldFetch = !options?.flags && ctx == null;\n const fetched = useWireFeatures(shouldFetch ? options?.config : undefined);\n return options?.flags ?? ctx ?? fetched ?? defaultWireFeatures;\n};\n","/**\n * equality.ts — tiny, dependency-free value-equality helpers used to STABILIZE\n * references across renders.\n *\n * WHY this exists: real integrators pass inline objects — `config={{ id: 'x' }}`,\n * `decision={{ fire: true }}` — whose IDENTITY changes every render even though\n * their VALUES do not. If the review hook let those unstable references flow into\n * a `useMemo`/`useEffect` dependency list (or handed a fresh verdict object back\n * every render), a host that depends on the returned value would re-run its own\n * effect forever → \"Maximum update depth exceeded\". A library must tolerate inline\n * props without looping; these helpers are how the hook holds a stable reference\n * whenever the underlying values are unchanged.\n *\n * Pure (no React import) so it runs under the kit's node:test runner.\n */\nimport type { ReviewDecision } from \"./types\";\n\n/**\n * One-level value-equality of two plain objects (or primitives). Same-reference is\n * equal; two objects are equal when they have the same keys and each value is\n * `Object.is`-equal. Good enough for the flat `config` / `decision` shapes the gate\n * accepts (all leaf values are primitives).\n */\nexport const shallowEqual = (a: unknown, b: unknown): boolean => {\n if (Object.is(a, b)) return true;\n if (\n a == null ||\n b == null ||\n typeof a !== \"object\" ||\n typeof b !== \"object\"\n ) {\n return false;\n }\n const ak = Object.keys(a as Record<string, unknown>);\n const bk = Object.keys(b as Record<string, unknown>);\n if (ak.length !== bk.length) return false;\n for (const k of ak) {\n if (!Object.is((a as Record<string, unknown>)[k], (b as Record<string, unknown>)[k])) {\n return false;\n }\n }\n return true;\n};\n\n/**\n * Structural equality of two review verdicts by their contract fields (`fire`,\n * `reason`). A `null`/`undefined` verdict equals only another absent one. Used to\n * decide whether the hook can keep returning the SAME verdict object (so a host\n * that lists `controller.decision` in an effect dep array never loops).\n */\nexport const sameDecision = (a?: ReviewDecision | null, b?: ReviewDecision | null): boolean => {\n if (a == null || b == null) return a == b;\n return a.fire === b.fire && a.reason === b.reason;\n};\n","/**\n * Runtime helpers for the review gate: the `wire_review_*` gate keys and small\n * typed storage accessors. The gate reuses the SAME injected sync storage + global\n * testing flag as the coachmark engine (see coachmarks/runtime), so a host that\n * mounted CoachmarkProvider gets review gating for free — no second storage to wire.\n */\nimport type { CoachmarkStorage } from \"../coachmarks/types\";\nimport { getCoachmarkStorage } from \"../coachmarks/runtime\";\n\n/** Once-gate key. Keyed by app version when `oncePerVersion` is on, so a new release re-enables. */\nexport const reviewSeenKey = (id: string, version?: string): string =>\n version ? `wire_review_${id}_${version}_seen` : `wire_review_${id}_seen`;\n\n/** Last-shown timestamp key (epoch ms), for the cooldown rule. */\nexport const reviewLastShownKey = (id: string): string => `wire_review_${id}_last`;\n\n/** Session-count key, incremented once per gate mount, for the min-sessions rule. */\nexport const reviewSessionsKey = (id: string): string => `wire_review_${id}_sessions`;\n\n/** Resolve the storage to use: an explicit override, else the provider-injected singleton. */\nexport const resolveStorage = (\n override?: CoachmarkStorage | null,\n): CoachmarkStorage | null => override ?? getCoachmarkStorage();\n\n/** Read an integer from sync storage (0 on a missing/unparseable/throwing read). */\nexport const readInt = (storage: CoachmarkStorage | null, key: string): number => {\n if (!storage) return 0;\n try {\n const raw = storage.getItem(key);\n const n = raw == null ? 0 : parseInt(raw, 10);\n return Number.isFinite(n) ? n : 0;\n } catch {\n return 0;\n }\n};\n\n/** Write an integer to sync storage. Best-effort — a throwing adapter never breaks the gate. */\nexport const writeInt = (storage: CoachmarkStorage | null, key: string, value: number): void => {\n if (!storage) return;\n try {\n storage.setItem(key, String(value));\n } catch {\n // Best-effort.\n }\n};\n","/**\n * useReviewGate — owns WHEN the review gate shows.\n *\n * v1 decision = local rules from `config` (enabled / minSessions / minEvents / cooldownDays\n * / oncePerVersion), evaluated against the same injected sync storage as the coachmark\n * engine (`wire_review_*` keys). The AI seam: pass a server-provided `decision` and it\n * OVERRIDES the local rules (see decideReview). The whole cost of being AI-ready is passing\n * that object straight through.\n *\n * Fallback chain (matches the server design): (1) a server `decision` wins immediately;\n * (2) no decision → after `timeoutFallbackMs` the local rules take over; (3) the once-gate\n * (`wire_review_<id>_seen`) always applies locally, so a server bug can never spam the prompt.\n * The global `isTestingCoachmark` flag force-shows the gate for QA replay.\n */\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\nimport { hasSeenGate, isCoachmarkTesting, markSeenGate } from \"../coachmarks/runtime\";\nimport { useResolvedFeatures } from \"../features/WireFeaturesProvider\";\nimport { decideReview, evaluateGate, resolveRules } from \"./decision\";\nimport { sameDecision, shallowEqual } from \"./equality\";\nimport {\n readInt,\n resolveStorage,\n reviewLastShownKey,\n reviewSeenKey,\n reviewSessionsKey,\n writeInt,\n} from \"./runtime\";\nimport type {\n ReviewDecision,\n ReviewGateController,\n UseReviewGateOptions,\n} from \"./types\";\n\n/**\n * Hold a stable reference for a value while its CONTENT is unchanged. Integrators\n * pass inline `config` / `decision` objects (idiomatic React) whose identity flips\n * every render; feeding those straight into the deps below would recompute the\n * verdict — and mint a fresh verdict object — on every render, which loops any host\n * that lists the returned verdict in its own effect. Comparing by value here keeps\n * the reference (and everything derived from it) stable across identity-only churn.\n */\nconst useStableValue = <T,>(value: T, isEqual: (a: T, b: T) => boolean): T => {\n const ref = useRef(value);\n if (!isEqual(ref.current, value)) ref.current = value;\n return ref.current;\n};\n\nexport const useReviewGate = ({\n config: configProp,\n decision: decisionProp,\n events = 0,\n storage,\n isTesting,\n features,\n featuresConfig,\n}: UseReviewGateOptions): ReviewGateController => {\n // Tolerate inline-identity props: only adopt a new reference on a real value change.\n const config = useStableValue(configProp, shallowEqual);\n const decision = useStableValue(\n decisionProp,\n (a, b) => sameDecision(a, b),\n );\n\n // The review MASTER switch. Disabled → the gate never fires, composing as an extra AND over\n // the local rules + the server decision seam below (fail-open: defaults to enabled).\n const featureEnabled = useResolvedFeatures({ flags: features, config: featuresConfig }).review.enabled;\n\n const testing = isTesting ?? isCoachmarkTesting();\n const seenKey = reviewSeenKey(config.id, config.oncePerVersion === false ? undefined : config.appVersion);\n const lastKey = reviewLastShownKey(config.id);\n const sessionsKey = reviewSessionsKey(config.id);\n\n // Read (and bump) the session counter ONCE per mount: this mount is a new session.\n const sessions = useState(() => {\n const store = resolveStorage(storage);\n const next = readInt(store, sessionsKey) + 1;\n writeInt(store, sessionsKey, next);\n return next;\n })[0];\n\n // Gate the local rules behind an optional client-side timeout, so a reachable server\n // gets a window to answer first. A present `decision` bypasses the wait entirely.\n const hasServerDecision = decision != null;\n const timeoutMs = config.timeoutFallbackMs;\n const waits = !hasServerDecision && timeoutMs != null && timeoutMs > 0;\n const [elapsed, setElapsed] = useState(!waits);\n useEffect(() => {\n if (!waits) {\n setElapsed(true);\n return;\n }\n const timer = setTimeout(() => setElapsed(true), timeoutMs as number);\n return () => clearTimeout(timer);\n }, [waits, timeoutMs]);\n\n const { visible, verdict } = useMemo(() => {\n // Master kill switch wins over everything, including QA replay: a tenant that turned reviews\n // OFF must never see the gate, whatever the rules, the server decision, or testing say.\n if (!featureEnabled) return { visible: false, verdict: { fire: false, reason: \"feature_disabled\" } as ReviewDecision };\n\n // QA replay: force-show, bypassing rules + gates (safe: only via __DEV__ && bool).\n if (testing) return { visible: true, verdict: { fire: true, reason: \"testing\" } as ReviewDecision };\n\n const store = resolveStorage(storage);\n const local = evaluateGate(resolveRules(config), {\n sessions,\n events,\n lastShownAt: readInt(store, lastKey) || null,\n seen: hasSeenGate(seenKey, storage, isTesting),\n now: Date.now(),\n });\n const resolved = decideReview(local, decision);\n // (3) the once-gate always wins locally, even over a server \"fire\".\n const alreadySeen = hasSeenGate(seenKey, storage, isTesting);\n const ready = hasServerDecision || elapsed;\n return { visible: ready && resolved.fire && !alreadySeen, verdict: resolved };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [featureEnabled, testing, config, decision, events, sessions, elapsed, hasServerDecision, seenKey, lastKey, storage, isTesting]);\n\n // Stable callbacks + a stable controller object, so a host can safely list any of\n // them in its own effect deps without the effect re-firing every render.\n const shownRef = useRef(false);\n const markShown = useCallback(() => {\n if (shownRef.current) return;\n shownRef.current = true;\n writeInt(resolveStorage(storage), lastKey, Date.now());\n }, [storage, lastKey]);\n const markResolved = useCallback(() => {\n markSeenGate(seenKey, storage, isTesting);\n }, [seenKey, storage, isTesting]);\n\n return useMemo<ReviewGateController>(\n () => ({ visible, decision: verdict, markShown, markResolved }),\n [visible, verdict, markShown, markResolved],\n );\n};\n"]}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import React__default from 'react';
|
|
2
|
+
import { ImageSourcePropType } from 'react-native';
|
|
3
|
+
import { G as GestureKind, C as CoachmarkStorage } from '../types-GL_hQ0TN.mjs';
|
|
4
|
+
import { O as OnboardingTheme } from '../types-BKfpdZzX.mjs';
|
|
5
|
+
import { W as WireFeatures, a as WireFeaturesConfig } from '../types-CMuOexw0.mjs';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* One slide in the pre-onboarding feature showcase (the "app intro"). Maps onto
|
|
9
|
+
* a `@blazejkustra/react-native-onboarding` step, with an optional perf-tuned
|
|
10
|
+
* GestureHint overlaid on top. An `image` is strongly recommended — the
|
|
11
|
+
* underlying package renders an image area per slide; slides without one fall
|
|
12
|
+
* back to a transparent placeholder so nothing crashes.
|
|
13
|
+
*/
|
|
14
|
+
interface ShowcaseSlide {
|
|
15
|
+
id: string;
|
|
16
|
+
title: string;
|
|
17
|
+
description: string;
|
|
18
|
+
image?: ImageSourcePropType;
|
|
19
|
+
/** Where the image sits relative to the copy. Default `top`. */
|
|
20
|
+
imagePosition?: "top" | "bottom";
|
|
21
|
+
/** Per-slide primary button label; falls back to the showcase default. */
|
|
22
|
+
buttonLabel?: string;
|
|
23
|
+
/** Optional animated hand overlaid on the slide (the perf-tuned GestureHint). */
|
|
24
|
+
gesture?: GestureKind;
|
|
25
|
+
}
|
|
26
|
+
/** Optional intro panel shown before the slides begin. */
|
|
27
|
+
interface ShowcaseIntroPanel {
|
|
28
|
+
title?: string;
|
|
29
|
+
subtitle?: string;
|
|
30
|
+
/** Start-button label (required by the underlying package). */
|
|
31
|
+
button: string;
|
|
32
|
+
image?: ImageSourcePropType;
|
|
33
|
+
}
|
|
34
|
+
/** The whole declarative feature-showcase an app supplies. */
|
|
35
|
+
interface ShowcaseConfig {
|
|
36
|
+
/** Gate key — persisted as `wire_showcase_<id>_seen`. */
|
|
37
|
+
id: string;
|
|
38
|
+
introPanel?: ShowcaseIntroPanel;
|
|
39
|
+
slides: ShowcaseSlide[];
|
|
40
|
+
}
|
|
41
|
+
interface FeatureShowcaseProps {
|
|
42
|
+
config: ShowcaseConfig;
|
|
43
|
+
/** Called when the showcase completes, is skipped, or was already seen. */
|
|
44
|
+
onDone: () => void;
|
|
45
|
+
/** Partial theme merged over the active onboarding theme for the showcase colors. */
|
|
46
|
+
theme?: Partial<OnboardingTheme>;
|
|
47
|
+
/** Accent override (buttons + emphasis). Defaults to the theme's `primary`. */
|
|
48
|
+
accentColor?: string;
|
|
49
|
+
/** Default primary button label when a slide/intro omits one. Default "Next". */
|
|
50
|
+
buttonLabel?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Sync gate storage. Defaults to the storage injected by CoachmarkProvider, so
|
|
53
|
+
* you usually don't pass this — the showcase gates through the same store as
|
|
54
|
+
* the coachmark tours.
|
|
55
|
+
*/
|
|
56
|
+
storage?: CoachmarkStorage | null;
|
|
57
|
+
/** Testing override; defaults to the global `isTestingCoachmark` flag. */
|
|
58
|
+
isTesting?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* OPTIONAL feature-flag wiring. When `flags.showcase.enabled` is false, the showcase renders
|
|
61
|
+
* NOTHING and calls `onDone()` from an effect so the host's navigation flow continues exactly
|
|
62
|
+
* as if it had completed — it can never strand a flow waiting on a screen that won't appear. It
|
|
63
|
+
* does NOT write the seen gate, so re-enabling replays it. Provide pre-resolved `features`, OR a
|
|
64
|
+
* `featuresConfig` to lazily fetch once, OR mount a `WireFeaturesProvider` above (context is
|
|
65
|
+
* read automatically). Omit all three and the showcase stays on (fail-open).
|
|
66
|
+
*/
|
|
67
|
+
features?: WireFeatures;
|
|
68
|
+
/** Lazy-fetch config for the flags, used when `features` is absent and no provider is above. */
|
|
69
|
+
featuresConfig?: WireFeaturesConfig;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* FeatureShowcase — the pre-onboarding "app intro" slides.
|
|
74
|
+
*
|
|
75
|
+
* Wraps `@blazejkustra/react-native-onboarding`: the app supplies a declarative
|
|
76
|
+
* `ShowcaseConfig` (slides + optional intro panel) and the kit maps it to the
|
|
77
|
+
* package's API, bakes in the Wire onboarding theme colors, overlays the
|
|
78
|
+
* perf-tuned GestureHint on any slide that asks for one, and gates the whole
|
|
79
|
+
* thing once through the same sync storage as the coachmark tours
|
|
80
|
+
* (`wire_showcase_<id>_seen`; the global testing flag bypasses it).
|
|
81
|
+
*
|
|
82
|
+
* When the gate says "seen", it renders nothing and calls `onDone` from an
|
|
83
|
+
* effect (never during render).
|
|
84
|
+
*/
|
|
85
|
+
declare const FeatureShowcase: React__default.FC<FeatureShowcaseProps>;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* selectShowcaseSlides — the value-bridge selection seam for the feature showcase.
|
|
89
|
+
*
|
|
90
|
+
* The recommended shape (v0.2.1+) renders the showcase AS A PERSONALIZED VALUE
|
|
91
|
+
* BRIDGE right AFTER the AI onboarding completes: the user just told you what they
|
|
92
|
+
* want in `result.answers`, so you show only the 2-3 slides that answer it, in the
|
|
93
|
+
* most-relevant-first order — "you said you want X → here's how the app does X"
|
|
94
|
+
* (the Duolingo / Headspace terminal-moment pattern). The host declares the full
|
|
95
|
+
* slide catalog once and passes an ordered id list computed from the answers:
|
|
96
|
+
*
|
|
97
|
+
* // Full deck, declared order (pre-onboarding placement, or thin answers):
|
|
98
|
+
* const slides = selectShowcaseSlides(catalog);
|
|
99
|
+
*
|
|
100
|
+
* // Personalized bridge, post-onboarding: subset + order chosen from answers:
|
|
101
|
+
* const slides = selectShowcaseSlides(catalog, idsFromAnswers(result.answers));
|
|
102
|
+
*
|
|
103
|
+
* Contract (identical to `selectTourSteps`):
|
|
104
|
+
* - `selection` absent → returns the catalog unchanged (identity / declared order).
|
|
105
|
+
* - `selection` present → returns only catalog entries whose `id` appears in
|
|
106
|
+
* `selection`, ordered by `selection` (unknown ids are skipped; duplicates in
|
|
107
|
+
* `selection` are ignored after the first match).
|
|
108
|
+
*
|
|
109
|
+
* v2 SERVER SEAM (document-only, not built here): the onboarding server's final
|
|
110
|
+
* turn will later return an ordered `showcase: string[]` slide-key list chosen
|
|
111
|
+
* server-side from the same answers. The host passes it straight in as `selection`
|
|
112
|
+
* — same catalog, same contract, AI is a drop-in. Old kits that don't read the new
|
|
113
|
+
* field simply ignore it, so shipping the extra field is backward-safe.
|
|
114
|
+
*/
|
|
115
|
+
declare const selectShowcaseSlides: <T extends {
|
|
116
|
+
id: string;
|
|
117
|
+
}>(catalog: T[], selection?: string[]) => T[];
|
|
118
|
+
|
|
119
|
+
export { FeatureShowcase, type FeatureShowcaseProps, type ShowcaseConfig, type ShowcaseIntroPanel, type ShowcaseSlide, selectShowcaseSlides };
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import React__default from 'react';
|
|
2
|
+
import { ImageSourcePropType } from 'react-native';
|
|
3
|
+
import { G as GestureKind, C as CoachmarkStorage } from '../types-GL_hQ0TN.js';
|
|
4
|
+
import { O as OnboardingTheme } from '../types-BKfpdZzX.js';
|
|
5
|
+
import { W as WireFeatures, a as WireFeaturesConfig } from '../types-CMuOexw0.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* One slide in the pre-onboarding feature showcase (the "app intro"). Maps onto
|
|
9
|
+
* a `@blazejkustra/react-native-onboarding` step, with an optional perf-tuned
|
|
10
|
+
* GestureHint overlaid on top. An `image` is strongly recommended — the
|
|
11
|
+
* underlying package renders an image area per slide; slides without one fall
|
|
12
|
+
* back to a transparent placeholder so nothing crashes.
|
|
13
|
+
*/
|
|
14
|
+
interface ShowcaseSlide {
|
|
15
|
+
id: string;
|
|
16
|
+
title: string;
|
|
17
|
+
description: string;
|
|
18
|
+
image?: ImageSourcePropType;
|
|
19
|
+
/** Where the image sits relative to the copy. Default `top`. */
|
|
20
|
+
imagePosition?: "top" | "bottom";
|
|
21
|
+
/** Per-slide primary button label; falls back to the showcase default. */
|
|
22
|
+
buttonLabel?: string;
|
|
23
|
+
/** Optional animated hand overlaid on the slide (the perf-tuned GestureHint). */
|
|
24
|
+
gesture?: GestureKind;
|
|
25
|
+
}
|
|
26
|
+
/** Optional intro panel shown before the slides begin. */
|
|
27
|
+
interface ShowcaseIntroPanel {
|
|
28
|
+
title?: string;
|
|
29
|
+
subtitle?: string;
|
|
30
|
+
/** Start-button label (required by the underlying package). */
|
|
31
|
+
button: string;
|
|
32
|
+
image?: ImageSourcePropType;
|
|
33
|
+
}
|
|
34
|
+
/** The whole declarative feature-showcase an app supplies. */
|
|
35
|
+
interface ShowcaseConfig {
|
|
36
|
+
/** Gate key — persisted as `wire_showcase_<id>_seen`. */
|
|
37
|
+
id: string;
|
|
38
|
+
introPanel?: ShowcaseIntroPanel;
|
|
39
|
+
slides: ShowcaseSlide[];
|
|
40
|
+
}
|
|
41
|
+
interface FeatureShowcaseProps {
|
|
42
|
+
config: ShowcaseConfig;
|
|
43
|
+
/** Called when the showcase completes, is skipped, or was already seen. */
|
|
44
|
+
onDone: () => void;
|
|
45
|
+
/** Partial theme merged over the active onboarding theme for the showcase colors. */
|
|
46
|
+
theme?: Partial<OnboardingTheme>;
|
|
47
|
+
/** Accent override (buttons + emphasis). Defaults to the theme's `primary`. */
|
|
48
|
+
accentColor?: string;
|
|
49
|
+
/** Default primary button label when a slide/intro omits one. Default "Next". */
|
|
50
|
+
buttonLabel?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Sync gate storage. Defaults to the storage injected by CoachmarkProvider, so
|
|
53
|
+
* you usually don't pass this — the showcase gates through the same store as
|
|
54
|
+
* the coachmark tours.
|
|
55
|
+
*/
|
|
56
|
+
storage?: CoachmarkStorage | null;
|
|
57
|
+
/** Testing override; defaults to the global `isTestingCoachmark` flag. */
|
|
58
|
+
isTesting?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* OPTIONAL feature-flag wiring. When `flags.showcase.enabled` is false, the showcase renders
|
|
61
|
+
* NOTHING and calls `onDone()` from an effect so the host's navigation flow continues exactly
|
|
62
|
+
* as if it had completed — it can never strand a flow waiting on a screen that won't appear. It
|
|
63
|
+
* does NOT write the seen gate, so re-enabling replays it. Provide pre-resolved `features`, OR a
|
|
64
|
+
* `featuresConfig` to lazily fetch once, OR mount a `WireFeaturesProvider` above (context is
|
|
65
|
+
* read automatically). Omit all three and the showcase stays on (fail-open).
|
|
66
|
+
*/
|
|
67
|
+
features?: WireFeatures;
|
|
68
|
+
/** Lazy-fetch config for the flags, used when `features` is absent and no provider is above. */
|
|
69
|
+
featuresConfig?: WireFeaturesConfig;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* FeatureShowcase — the pre-onboarding "app intro" slides.
|
|
74
|
+
*
|
|
75
|
+
* Wraps `@blazejkustra/react-native-onboarding`: the app supplies a declarative
|
|
76
|
+
* `ShowcaseConfig` (slides + optional intro panel) and the kit maps it to the
|
|
77
|
+
* package's API, bakes in the Wire onboarding theme colors, overlays the
|
|
78
|
+
* perf-tuned GestureHint on any slide that asks for one, and gates the whole
|
|
79
|
+
* thing once through the same sync storage as the coachmark tours
|
|
80
|
+
* (`wire_showcase_<id>_seen`; the global testing flag bypasses it).
|
|
81
|
+
*
|
|
82
|
+
* When the gate says "seen", it renders nothing and calls `onDone` from an
|
|
83
|
+
* effect (never during render).
|
|
84
|
+
*/
|
|
85
|
+
declare const FeatureShowcase: React__default.FC<FeatureShowcaseProps>;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* selectShowcaseSlides — the value-bridge selection seam for the feature showcase.
|
|
89
|
+
*
|
|
90
|
+
* The recommended shape (v0.2.1+) renders the showcase AS A PERSONALIZED VALUE
|
|
91
|
+
* BRIDGE right AFTER the AI onboarding completes: the user just told you what they
|
|
92
|
+
* want in `result.answers`, so you show only the 2-3 slides that answer it, in the
|
|
93
|
+
* most-relevant-first order — "you said you want X → here's how the app does X"
|
|
94
|
+
* (the Duolingo / Headspace terminal-moment pattern). The host declares the full
|
|
95
|
+
* slide catalog once and passes an ordered id list computed from the answers:
|
|
96
|
+
*
|
|
97
|
+
* // Full deck, declared order (pre-onboarding placement, or thin answers):
|
|
98
|
+
* const slides = selectShowcaseSlides(catalog);
|
|
99
|
+
*
|
|
100
|
+
* // Personalized bridge, post-onboarding: subset + order chosen from answers:
|
|
101
|
+
* const slides = selectShowcaseSlides(catalog, idsFromAnswers(result.answers));
|
|
102
|
+
*
|
|
103
|
+
* Contract (identical to `selectTourSteps`):
|
|
104
|
+
* - `selection` absent → returns the catalog unchanged (identity / declared order).
|
|
105
|
+
* - `selection` present → returns only catalog entries whose `id` appears in
|
|
106
|
+
* `selection`, ordered by `selection` (unknown ids are skipped; duplicates in
|
|
107
|
+
* `selection` are ignored after the first match).
|
|
108
|
+
*
|
|
109
|
+
* v2 SERVER SEAM (document-only, not built here): the onboarding server's final
|
|
110
|
+
* turn will later return an ordered `showcase: string[]` slide-key list chosen
|
|
111
|
+
* server-side from the same answers. The host passes it straight in as `selection`
|
|
112
|
+
* — same catalog, same contract, AI is a drop-in. Old kits that don't read the new
|
|
113
|
+
* field simply ignore it, so shipping the extra field is backward-safe.
|
|
114
|
+
*/
|
|
115
|
+
declare const selectShowcaseSlides: <T extends {
|
|
116
|
+
id: string;
|
|
117
|
+
}>(catalog: T[], selection?: string[]) => T[];
|
|
118
|
+
|
|
119
|
+
export { FeatureShowcase, type FeatureShowcaseProps, type ShowcaseConfig, type ShowcaseIntroPanel, type ShowcaseSlide, selectShowcaseSlides };
|