@wireai/activation 0.12.2 → 0.13.2
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 +3 -1
- package/CHANGELOG.md +259 -1
- package/README.md +87 -3
- package/dist/analytics/index.d.mts +2 -2
- package/dist/analytics/index.d.ts +2 -2
- package/dist/analytics/index.js +174 -36
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +174 -37
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/{currentSession-BlCeDP0f.d.mts → currentSession-ClkLjcJ0.d.mts} +456 -19
- package/dist/{currentSession-BxEB37xt.d.ts → currentSession-DOVZEWJl.d.ts} +456 -19
- package/dist/index.d.mts +197 -16
- package/dist/index.d.ts +197 -16
- package/dist/index.js +1056 -390
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +836 -193
- package/dist/index.mjs.map +1 -1
- package/dist/questionnaire/index.js.map +1 -1
- package/dist/questionnaire/index.mjs.map +1 -1
- package/dist/reviews/index.js +20 -7
- package/dist/reviews/index.js.map +1 -1
- package/dist/reviews/index.mjs +20 -7
- package/dist/reviews/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/OnboardingFlow.tsx +141 -3
- package/src/WireOnboarding.tsx +178 -34
- package/src/activation/wireActivation.ts +13 -7
- package/src/analytics/analyticsEvent.ts +16 -1
- package/src/analytics/analyticsFacade.ts +11 -10
- package/src/analytics/currentSession.ts +6 -20
- package/src/analytics/eventQueue.ts +71 -1
- package/src/analytics/index.ts +1 -1
- package/src/analytics/reportClientEvent.ts +157 -38
- package/src/cards/PermissionCard.tsx +438 -0
- package/src/cards/index.ts +7 -0
- package/src/config/wireConfigFromEnv.ts +1 -10
- package/src/context/deviceId.ts +77 -16
- package/src/context/userContext.ts +4 -15
- package/src/identity/identityRecord.ts +123 -0
- package/src/identity/userIdentity.ts +45 -9
- package/src/illustrations/defaultIllustrations.tsx +44 -3
- package/src/index.ts +44 -4
- package/src/permissions/index.ts +64 -0
- package/src/permissions/permissionCopy.ts +87 -0
- package/src/permissions/permissionEvents.ts +76 -0
- package/src/permissions/permissionMemory.ts +88 -0
- package/src/permissions/placement.ts +88 -0
- package/src/permissions/types.ts +131 -0
- package/src/session/persistedSession.ts +10 -3
- package/src/session-analytics/useLifecycleEvents.ts +10 -1
- package/src/types.ts +77 -1
- package/src/utils/deriveAnswers.ts +6 -2
- package/src/utils/readProgress.ts +4 -0
- package/src/utils/warnInDev.ts +33 -0
- package/src/components/DoneBlock.tsx +0 -37
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var reactNative = require('react-native');
|
|
4
|
-
var
|
|
4
|
+
var React19 = require('react');
|
|
5
5
|
var wireaiRn = require('wireai-rn');
|
|
6
6
|
var jsxRuntime = require('react/jsx-runtime');
|
|
7
7
|
var reactNativeSafeAreaContext = require('react-native-safe-area-context');
|
|
@@ -9,7 +9,7 @@ var zod = require('zod');
|
|
|
9
9
|
|
|
10
10
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
11
|
|
|
12
|
-
var
|
|
12
|
+
var React19__default = /*#__PURE__*/_interopDefault(React19);
|
|
13
13
|
|
|
14
14
|
var __defProp = Object.defineProperty;
|
|
15
15
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -464,33 +464,33 @@ var mergeTheme = (override) => {
|
|
|
464
464
|
button: { ...defaultOnboardingTheme.button, ...override.button }
|
|
465
465
|
};
|
|
466
466
|
};
|
|
467
|
-
var OnboardingThemeContext =
|
|
467
|
+
var OnboardingThemeContext = React19.createContext(null);
|
|
468
468
|
var OnboardingThemeProvider = ({
|
|
469
469
|
theme,
|
|
470
470
|
children
|
|
471
471
|
}) => {
|
|
472
|
-
const resolved =
|
|
472
|
+
const resolved = React19.useMemo(() => mergeTheme(theme), [theme]);
|
|
473
473
|
return /* @__PURE__ */ jsxRuntime.jsx(OnboardingThemeContext.Provider, { value: resolved, children });
|
|
474
474
|
};
|
|
475
475
|
var useOnboardingTheme = () => {
|
|
476
|
-
const ctx =
|
|
476
|
+
const ctx = React19.useContext(OnboardingThemeContext);
|
|
477
477
|
return ctx != null ? ctx : mergeTheme();
|
|
478
478
|
};
|
|
479
479
|
var EMPTY_REGISTRY = {};
|
|
480
|
-
var IllustrationContext =
|
|
480
|
+
var IllustrationContext = React19.createContext(EMPTY_REGISTRY);
|
|
481
481
|
var IllustrationProvider = ({
|
|
482
482
|
registry,
|
|
483
483
|
children
|
|
484
484
|
}) => {
|
|
485
|
-
const value =
|
|
485
|
+
const value = React19.useMemo(() => registry != null ? registry : EMPTY_REGISTRY, [registry]);
|
|
486
486
|
return /* @__PURE__ */ jsxRuntime.jsx(IllustrationContext.Provider, { value, children });
|
|
487
487
|
};
|
|
488
488
|
var useIllustration = (name2) => {
|
|
489
|
-
const registry =
|
|
489
|
+
const registry = React19.useContext(IllustrationContext);
|
|
490
490
|
if (!name2) return void 0;
|
|
491
491
|
return registry[name2];
|
|
492
492
|
};
|
|
493
|
-
var
|
|
493
|
+
var Disc = ({ children }) => {
|
|
494
494
|
const t = useOnboardingTheme();
|
|
495
495
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
496
496
|
reactNative.View,
|
|
@@ -499,11 +499,23 @@ var Glyph = ({ children }) => {
|
|
|
499
499
|
styles.disc,
|
|
500
500
|
{ backgroundColor: t.colors.primarySoft, borderRadius: t.radius.full }
|
|
501
501
|
],
|
|
502
|
-
children
|
|
502
|
+
children
|
|
503
503
|
}
|
|
504
504
|
);
|
|
505
505
|
};
|
|
506
|
+
var Glyph = ({ children }) => {
|
|
507
|
+
const t = useOnboardingTheme();
|
|
508
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Disc, { children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles.glyphText, { color: t.colors.primary }], allowFontScaling: false, children }) });
|
|
509
|
+
};
|
|
506
510
|
var MomentumGlyph = () => /* @__PURE__ */ jsxRuntime.jsx(Glyph, { children: "\u2197" });
|
|
511
|
+
var NotificationsGlyph = () => {
|
|
512
|
+
const t = useOnboardingTheme();
|
|
513
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Disc, { children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.bell, children: [
|
|
514
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles.bellBody, { backgroundColor: t.colors.primary }] }),
|
|
515
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles.bellRim, { backgroundColor: t.colors.primary }] }),
|
|
516
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles.bellClapper, { backgroundColor: t.colors.primary }] })
|
|
517
|
+
] }) });
|
|
518
|
+
};
|
|
507
519
|
var BeforeAfterGlyph = () => {
|
|
508
520
|
const t = useOnboardingTheme();
|
|
509
521
|
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.row, children: [
|
|
@@ -530,7 +542,8 @@ var BeforeAfterGlyph = () => {
|
|
|
530
542
|
};
|
|
531
543
|
var defaultIllustrations = {
|
|
532
544
|
momentum: /* @__PURE__ */ jsxRuntime.jsx(MomentumGlyph, {}),
|
|
533
|
-
"before-after": /* @__PURE__ */ jsxRuntime.jsx(BeforeAfterGlyph, {})
|
|
545
|
+
"before-after": /* @__PURE__ */ jsxRuntime.jsx(BeforeAfterGlyph, {}),
|
|
546
|
+
notifications: /* @__PURE__ */ jsxRuntime.jsx(NotificationsGlyph, {})
|
|
534
547
|
};
|
|
535
548
|
var styles = reactNative.StyleSheet.create({
|
|
536
549
|
disc: {
|
|
@@ -558,19 +571,24 @@ var styles = reactNative.StyleSheet.create({
|
|
|
558
571
|
arrow: {
|
|
559
572
|
fontSize: 28,
|
|
560
573
|
fontWeight: "700"
|
|
561
|
-
}
|
|
574
|
+
},
|
|
575
|
+
// The bell: a domed body over a wider rim, with the clapper hanging below it.
|
|
576
|
+
bell: { alignItems: "center", justifyContent: "center" },
|
|
577
|
+
bellBody: { width: 34, height: 30, borderTopLeftRadius: 17, borderTopRightRadius: 17 },
|
|
578
|
+
bellRim: { width: 44, height: 5, borderRadius: 3, marginTop: 2 },
|
|
579
|
+
bellClapper: { width: 9, height: 9, borderRadius: 5, marginTop: 3 }
|
|
562
580
|
});
|
|
563
581
|
var EMPTY_REGISTRY2 = {};
|
|
564
|
-
var IconContext =
|
|
582
|
+
var IconContext = React19.createContext(EMPTY_REGISTRY2);
|
|
565
583
|
var IconRegistryProvider = ({
|
|
566
584
|
registry,
|
|
567
585
|
children
|
|
568
586
|
}) => {
|
|
569
|
-
const value =
|
|
587
|
+
const value = React19.useMemo(() => registry != null ? registry : EMPTY_REGISTRY2, [registry]);
|
|
570
588
|
return /* @__PURE__ */ jsxRuntime.jsx(IconContext.Provider, { value, children });
|
|
571
589
|
};
|
|
572
590
|
var useHostIcon = (name2) => {
|
|
573
|
-
const registry =
|
|
591
|
+
const registry = React19.useContext(IconContext);
|
|
574
592
|
if (!name2) return void 0;
|
|
575
593
|
if (!Object.prototype.hasOwnProperty.call(registry, name2)) return void 0;
|
|
576
594
|
return registry[name2];
|
|
@@ -729,8 +747,8 @@ var stateChangeDuration = (reduced, durationMs) => reduced ? 0 : durationMs;
|
|
|
729
747
|
var loopEnabled = (reduced) => !reduced;
|
|
730
748
|
var lastKnown = false;
|
|
731
749
|
var useReducedMotion = () => {
|
|
732
|
-
const [reduced, setReduced] =
|
|
733
|
-
|
|
750
|
+
const [reduced, setReduced] = React19.useState(lastKnown);
|
|
751
|
+
React19.useEffect(() => {
|
|
734
752
|
let mounted = true;
|
|
735
753
|
reactNative.AccessibilityInfo.isReduceMotionEnabled().then((value) => {
|
|
736
754
|
lastKnown = value;
|
|
@@ -752,8 +770,8 @@ var ROTATION_MS = 1800;
|
|
|
752
770
|
var _AnimatedSparkle = ({ size = 30, color, variant = "spin" }) => {
|
|
753
771
|
const t = useOnboardingTheme();
|
|
754
772
|
const reduced = useReducedMotion();
|
|
755
|
-
const progress =
|
|
756
|
-
|
|
773
|
+
const progress = React19.useRef(new reactNative.Animated.Value(0)).current;
|
|
774
|
+
React19.useEffect(() => {
|
|
757
775
|
if (!loopEnabled(reduced)) {
|
|
758
776
|
progress.setValue(0);
|
|
759
777
|
return;
|
|
@@ -808,7 +826,7 @@ var _AnimatedSparkle = ({ size = 30, color, variant = "spin" }) => {
|
|
|
808
826
|
}
|
|
809
827
|
) });
|
|
810
828
|
};
|
|
811
|
-
var AnimatedSparkle =
|
|
829
|
+
var AnimatedSparkle = React19__default.default.memo(_AnimatedSparkle);
|
|
812
830
|
var styles2 = reactNative.StyleSheet.create({
|
|
813
831
|
box: { alignItems: "center", justifyContent: "center" },
|
|
814
832
|
glyph: {
|
|
@@ -820,8 +838,8 @@ var styles2 = reactNative.StyleSheet.create({
|
|
|
820
838
|
var STAGE_SIZE = 96;
|
|
821
839
|
var ORBIT_DOT = 9;
|
|
822
840
|
var OrbitDot = ({ color, reduced }) => {
|
|
823
|
-
const spin =
|
|
824
|
-
|
|
841
|
+
const spin = React19.useRef(new reactNative.Animated.Value(0)).current;
|
|
842
|
+
React19.useEffect(() => {
|
|
825
843
|
if (!loopEnabled(reduced)) return;
|
|
826
844
|
const loop = reactNative.Animated.loop(
|
|
827
845
|
reactNative.Animated.timing(spin, {
|
|
@@ -844,8 +862,8 @@ var SkeletonLine = ({
|
|
|
844
862
|
color,
|
|
845
863
|
reduced
|
|
846
864
|
}) => {
|
|
847
|
-
const pulse =
|
|
848
|
-
|
|
865
|
+
const pulse = React19.useRef(new reactNative.Animated.Value(1)).current;
|
|
866
|
+
React19.useEffect(() => {
|
|
849
867
|
if (!loopEnabled(reduced)) return;
|
|
850
868
|
const loop = reactNative.Animated.loop(
|
|
851
869
|
reactNative.Animated.sequence([
|
|
@@ -880,8 +898,8 @@ var _LoadingBlock = ({
|
|
|
880
898
|
}) => {
|
|
881
899
|
const t = useOnboardingTheme();
|
|
882
900
|
const reduced = useReducedMotion();
|
|
883
|
-
const [showSkeleton, setShowSkeleton] =
|
|
884
|
-
|
|
901
|
+
const [showSkeleton, setShowSkeleton] = React19.useState(false);
|
|
902
|
+
React19.useEffect(() => {
|
|
885
903
|
const timer = setTimeout(() => setShowSkeleton(true), SKELETON_DELAY_MS);
|
|
886
904
|
return () => clearTimeout(timer);
|
|
887
905
|
}, []);
|
|
@@ -911,7 +929,7 @@ var _LoadingBlock = ({
|
|
|
911
929
|
)
|
|
912
930
|
] });
|
|
913
931
|
};
|
|
914
|
-
var LoadingBlock =
|
|
932
|
+
var LoadingBlock = React19__default.default.memo(_LoadingBlock);
|
|
915
933
|
var styles3 = reactNative.StyleSheet.create({
|
|
916
934
|
center: { flex: 1, alignItems: "center", justifyContent: "center" },
|
|
917
935
|
// Absolutely-positioned label column, pinned just under the centered stage: its top
|
|
@@ -956,7 +974,7 @@ var _LoadingScreen = ({ title, hint }) => {
|
|
|
956
974
|
}
|
|
957
975
|
);
|
|
958
976
|
};
|
|
959
|
-
var LoadingScreen =
|
|
977
|
+
var LoadingScreen = React19__default.default.memo(_LoadingScreen);
|
|
960
978
|
var styles4 = reactNative.StyleSheet.create({
|
|
961
979
|
flex: { flex: 1 }
|
|
962
980
|
});
|
|
@@ -975,8 +993,8 @@ var targetFor = (step, complete, approxScreens) => {
|
|
|
975
993
|
var _StepProgress = ({ step, complete, approxScreens }) => {
|
|
976
994
|
const t = useOnboardingTheme();
|
|
977
995
|
const reduced = useReducedMotion();
|
|
978
|
-
const fill =
|
|
979
|
-
|
|
996
|
+
const fill = React19.useRef(new reactNative.Animated.Value(targetFor(step, complete, approxScreens))).current;
|
|
997
|
+
React19.useEffect(() => {
|
|
980
998
|
reactNative.Animated.timing(fill, {
|
|
981
999
|
toValue: targetFor(step, complete, approxScreens),
|
|
982
1000
|
duration: scaledMs(stateChangeDuration(reduced, PROGRESS_MS)),
|
|
@@ -996,7 +1014,7 @@ var _StepProgress = ({ step, complete, approxScreens }) => {
|
|
|
996
1014
|
}
|
|
997
1015
|
) });
|
|
998
1016
|
};
|
|
999
|
-
var StepProgress =
|
|
1017
|
+
var StepProgress = React19__default.default.memo(_StepProgress);
|
|
1000
1018
|
var styles5 = reactNative.StyleSheet.create({
|
|
1001
1019
|
track: { height: 6, width: "100%", overflow: "hidden" },
|
|
1002
1020
|
fill: { height: 6 }
|
|
@@ -1012,7 +1030,7 @@ var _OnboardingScaffold = ({
|
|
|
1012
1030
|
children
|
|
1013
1031
|
}) => {
|
|
1014
1032
|
const t = useOnboardingTheme();
|
|
1015
|
-
const styles18 =
|
|
1033
|
+
const styles18 = React19.useMemo(() => makeStyles(t), [t]);
|
|
1016
1034
|
const hit = { top: 12, bottom: 12, left: 12, right: 12 };
|
|
1017
1035
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1018
1036
|
reactNativeSafeAreaContext.SafeAreaView,
|
|
@@ -1036,7 +1054,7 @@ var _OnboardingScaffold = ({
|
|
|
1036
1054
|
}
|
|
1037
1055
|
);
|
|
1038
1056
|
};
|
|
1039
|
-
var OnboardingScaffold =
|
|
1057
|
+
var OnboardingScaffold = React19__default.default.memo(_OnboardingScaffold);
|
|
1040
1058
|
function makeStyles(t) {
|
|
1041
1059
|
return reactNative.StyleSheet.create({
|
|
1042
1060
|
flex: { flex: 1 },
|
|
@@ -1057,8 +1075,8 @@ var _Button = ({
|
|
|
1057
1075
|
const t = useOnboardingTheme();
|
|
1058
1076
|
const reduced = useReducedMotion();
|
|
1059
1077
|
const isPrimary = variant === "primary";
|
|
1060
|
-
const pressT =
|
|
1061
|
-
const pressTo =
|
|
1078
|
+
const pressT = React19.useRef(new reactNative.Animated.Value(0)).current;
|
|
1079
|
+
const pressTo = React19.useCallback(
|
|
1062
1080
|
(to) => {
|
|
1063
1081
|
if (reduced) {
|
|
1064
1082
|
reactNative.Animated.timing(pressT, {
|
|
@@ -1077,16 +1095,16 @@ var _Button = ({
|
|
|
1077
1095
|
},
|
|
1078
1096
|
[pressT, reduced]
|
|
1079
1097
|
);
|
|
1080
|
-
const lastPress =
|
|
1081
|
-
const handlePress =
|
|
1098
|
+
const lastPress = React19.useRef(0);
|
|
1099
|
+
const handlePress = React19.useCallback(() => {
|
|
1082
1100
|
if (!onPress) return;
|
|
1083
1101
|
const now = Date.now();
|
|
1084
1102
|
if (now - lastPress.current < 500) return;
|
|
1085
1103
|
lastPress.current = now;
|
|
1086
1104
|
onPress();
|
|
1087
1105
|
}, [onPress]);
|
|
1088
|
-
const handlePressIn =
|
|
1089
|
-
const handlePressOut =
|
|
1106
|
+
const handlePressIn = React19.useCallback(() => pressTo(1), [pressTo]);
|
|
1107
|
+
const handlePressOut = React19.useCallback(() => pressTo(0), [pressTo]);
|
|
1090
1108
|
const scale = pressT.interpolate({
|
|
1091
1109
|
inputRange: [0, 1],
|
|
1092
1110
|
outputRange: [1, reduced ? 1 : BUTTON_PRESS_SCALE]
|
|
@@ -1137,7 +1155,7 @@ var _Button = ({
|
|
|
1137
1155
|
}
|
|
1138
1156
|
);
|
|
1139
1157
|
};
|
|
1140
|
-
var Button =
|
|
1158
|
+
var Button = React19__default.default.memo(_Button);
|
|
1141
1159
|
var styles6 = reactNative.StyleSheet.create({
|
|
1142
1160
|
base: { alignItems: "center", justifyContent: "center" },
|
|
1143
1161
|
fill: { width: "100%" }
|
|
@@ -1155,7 +1173,7 @@ var _ErrorBlock = ({
|
|
|
1155
1173
|
onRetry ? /* @__PURE__ */ jsxRuntime.jsx(Button, { title: retryLabel, onPress: onRetry, variant: "outline" }) : null
|
|
1156
1174
|
] });
|
|
1157
1175
|
};
|
|
1158
|
-
var ErrorBlock =
|
|
1176
|
+
var ErrorBlock = React19__default.default.memo(_ErrorBlock);
|
|
1159
1177
|
var styles7 = reactNative.StyleSheet.create({
|
|
1160
1178
|
center: { flex: 1, alignItems: "center", justifyContent: "center" }
|
|
1161
1179
|
});
|
|
@@ -1168,7 +1186,7 @@ var _CardLayout = ({
|
|
|
1168
1186
|
titleNode
|
|
1169
1187
|
}) => {
|
|
1170
1188
|
const t = useOnboardingTheme();
|
|
1171
|
-
const styles18 =
|
|
1189
|
+
const styles18 = React19.useMemo(() => makeStyles2(t), [t]);
|
|
1172
1190
|
const hasHeader = !!title || !!subtitle || !!titleNode;
|
|
1173
1191
|
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.KeyboardAvoidingView, { style: styles18.fill, behavior: "padding", children: [
|
|
1174
1192
|
hasHeader ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles18.header, children: [
|
|
@@ -1201,7 +1219,7 @@ var _CardLayout = ({
|
|
|
1201
1219
|
footer ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles18.footer, children: footer }) : null
|
|
1202
1220
|
] });
|
|
1203
1221
|
};
|
|
1204
|
-
var CardLayout =
|
|
1222
|
+
var CardLayout = React19__default.default.memo(_CardLayout);
|
|
1205
1223
|
function makeStyles2(t) {
|
|
1206
1224
|
return reactNative.StyleSheet.create({
|
|
1207
1225
|
fill: { flex: 1 },
|
|
@@ -1224,12 +1242,12 @@ var _CompletionView = ({
|
|
|
1224
1242
|
}) => {
|
|
1225
1243
|
const t = useOnboardingTheme();
|
|
1226
1244
|
const reduced = useReducedMotion();
|
|
1227
|
-
const [submitted, setSubmitted] =
|
|
1228
|
-
const pop =
|
|
1229
|
-
const burst =
|
|
1230
|
-
const titleT =
|
|
1231
|
-
const messageT =
|
|
1232
|
-
|
|
1245
|
+
const [submitted, setSubmitted] = React19.useState(false);
|
|
1246
|
+
const pop = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
|
|
1247
|
+
const burst = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
|
|
1248
|
+
const titleT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
|
|
1249
|
+
const messageT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
|
|
1250
|
+
React19.useEffect(() => {
|
|
1233
1251
|
if (reduced) {
|
|
1234
1252
|
pop.setValue(1);
|
|
1235
1253
|
burst.setValue(1);
|
|
@@ -1264,7 +1282,7 @@ var _CompletionView = ({
|
|
|
1264
1282
|
anims.forEach((a) => a.start());
|
|
1265
1283
|
return () => anims.forEach((a) => a.stop());
|
|
1266
1284
|
}, [reduced, pop, burst, titleT, messageT]);
|
|
1267
|
-
const handlePress =
|
|
1285
|
+
const handlePress = React19.useCallback(() => {
|
|
1268
1286
|
if (submitted) return;
|
|
1269
1287
|
setSubmitted(true);
|
|
1270
1288
|
onContinue();
|
|
@@ -1326,7 +1344,7 @@ var _CompletionView = ({
|
|
|
1326
1344
|
}
|
|
1327
1345
|
);
|
|
1328
1346
|
};
|
|
1329
|
-
var CompletionView =
|
|
1347
|
+
var CompletionView = React19__default.default.memo(_CompletionView);
|
|
1330
1348
|
var styles8 = reactNative.StyleSheet.create({
|
|
1331
1349
|
center: { width: "100%", alignItems: "center", justifyContent: "center" },
|
|
1332
1350
|
badgeStage: {
|
|
@@ -1348,13 +1366,13 @@ var _CardHandoff = ({
|
|
|
1348
1366
|
children
|
|
1349
1367
|
}) => {
|
|
1350
1368
|
const reduced = useReducedMotion();
|
|
1351
|
-
const [leaving, setLeaving] =
|
|
1352
|
-
const enterT =
|
|
1353
|
-
const exitT =
|
|
1354
|
-
const prev =
|
|
1355
|
-
const running =
|
|
1356
|
-
const mounted =
|
|
1357
|
-
|
|
1369
|
+
const [leaving, setLeaving] = React19.useState(null);
|
|
1370
|
+
const enterT = React19.useRef(new reactNative.Animated.Value(0)).current;
|
|
1371
|
+
const exitT = React19.useRef(new reactNative.Animated.Value(0)).current;
|
|
1372
|
+
const prev = React19.useRef({ key: transitionKey, node: children, variant });
|
|
1373
|
+
const running = React19.useRef({});
|
|
1374
|
+
const mounted = React19.useRef(false);
|
|
1375
|
+
React19.useEffect(() => {
|
|
1358
1376
|
var _a2, _b;
|
|
1359
1377
|
const last = prev.current;
|
|
1360
1378
|
prev.current = { key: transitionKey, node: children, variant };
|
|
@@ -1387,7 +1405,7 @@ var _CardHandoff = ({
|
|
|
1387
1405
|
);
|
|
1388
1406
|
running.current.enter = enter;
|
|
1389
1407
|
});
|
|
1390
|
-
|
|
1408
|
+
React19.useEffect(
|
|
1391
1409
|
() => () => {
|
|
1392
1410
|
var _a2, _b;
|
|
1393
1411
|
(_a2 = running.current.enter) == null ? void 0 : _a2.stop();
|
|
@@ -1456,7 +1474,7 @@ var transformFor = (variant, t, reduced, layer, travel) => {
|
|
|
1456
1474
|
}
|
|
1457
1475
|
];
|
|
1458
1476
|
};
|
|
1459
|
-
var CardHandoff =
|
|
1477
|
+
var CardHandoff = React19__default.default.memo(_CardHandoff);
|
|
1460
1478
|
var styles9 = reactNative.StyleSheet.create({
|
|
1461
1479
|
fill: { flex: 1 }
|
|
1462
1480
|
});
|
|
@@ -1480,7 +1498,7 @@ var deriveAnswers = (messages) => {
|
|
|
1480
1498
|
const props = (_a2 = r.props) != null ? _a2 : {};
|
|
1481
1499
|
const progress = props.progress;
|
|
1482
1500
|
const questionText = (_c = (_b = props.title) != null ? _b : props.label) != null ? _c : props.question;
|
|
1483
|
-
const key = (progress == null ? void 0 : progress.key) || questionText;
|
|
1501
|
+
const key = (progress == null ? void 0 : progress.slot_id) || (progress == null ? void 0 : progress.key) || questionText;
|
|
1484
1502
|
const reply = messages[i + 1];
|
|
1485
1503
|
if (key && (reply == null ? void 0 : reply.role) === "user") {
|
|
1486
1504
|
out[key] = tryParse(stripPrefix(reply.content));
|
|
@@ -1501,45 +1519,71 @@ var readProgress = (response) => {
|
|
|
1501
1519
|
step: typeof p.step === "number" ? p.step : void 0,
|
|
1502
1520
|
total: typeof p.total === "number" ? p.total : void 0,
|
|
1503
1521
|
key: typeof p.key === "string" ? p.key : void 0,
|
|
1522
|
+
// Whitelisted the same way as every other field: an old backend simply omits it.
|
|
1523
|
+
slot_id: typeof p.slot_id === "string" ? p.slot_id : void 0,
|
|
1504
1524
|
skippable: typeof p.skippable === "boolean" ? p.skippable : void 0
|
|
1505
1525
|
};
|
|
1506
1526
|
};
|
|
1507
1527
|
|
|
1508
1528
|
// src/analytics/reportClientEvent.ts
|
|
1509
1529
|
var makeSessionId = () => `wire_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
1530
|
+
var toWireEvents = (events) => events.map((event) => {
|
|
1531
|
+
if (typeof event.ts !== "number") return event;
|
|
1532
|
+
const { ts, ...rest } = event;
|
|
1533
|
+
if (!Number.isFinite(ts)) return rest;
|
|
1534
|
+
try {
|
|
1535
|
+
return { ...rest, ts: new Date(ts).toISOString() };
|
|
1536
|
+
} catch {
|
|
1537
|
+
return rest;
|
|
1538
|
+
}
|
|
1539
|
+
});
|
|
1510
1540
|
var buildEventsRequest = (target, events) => {
|
|
1511
1541
|
if (!(target == null ? void 0 : target.serverUrl) || events.length === 0) return null;
|
|
1512
1542
|
try {
|
|
1513
1543
|
const url = `${target.serverUrl.replace(/\/$/, "")}/v1/events`;
|
|
1514
1544
|
const headers = { "Content-Type": "application/json" };
|
|
1515
1545
|
if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;
|
|
1516
|
-
|
|
1546
|
+
const body = JSON.stringify({ events: toWireEvents(events) });
|
|
1547
|
+
return { url, init: { method: "POST", headers, body } };
|
|
1517
1548
|
} catch {
|
|
1518
1549
|
return null;
|
|
1519
1550
|
}
|
|
1520
1551
|
};
|
|
1521
|
-
var
|
|
1522
|
-
if (typeof __DEV__ === "undefined" || !__DEV__) return;
|
|
1523
|
-
if (typeof console === "undefined" || !console.warn) return;
|
|
1552
|
+
var readEventsAck = async (res) => {
|
|
1524
1553
|
try {
|
|
1525
1554
|
const json = res == null ? void 0 : res.json;
|
|
1526
|
-
if (typeof json !== "function") return;
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1555
|
+
if (typeof json !== "function") return void 0;
|
|
1556
|
+
const body = await Promise.resolve(json.call(res));
|
|
1557
|
+
const skipped = body == null ? void 0 : body.skipped;
|
|
1558
|
+
if (typeof skipped !== "number" || !Number.isFinite(skipped)) return void 0;
|
|
1559
|
+
const reasons = Array.isArray(body == null ? void 0 : body.errors) ? body.errors.map((e) => {
|
|
1560
|
+
const entry = e;
|
|
1561
|
+
const reason = entry == null ? void 0 : entry.reason;
|
|
1562
|
+
if (typeof reason !== "string") return void 0;
|
|
1563
|
+
const field = entry == null ? void 0 : entry.field;
|
|
1564
|
+
return typeof field === "string" && field.length > 0 ? `${reason} (field: ${field})` : reason;
|
|
1565
|
+
}).filter((r) => typeof r === "string") : [];
|
|
1566
|
+
return { written: typeof (body == null ? void 0 : body.written) === "number" ? body.written : void 0, skipped, reasons };
|
|
1535
1567
|
} catch {
|
|
1568
|
+
return void 0;
|
|
1536
1569
|
}
|
|
1537
1570
|
};
|
|
1571
|
+
var describeDiscarded = (ack) => `[wireai] the server ACCEPTED the /v1/events POST but DISCARDED ${ack.skipped} event(s) (skipped in the response body) \u2014 they are gone, not retried. The server reported: skipped=${ack.skipped}${ack.written !== void 0 ? `, written=${ack.written}` : ""}` + (ack.reasons.length > 0 ? `, reasons: ${ack.reasons.join(", ")}.` : ". It gave no reason (the endpoint folds every rejection into one count), so check the server's ingest log for this request rather than guessing.");
|
|
1572
|
+
var warnOnSkippedEvents = (res) => {
|
|
1573
|
+
if (typeof __DEV__ === "undefined" || !__DEV__) return;
|
|
1574
|
+
if (typeof console === "undefined" || !console.warn) return;
|
|
1575
|
+
void readEventsAck(res).then((ack) => {
|
|
1576
|
+
if (!ack || ack.skipped <= 0) return;
|
|
1577
|
+
console.warn(describeDiscarded(ack));
|
|
1578
|
+
});
|
|
1579
|
+
};
|
|
1538
1580
|
var reportClientEvents = (target, events) => {
|
|
1539
1581
|
try {
|
|
1540
1582
|
const req = buildEventsRequest(target, events);
|
|
1541
1583
|
if (!req) return;
|
|
1542
|
-
void fetch(req.url, req.init).
|
|
1584
|
+
void fetch(req.url, req.init).then((res) => {
|
|
1585
|
+
warnOnSkippedEvents(res);
|
|
1586
|
+
}).catch(() => {
|
|
1543
1587
|
});
|
|
1544
1588
|
} catch {
|
|
1545
1589
|
}
|
|
@@ -1550,7 +1594,13 @@ var reportClientEventsAwait = async (target, events) => {
|
|
|
1550
1594
|
const req = buildEventsRequest(target, events);
|
|
1551
1595
|
if (!req) return false;
|
|
1552
1596
|
const res = await fetch(req.url, req.init);
|
|
1553
|
-
|
|
1597
|
+
if (!res || !res.ok) return false;
|
|
1598
|
+
const ack = await readEventsAck(res);
|
|
1599
|
+
if (!ack || ack.skipped <= 0) return true;
|
|
1600
|
+
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
1601
|
+
console.warn(describeDiscarded(ack));
|
|
1602
|
+
}
|
|
1603
|
+
return false;
|
|
1554
1604
|
} catch {
|
|
1555
1605
|
return false;
|
|
1556
1606
|
}
|
|
@@ -1582,6 +1632,382 @@ var sendPreview = (target, { sessionId, userMessage }) => {
|
|
|
1582
1632
|
} catch {
|
|
1583
1633
|
}
|
|
1584
1634
|
};
|
|
1635
|
+
|
|
1636
|
+
// src/permissions/permissionEvents.ts
|
|
1637
|
+
var WIRE_PERMISSION_EVENTS = {
|
|
1638
|
+
/** The priming screen became visible. The denominator for every rate below. */
|
|
1639
|
+
screenShown: "wire_permission_screen_shown",
|
|
1640
|
+
/** The user tapped the primary, so the OS dialog is about to open. The rationale worked. */
|
|
1641
|
+
primerAccepted: "wire_permission_primer_accepted",
|
|
1642
|
+
/** The OS granted it. */
|
|
1643
|
+
granted: "wire_permission_granted",
|
|
1644
|
+
/** The OS refused it (a permanently blocked answer reports here too, with `status: "blocked"`). */
|
|
1645
|
+
denied: "wire_permission_denied",
|
|
1646
|
+
/** The user took the secondary. The one native prompt was NOT spent. */
|
|
1647
|
+
skipped: "wire_permission_skipped",
|
|
1648
|
+
/** A blocked user was redirected to the OS settings page. */
|
|
1649
|
+
settingsOpened: "wire_permission_settings_opened"
|
|
1650
|
+
};
|
|
1651
|
+
var permissionEventName = (stage) => {
|
|
1652
|
+
switch (stage) {
|
|
1653
|
+
case "shown":
|
|
1654
|
+
return WIRE_PERMISSION_EVENTS.screenShown;
|
|
1655
|
+
case "accepted":
|
|
1656
|
+
return WIRE_PERMISSION_EVENTS.primerAccepted;
|
|
1657
|
+
case "granted":
|
|
1658
|
+
return WIRE_PERMISSION_EVENTS.granted;
|
|
1659
|
+
case "denied":
|
|
1660
|
+
return WIRE_PERMISSION_EVENTS.denied;
|
|
1661
|
+
case "skipped":
|
|
1662
|
+
return WIRE_PERMISSION_EVENTS.skipped;
|
|
1663
|
+
case "settings":
|
|
1664
|
+
return WIRE_PERMISSION_EVENTS.settingsOpened;
|
|
1665
|
+
default: {
|
|
1666
|
+
const _exhaustive = stage;
|
|
1667
|
+
return _exhaustive;
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
};
|
|
1671
|
+
var permissionEventProps = (permission, status) => status ? { permission, status } : { permission };
|
|
1672
|
+
var normalizePermissionStatus = (value) => value === "granted" || value === "denied" || value === "blocked" ? value : "denied";
|
|
1673
|
+
|
|
1674
|
+
// src/haptics/haptics.ts
|
|
1675
|
+
var play = async (tone) => {
|
|
1676
|
+
try {
|
|
1677
|
+
const mod = await import('expo-haptics');
|
|
1678
|
+
if (!mod) return false;
|
|
1679
|
+
if (tone === "selection") ;
|
|
1680
|
+
if (tone === "light") ;
|
|
1681
|
+
await mod.notificationAsync(mod.NotificationFeedbackType.Success);
|
|
1682
|
+
return true;
|
|
1683
|
+
} catch {
|
|
1684
|
+
return false;
|
|
1685
|
+
}
|
|
1686
|
+
};
|
|
1687
|
+
var playHaptic = (tone) => {
|
|
1688
|
+
void play(tone);
|
|
1689
|
+
};
|
|
1690
|
+
|
|
1691
|
+
// src/utils/warnInDev.ts
|
|
1692
|
+
var warnInDev = (message) => {
|
|
1693
|
+
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
1694
|
+
console.warn(message);
|
|
1695
|
+
return true;
|
|
1696
|
+
}
|
|
1697
|
+
return false;
|
|
1698
|
+
};
|
|
1699
|
+
var easeWire4 = reactNative.Easing.bezier(...WIRE_BEZIER);
|
|
1700
|
+
var RISE_PX = 10;
|
|
1701
|
+
var PERMISSION_CARD_NAME = "PermissionCard";
|
|
1702
|
+
var REQUEST_WATCHDOG_MS = 9e4;
|
|
1703
|
+
var schema = zod.z.object({
|
|
1704
|
+
permission: zod.z.string().describe("Which OS permission this screen primes, e.g. 'notifications'"),
|
|
1705
|
+
title: zod.z.string().describe("Headline for the ask"),
|
|
1706
|
+
message: zod.z.string().describe("Why the app needs it, in the user's terms"),
|
|
1707
|
+
primaryLabel: zod.z.string().describe("Primary button, the only control that can open the OS dialog"),
|
|
1708
|
+
secondaryLabel: zod.z.string().describe("Secondary button, advances without spending the OS prompt"),
|
|
1709
|
+
blockedTitle: zod.z.string().optional().describe("Headline once the permission is permanently refused"),
|
|
1710
|
+
blockedMessage: zod.z.string().optional().describe("Rationale for the settings route"),
|
|
1711
|
+
settingsLabel: zod.z.string().optional().describe("Primary button label on the blocked route"),
|
|
1712
|
+
continueLabel: zod.z.string().optional().describe("Primary button label when there is nothing to ask"),
|
|
1713
|
+
illustration: zod.z.string().optional().describe("Name of an app-provided illustration (defaults to the permission name)")
|
|
1714
|
+
});
|
|
1715
|
+
var _PermissionCard = ({
|
|
1716
|
+
permission,
|
|
1717
|
+
title,
|
|
1718
|
+
message,
|
|
1719
|
+
primaryLabel,
|
|
1720
|
+
secondaryLabel,
|
|
1721
|
+
blockedTitle,
|
|
1722
|
+
blockedMessage,
|
|
1723
|
+
settingsLabel,
|
|
1724
|
+
continueLabel,
|
|
1725
|
+
illustration,
|
|
1726
|
+
request,
|
|
1727
|
+
getStatus,
|
|
1728
|
+
openSettings,
|
|
1729
|
+
onStage,
|
|
1730
|
+
onSettled
|
|
1731
|
+
}) => {
|
|
1732
|
+
const t = useOnboardingTheme();
|
|
1733
|
+
const reduced = useReducedMotion();
|
|
1734
|
+
const art = useIllustration(illustration != null ? illustration : permission);
|
|
1735
|
+
const [status, setStatus] = React19.useState(void 0);
|
|
1736
|
+
const [busy, setBusy] = React19.useState(false);
|
|
1737
|
+
const settledRef = React19.useRef(false);
|
|
1738
|
+
const acceptedRef = React19.useRef(false);
|
|
1739
|
+
const watchdogRef = React19.useRef(null);
|
|
1740
|
+
const clearWatchdog = React19.useCallback(() => {
|
|
1741
|
+
if (watchdogRef.current) {
|
|
1742
|
+
clearTimeout(watchdogRef.current);
|
|
1743
|
+
watchdogRef.current = null;
|
|
1744
|
+
}
|
|
1745
|
+
}, []);
|
|
1746
|
+
React19.useEffect(() => clearWatchdog, [clearWatchdog]);
|
|
1747
|
+
const onStageRef = React19.useRef(onStage);
|
|
1748
|
+
onStageRef.current = onStage;
|
|
1749
|
+
const onSettledRef = React19.useRef(onSettled);
|
|
1750
|
+
onSettledRef.current = onSettled;
|
|
1751
|
+
const getStatusRef = React19.useRef(getStatus);
|
|
1752
|
+
getStatusRef.current = getStatus;
|
|
1753
|
+
const shownRef = React19.useRef(false);
|
|
1754
|
+
React19.useEffect(() => {
|
|
1755
|
+
var _a2;
|
|
1756
|
+
if (shownRef.current) return;
|
|
1757
|
+
shownRef.current = true;
|
|
1758
|
+
(_a2 = onStageRef.current) == null ? void 0 : _a2.call(onStageRef, "shown");
|
|
1759
|
+
}, []);
|
|
1760
|
+
React19.useEffect(() => {
|
|
1761
|
+
const probe = getStatusRef.current;
|
|
1762
|
+
if (!probe) return;
|
|
1763
|
+
let cancelled = false;
|
|
1764
|
+
try {
|
|
1765
|
+
void Promise.resolve(probe()).then((value) => {
|
|
1766
|
+
if (!cancelled) setStatus(normalizePermissionStatus(value));
|
|
1767
|
+
}).catch(() => {
|
|
1768
|
+
});
|
|
1769
|
+
} catch {
|
|
1770
|
+
}
|
|
1771
|
+
return () => {
|
|
1772
|
+
cancelled = true;
|
|
1773
|
+
};
|
|
1774
|
+
}, []);
|
|
1775
|
+
const requestRef = React19.useRef(request);
|
|
1776
|
+
requestRef.current = request;
|
|
1777
|
+
React19.useEffect(() => {
|
|
1778
|
+
if (requestRef.current) return;
|
|
1779
|
+
warnInDev(
|
|
1780
|
+
`[wireai] <WireOnboarding> got a permission screen for "${permission}" with no \`request\` function, so it cannot open the OS dialog and renders as a plain continue. Pass request: () => Promise<'granted' | 'denied' | 'blocked'> (5 lines around expo-notifications, see the README).`
|
|
1781
|
+
);
|
|
1782
|
+
}, [permission]);
|
|
1783
|
+
const settle = React19.useCallback((outcome) => {
|
|
1784
|
+
var _a2;
|
|
1785
|
+
if (settledRef.current) return;
|
|
1786
|
+
settledRef.current = true;
|
|
1787
|
+
clearWatchdog();
|
|
1788
|
+
(_a2 = onSettledRef.current) == null ? void 0 : _a2.call(onSettledRef, outcome);
|
|
1789
|
+
}, [clearWatchdog]);
|
|
1790
|
+
const primaryAction = status === "blocked" ? "settings" : status === "granted" || !request ? "continue" : "ask";
|
|
1791
|
+
const handlePrimary = React19.useCallback(() => {
|
|
1792
|
+
var _a2, _b, _c, _d;
|
|
1793
|
+
if (settledRef.current || busy) return;
|
|
1794
|
+
if (primaryAction === "settings") {
|
|
1795
|
+
(_a2 = onStageRef.current) == null ? void 0 : _a2.call(onStageRef, "settings", "blocked");
|
|
1796
|
+
try {
|
|
1797
|
+
void Promise.resolve(openSettings == null ? void 0 : openSettings()).catch(() => {
|
|
1798
|
+
});
|
|
1799
|
+
} catch {
|
|
1800
|
+
}
|
|
1801
|
+
settle("blocked");
|
|
1802
|
+
return;
|
|
1803
|
+
}
|
|
1804
|
+
if (primaryAction === "continue") {
|
|
1805
|
+
if (status === "granted") {
|
|
1806
|
+
(_b = onStageRef.current) == null ? void 0 : _b.call(onStageRef, "granted", "granted");
|
|
1807
|
+
settle("granted");
|
|
1808
|
+
} else {
|
|
1809
|
+
(_c = onStageRef.current) == null ? void 0 : _c.call(onStageRef, "skipped");
|
|
1810
|
+
settle("skipped");
|
|
1811
|
+
}
|
|
1812
|
+
return;
|
|
1813
|
+
}
|
|
1814
|
+
if (!acceptedRef.current) {
|
|
1815
|
+
acceptedRef.current = true;
|
|
1816
|
+
(_d = onStageRef.current) == null ? void 0 : _d.call(onStageRef, "accepted");
|
|
1817
|
+
}
|
|
1818
|
+
setBusy(true);
|
|
1819
|
+
const finish = (value) => {
|
|
1820
|
+
var _a3, _b2;
|
|
1821
|
+
if (settledRef.current) return;
|
|
1822
|
+
clearWatchdog();
|
|
1823
|
+
const resolved = normalizePermissionStatus(value);
|
|
1824
|
+
setBusy(false);
|
|
1825
|
+
setStatus(resolved);
|
|
1826
|
+
if (resolved === "granted") {
|
|
1827
|
+
playHaptic("success");
|
|
1828
|
+
(_a3 = onStageRef.current) == null ? void 0 : _a3.call(onStageRef, "granted", "granted");
|
|
1829
|
+
} else {
|
|
1830
|
+
(_b2 = onStageRef.current) == null ? void 0 : _b2.call(onStageRef, "denied", resolved);
|
|
1831
|
+
}
|
|
1832
|
+
settle(resolved);
|
|
1833
|
+
};
|
|
1834
|
+
clearWatchdog();
|
|
1835
|
+
watchdogRef.current = setTimeout(() => {
|
|
1836
|
+
watchdogRef.current = null;
|
|
1837
|
+
if (settledRef.current) return;
|
|
1838
|
+
setBusy(false);
|
|
1839
|
+
warnInDev(
|
|
1840
|
+
`[wireai] the \`request\` for the "${permission}" permission screen has not settled after ${Math.round(REQUEST_WATCHDOG_MS / 1e3)}s, so the kit handed the controls back rather than leaving the user on a dead-end screen. It recorded NO outcome, because a pending request is not a denial. Make sure your request resolves to 'granted' | 'denied' | 'blocked' on every branch, including the one where the user dismisses the OS dialog.`
|
|
1841
|
+
);
|
|
1842
|
+
}, REQUEST_WATCHDOG_MS);
|
|
1843
|
+
try {
|
|
1844
|
+
void Promise.resolve(request == null ? void 0 : request()).then(finish, () => finish("denied"));
|
|
1845
|
+
} catch {
|
|
1846
|
+
finish("denied");
|
|
1847
|
+
}
|
|
1848
|
+
}, [busy, primaryAction, status, request, openSettings, settle, clearWatchdog, permission]);
|
|
1849
|
+
const handleSecondary = React19.useCallback(() => {
|
|
1850
|
+
var _a2;
|
|
1851
|
+
if (settledRef.current || busy) return;
|
|
1852
|
+
(_a2 = onStageRef.current) == null ? void 0 : _a2.call(onStageRef, "skipped");
|
|
1853
|
+
settle("skipped");
|
|
1854
|
+
}, [busy, settle]);
|
|
1855
|
+
const popT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
|
|
1856
|
+
const titleT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
|
|
1857
|
+
const bodyT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
|
|
1858
|
+
React19.useEffect(() => {
|
|
1859
|
+
if (reduced) {
|
|
1860
|
+
popT.setValue(1);
|
|
1861
|
+
titleT.setValue(1);
|
|
1862
|
+
bodyT.setValue(1);
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1865
|
+
const rise = (value, delay) => reactNative.Animated.timing(value, {
|
|
1866
|
+
toValue: 1,
|
|
1867
|
+
duration: scaledMs(INTERSTITIAL_HEAD_MS),
|
|
1868
|
+
delay,
|
|
1869
|
+
easing: easeWire4,
|
|
1870
|
+
useNativeDriver: true
|
|
1871
|
+
});
|
|
1872
|
+
const anims = [
|
|
1873
|
+
reactNative.Animated.spring(popT, {
|
|
1874
|
+
toValue: 1,
|
|
1875
|
+
friction: STATUS_POP_SPRING.friction,
|
|
1876
|
+
tension: STATUS_POP_SPRING.tension,
|
|
1877
|
+
useNativeDriver: true
|
|
1878
|
+
}),
|
|
1879
|
+
rise(titleT, scaledMs(INTERSTITIAL_HEAD_STAGGER_MS)),
|
|
1880
|
+
rise(bodyT, scaledMs(INTERSTITIAL_HEAD_STAGGER_MS * 2))
|
|
1881
|
+
];
|
|
1882
|
+
anims.forEach((a) => a.start());
|
|
1883
|
+
return () => anims.forEach((a) => a.stop());
|
|
1884
|
+
}, [reduced, popT, titleT, bodyT]);
|
|
1885
|
+
const popScale = popT.interpolate({
|
|
1886
|
+
inputRange: [0, 1],
|
|
1887
|
+
outputRange: [STATUS_POP_SCALE_FROM, 1]
|
|
1888
|
+
});
|
|
1889
|
+
const riseStyle = (value) => ({
|
|
1890
|
+
opacity: value,
|
|
1891
|
+
transform: [
|
|
1892
|
+
{ translateY: value.interpolate({ inputRange: [0, 1], outputRange: [RISE_PX, 0] }) }
|
|
1893
|
+
]
|
|
1894
|
+
});
|
|
1895
|
+
const blocked = status === "blocked";
|
|
1896
|
+
const shownTitle = blocked ? blockedTitle != null ? blockedTitle : title : title;
|
|
1897
|
+
const shownMessage = blocked ? blockedMessage != null ? blockedMessage : message : message;
|
|
1898
|
+
const primaryTitle = primaryAction === "settings" ? settingsLabel != null ? settingsLabel : primaryLabel : primaryAction === "continue" ? continueLabel != null ? continueLabel : primaryLabel : primaryLabel;
|
|
1899
|
+
const showSecondary = status !== "granted";
|
|
1900
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1901
|
+
CardLayout,
|
|
1902
|
+
{
|
|
1903
|
+
align: "center",
|
|
1904
|
+
footer: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles10.footer, { gap: t.spacing.sm }], children: [
|
|
1905
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { title: primaryTitle, onPress: handlePrimary, variant: "primary", disabled: busy }),
|
|
1906
|
+
showSecondary ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1907
|
+
Button,
|
|
1908
|
+
{
|
|
1909
|
+
title: secondaryLabel,
|
|
1910
|
+
onPress: handleSecondary,
|
|
1911
|
+
variant: "outline",
|
|
1912
|
+
disabled: busy
|
|
1913
|
+
}
|
|
1914
|
+
) : null
|
|
1915
|
+
] }),
|
|
1916
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles10.center, { gap: t.spacing.md }], children: [
|
|
1917
|
+
art ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1918
|
+
reactNative.Animated.View,
|
|
1919
|
+
{
|
|
1920
|
+
style: [styles10.art, { opacity: popT, transform: [{ scale: popScale }] }],
|
|
1921
|
+
children: art
|
|
1922
|
+
}
|
|
1923
|
+
) : null,
|
|
1924
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Animated.View, { style: riseStyle(titleT), children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [headingStyle(t.fonts), { color: t.colors.text, textAlign: "center" }], children: shownTitle }) }),
|
|
1925
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Animated.View, { style: riseStyle(bodyT), children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted, textAlign: "center" }], children: shownMessage }) })
|
|
1926
|
+
] })
|
|
1927
|
+
}
|
|
1928
|
+
);
|
|
1929
|
+
};
|
|
1930
|
+
var PermissionCardView = React19__default.default.memo(_PermissionCard);
|
|
1931
|
+
var PermissionCard = {
|
|
1932
|
+
name: PERMISSION_CARD_NAME,
|
|
1933
|
+
description: "A priming screen shown BEFORE an OS permission dialog: it explains why the app wants the permission and asks only on the primary tap. Never first, never last. The user is never blocked by it: the secondary advances the flow with the OS prompt unspent.",
|
|
1934
|
+
component: PermissionCardView,
|
|
1935
|
+
propsSchema: schema
|
|
1936
|
+
};
|
|
1937
|
+
var styles10 = reactNative.StyleSheet.create({
|
|
1938
|
+
center: { width: "100%", alignItems: "center", justifyContent: "center" },
|
|
1939
|
+
art: { alignItems: "center", justifyContent: "center" },
|
|
1940
|
+
footer: { width: "100%", alignItems: "center" }
|
|
1941
|
+
});
|
|
1942
|
+
|
|
1943
|
+
// src/permissions/placement.ts
|
|
1944
|
+
var DEFAULT_PERMISSION_PLACEMENT = "beforeEnd";
|
|
1945
|
+
var normalizeAfterCard = (value) => Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
|
|
1946
|
+
var targetIndex = (placement) => {
|
|
1947
|
+
if (placement === "beforeEnd") return void 0;
|
|
1948
|
+
if (placement === "start") return 0;
|
|
1949
|
+
return normalizeAfterCard(placement.afterCard);
|
|
1950
|
+
};
|
|
1951
|
+
var isPermissionDue = (placement, position) => {
|
|
1952
|
+
if (position.cardIndex < 0) return false;
|
|
1953
|
+
const target = targetIndex(placement);
|
|
1954
|
+
if (target === void 0) return position.isTerminal;
|
|
1955
|
+
return position.cardIndex >= target || position.isTerminal;
|
|
1956
|
+
};
|
|
1957
|
+
var permissionScreenId = (screen, index) => {
|
|
1958
|
+
var _a2;
|
|
1959
|
+
return (_a2 = screen.id) != null ? _a2 : `${screen.permission}:${index}`;
|
|
1960
|
+
};
|
|
1961
|
+
var selectDuePermissionScreen = (screens, settled, position) => {
|
|
1962
|
+
var _a2;
|
|
1963
|
+
if (!screens || screens.length === 0) return void 0;
|
|
1964
|
+
for (let index = 0; index < screens.length; index++) {
|
|
1965
|
+
const screen = screens[index];
|
|
1966
|
+
if (!screen) continue;
|
|
1967
|
+
const id = permissionScreenId(screen, index);
|
|
1968
|
+
if (settled.includes(id)) continue;
|
|
1969
|
+
if (isPermissionDue((_a2 = screen.placement) != null ? _a2 : DEFAULT_PERMISSION_PLACEMENT, position)) {
|
|
1970
|
+
return { screen, id };
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
return void 0;
|
|
1974
|
+
};
|
|
1975
|
+
|
|
1976
|
+
// src/permissions/permissionCopy.ts
|
|
1977
|
+
var NOTIFICATIONS_PERMISSION_COPY = {
|
|
1978
|
+
title: "Want a nudge at the right moment?",
|
|
1979
|
+
message: "Turn notifications on and we will remind you when it actually helps. No daily noise, and you can turn them off any time.",
|
|
1980
|
+
primaryLabel: "Enable notifications",
|
|
1981
|
+
secondaryLabel: "Maybe later",
|
|
1982
|
+
blockedTitle: "Notifications are switched off",
|
|
1983
|
+
blockedMessage: "Notifications are turned off for this app, so we cannot ask from here. You can switch them back on in Settings.",
|
|
1984
|
+
settingsLabel: "Open settings",
|
|
1985
|
+
continueLabel: "Continue"
|
|
1986
|
+
};
|
|
1987
|
+
var GENERIC_PERMISSION_COPY = {
|
|
1988
|
+
title: "One quick permission",
|
|
1989
|
+
message: "We need your permission for this part to work. You can change it any time.",
|
|
1990
|
+
primaryLabel: "Allow",
|
|
1991
|
+
secondaryLabel: "Maybe later",
|
|
1992
|
+
blockedTitle: "Permission is switched off",
|
|
1993
|
+
blockedMessage: "This permission is turned off for this app, so we cannot ask from here. You can switch it back on in Settings.",
|
|
1994
|
+
settingsLabel: "Open settings",
|
|
1995
|
+
continueLabel: "Continue"
|
|
1996
|
+
};
|
|
1997
|
+
var DEFAULT_PERMISSION_COPY = {
|
|
1998
|
+
notifications: NOTIFICATIONS_PERMISSION_COPY
|
|
1999
|
+
};
|
|
2000
|
+
var resolvePermissionCopy = (permission, overrides) => {
|
|
2001
|
+
var _a2;
|
|
2002
|
+
const base = (_a2 = DEFAULT_PERMISSION_COPY[permission]) != null ? _a2 : GENERIC_PERMISSION_COPY;
|
|
2003
|
+
if (!overrides) return base;
|
|
2004
|
+
const merged = { ...base };
|
|
2005
|
+
for (const key of Object.keys(overrides)) {
|
|
2006
|
+
const value = overrides[key];
|
|
2007
|
+
if (typeof value === "string" && value.length > 0) merged[key] = value;
|
|
2008
|
+
}
|
|
2009
|
+
return merged;
|
|
2010
|
+
};
|
|
1585
2011
|
var SKIP_ONE_SENTINEL = "__wireai_skip__";
|
|
1586
2012
|
var ANSWER_CALLBACKS = ["onSubmit", "onSelect", "onConfirm", "onPress"];
|
|
1587
2013
|
var DEFAULT_COPY = {
|
|
@@ -1610,53 +2036,60 @@ var OnboardingFlow = ({
|
|
|
1610
2036
|
reportTarget,
|
|
1611
2037
|
sessionId,
|
|
1612
2038
|
resumed = false,
|
|
1613
|
-
clientContext
|
|
2039
|
+
clientContext,
|
|
2040
|
+
permissionScreens,
|
|
2041
|
+
settledPermissions,
|
|
2042
|
+
permissionsPending = false,
|
|
2043
|
+
onPermissionSettled
|
|
1614
2044
|
}) => {
|
|
1615
2045
|
var _a2, _b, _c, _d, _e, _f;
|
|
1616
2046
|
const { messages, error, isLoading, sendMessage, reset } = wireaiRn.useWireAIThread();
|
|
1617
2047
|
const makeActions = wireaiRn.useWireAIAction(sendMessage);
|
|
1618
|
-
const c =
|
|
1619
|
-
const onEventRef =
|
|
2048
|
+
const c = React19.useMemo(() => ({ ...DEFAULT_COPY, ...copy }), [copy]);
|
|
2049
|
+
const onEventRef = React19.useRef(onEvent);
|
|
1620
2050
|
onEventRef.current = onEvent;
|
|
1621
|
-
const onErrorRef =
|
|
2051
|
+
const onErrorRef = React19.useRef(onError);
|
|
1622
2052
|
onErrorRef.current = onError;
|
|
1623
|
-
const onCompleteRef =
|
|
2053
|
+
const onCompleteRef = React19.useRef(onComplete);
|
|
1624
2054
|
onCompleteRef.current = onComplete;
|
|
1625
|
-
const validatorsRef =
|
|
2055
|
+
const validatorsRef = React19.useRef(validators);
|
|
1626
2056
|
validatorsRef.current = validators;
|
|
1627
|
-
const reportTargetRef =
|
|
2057
|
+
const reportTargetRef = React19.useRef(reportTarget);
|
|
1628
2058
|
reportTargetRef.current = reportTarget;
|
|
1629
|
-
const sessionIdRef =
|
|
2059
|
+
const sessionIdRef = React19.useRef(sessionId);
|
|
1630
2060
|
sessionIdRef.current = sessionId;
|
|
1631
|
-
const clientContextRef =
|
|
2061
|
+
const clientContextRef = React19.useRef(clientContext);
|
|
1632
2062
|
clientContextRef.current = clientContext;
|
|
1633
|
-
const lastScreenIndexRef =
|
|
1634
|
-
const previewTimer =
|
|
1635
|
-
const started =
|
|
1636
|
-
const finished =
|
|
1637
|
-
const errored =
|
|
1638
|
-
const attempts =
|
|
1639
|
-
const degradedRef =
|
|
1640
|
-
const [degraded, setDegraded] =
|
|
1641
|
-
const [timedOut, setTimedOut] =
|
|
1642
|
-
const [validationError, setValidationError] =
|
|
1643
|
-
const [validating, setValidating] =
|
|
1644
|
-
const
|
|
2063
|
+
const lastScreenIndexRef = React19.useRef(-1);
|
|
2064
|
+
const previewTimer = React19.useRef(null);
|
|
2065
|
+
const started = React19.useRef(false);
|
|
2066
|
+
const finished = React19.useRef(false);
|
|
2067
|
+
const errored = React19.useRef(false);
|
|
2068
|
+
const attempts = React19.useRef(0);
|
|
2069
|
+
const degradedRef = React19.useRef(false);
|
|
2070
|
+
const [degraded, setDegraded] = React19.useState(false);
|
|
2071
|
+
const [timedOut, setTimedOut] = React19.useState(false);
|
|
2072
|
+
const [validationError, setValidationError] = React19.useState();
|
|
2073
|
+
const [validating, setValidating] = React19.useState(false);
|
|
2074
|
+
const [locallySettledPermissions, setLocallySettledPermissions] = React19.useState([]);
|
|
2075
|
+
const onPermissionSettledRef = React19.useRef(onPermissionSettled);
|
|
2076
|
+
onPermissionSettledRef.current = onPermissionSettled;
|
|
2077
|
+
const lastCard = React19.useMemo(
|
|
1645
2078
|
() => [...messages].reverse().find((m) => {
|
|
1646
2079
|
var _a3;
|
|
1647
2080
|
return m.role === "assistant" && ((_a3 = m.response) == null ? void 0 : _a3.action) === "render";
|
|
1648
2081
|
}),
|
|
1649
2082
|
[messages]
|
|
1650
2083
|
);
|
|
1651
|
-
const renderedCount =
|
|
2084
|
+
const renderedCount = React19.useMemo(
|
|
1652
2085
|
() => messages.filter((m) => {
|
|
1653
2086
|
var _a3;
|
|
1654
2087
|
return m.role === "assistant" && ((_a3 = m.response) == null ? void 0 : _a3.action) === "render";
|
|
1655
2088
|
}).length,
|
|
1656
2089
|
[messages]
|
|
1657
2090
|
);
|
|
1658
|
-
const progress =
|
|
1659
|
-
|
|
2091
|
+
const progress = React19.useMemo(() => readProgress(lastCard == null ? void 0 : lastCard.response), [lastCard]);
|
|
2092
|
+
React19.useEffect(() => {
|
|
1660
2093
|
var _a3;
|
|
1661
2094
|
if (started.current) return;
|
|
1662
2095
|
started.current = true;
|
|
@@ -1666,12 +2099,12 @@ var OnboardingFlow = ({
|
|
|
1666
2099
|
resumed ? { type: "resumed", contextId: sessionId } : { type: "started", contextId: sessionId }
|
|
1667
2100
|
);
|
|
1668
2101
|
}, [sendMessage, startMessage, resumed, sessionId]);
|
|
1669
|
-
|
|
2102
|
+
React19.useEffect(() => {
|
|
1670
2103
|
if (lastCard) return;
|
|
1671
2104
|
const timer = setTimeout(() => setTimedOut(true), startTimeoutMs);
|
|
1672
2105
|
return () => clearTimeout(timer);
|
|
1673
2106
|
}, [lastCard, startTimeoutMs]);
|
|
1674
|
-
|
|
2107
|
+
React19.useEffect(() => {
|
|
1675
2108
|
var _a3, _b2, _c2, _d2;
|
|
1676
2109
|
setValidationError(void 0);
|
|
1677
2110
|
if (!(lastCard == null ? void 0 : lastCard.id)) return;
|
|
@@ -1689,7 +2122,7 @@ var OnboardingFlow = ({
|
|
|
1689
2122
|
});
|
|
1690
2123
|
}
|
|
1691
2124
|
}, [lastCard == null ? void 0 : lastCard.id]);
|
|
1692
|
-
|
|
2125
|
+
React19.useEffect(() => {
|
|
1693
2126
|
return () => {
|
|
1694
2127
|
var _a3, _b2, _c2;
|
|
1695
2128
|
if (finished.current || degradedRef.current) return;
|
|
@@ -1703,13 +2136,13 @@ var OnboardingFlow = ({
|
|
|
1703
2136
|
});
|
|
1704
2137
|
};
|
|
1705
2138
|
}, []);
|
|
1706
|
-
const restartThread =
|
|
2139
|
+
const restartThread = React19.useCallback(() => {
|
|
1707
2140
|
setTimedOut(false);
|
|
1708
2141
|
started.current = false;
|
|
1709
2142
|
errored.current = false;
|
|
1710
2143
|
reset();
|
|
1711
2144
|
}, [reset]);
|
|
1712
|
-
|
|
2145
|
+
React19.useEffect(() => {
|
|
1713
2146
|
var _a3, _b2, _c2, _d2, _e2, _f2, _g;
|
|
1714
2147
|
if (errored.current) return;
|
|
1715
2148
|
if (!error && !timedOut) return;
|
|
@@ -1736,22 +2169,52 @@ var OnboardingFlow = ({
|
|
|
1736
2169
|
(_g = onErrorRef.current) == null ? void 0 : _g.call(onErrorRef, error != null ? error : new Error("Onboarding timed out"));
|
|
1737
2170
|
}
|
|
1738
2171
|
}, [error, timedOut, maxRetries]);
|
|
1739
|
-
const handleFinish =
|
|
2172
|
+
const handleFinish = React19.useCallback(() => {
|
|
1740
2173
|
if (finished.current) return;
|
|
1741
2174
|
finished.current = true;
|
|
1742
2175
|
onCompleteRef.current({ answers: deriveAnswers(messages), raw: messages });
|
|
1743
2176
|
}, [messages]);
|
|
1744
|
-
const handleRetry =
|
|
2177
|
+
const handleRetry = React19.useCallback(() => {
|
|
1745
2178
|
attempts.current = 0;
|
|
1746
2179
|
degradedRef.current = false;
|
|
1747
2180
|
setDegraded(false);
|
|
1748
2181
|
restartThread();
|
|
1749
2182
|
}, [restartThread]);
|
|
1750
|
-
const skipQuestion =
|
|
2183
|
+
const skipQuestion = React19.useCallback(() => {
|
|
1751
2184
|
if (isLoading) return;
|
|
1752
2185
|
sendMessage(SKIP_ONE_SENTINEL);
|
|
1753
2186
|
}, [isLoading, sendMessage]);
|
|
1754
|
-
const
|
|
2187
|
+
const emitPermissionStage = React19.useCallback(
|
|
2188
|
+
(permission, stage, status) => {
|
|
2189
|
+
var _a3, _b2, _c2, _d2;
|
|
2190
|
+
(_a3 = onEventRef.current) == null ? void 0 : _a3.call(onEventRef, { type: "permission", permission, stage, status });
|
|
2191
|
+
reportClientEvent(reportTargetRef.current, {
|
|
2192
|
+
event_type: "app_event",
|
|
2193
|
+
session_id: sessionIdRef.current,
|
|
2194
|
+
question_key: permissionEventName(stage),
|
|
2195
|
+
component: PERMISSION_CARD_NAME,
|
|
2196
|
+
screen_index: lastScreenIndexRef.current >= 0 ? lastScreenIndexRef.current : void 0,
|
|
2197
|
+
meta: JSON.stringify(permissionEventProps(permission, status)),
|
|
2198
|
+
device: (_b2 = clientContextRef.current) == null ? void 0 : _b2.device,
|
|
2199
|
+
user_context: (_c2 = clientContextRef.current) == null ? void 0 : _c2.userContext,
|
|
2200
|
+
user_id: (_d2 = clientContextRef.current) == null ? void 0 : _d2.userId
|
|
2201
|
+
});
|
|
2202
|
+
},
|
|
2203
|
+
[]
|
|
2204
|
+
);
|
|
2205
|
+
const settlePermission = React19.useCallback(
|
|
2206
|
+
(id, screen, outcome) => {
|
|
2207
|
+
var _a3, _b2;
|
|
2208
|
+
setLocallySettledPermissions((prev) => prev.includes(id) ? prev : [...prev, id]);
|
|
2209
|
+
(_a3 = onPermissionSettledRef.current) == null ? void 0 : _a3.call(onPermissionSettledRef, id, outcome);
|
|
2210
|
+
try {
|
|
2211
|
+
(_b2 = screen.onResult) == null ? void 0 : _b2.call(screen, screen.permission, outcome);
|
|
2212
|
+
} catch {
|
|
2213
|
+
}
|
|
2214
|
+
},
|
|
2215
|
+
[]
|
|
2216
|
+
);
|
|
2217
|
+
const previewOnSelect = React19.useCallback((value) => {
|
|
1755
2218
|
const target = reportTargetRef.current;
|
|
1756
2219
|
if (!(target == null ? void 0 : target.serverUrl)) return;
|
|
1757
2220
|
if (Array.isArray(value) ? value.length === 0 : !value) return;
|
|
@@ -1763,15 +2226,15 @@ var OnboardingFlow = ({
|
|
|
1763
2226
|
});
|
|
1764
2227
|
}, 300);
|
|
1765
2228
|
}, []);
|
|
1766
|
-
|
|
2229
|
+
React19.useEffect(() => () => {
|
|
1767
2230
|
if (previewTimer.current) clearTimeout(previewTimer.current);
|
|
1768
2231
|
}, []);
|
|
1769
|
-
const wrappedActions =
|
|
2232
|
+
const wrappedActions = React19.useCallback(
|
|
1770
2233
|
(messageId) => {
|
|
1771
2234
|
var _a3;
|
|
1772
2235
|
const base = makeActions(messageId);
|
|
1773
|
-
const
|
|
1774
|
-
const validator =
|
|
2236
|
+
const validators2 = validatorsRef.current;
|
|
2237
|
+
const validator = (_a3 = progress.slot_id ? validators2 == null ? void 0 : validators2[progress.slot_id] : void 0) != null ? _a3 : progress.key ? validators2 == null ? void 0 : validators2[progress.key] : void 0;
|
|
1775
2238
|
if (!validator) return base;
|
|
1776
2239
|
const out = { ...base };
|
|
1777
2240
|
for (const name2 of ANSWER_CALLBACKS) {
|
|
@@ -1796,7 +2259,7 @@ var OnboardingFlow = ({
|
|
|
1796
2259
|
}
|
|
1797
2260
|
return out;
|
|
1798
2261
|
},
|
|
1799
|
-
[makeActions, progress.key]
|
|
2262
|
+
[makeActions, progress.key, progress.slot_id]
|
|
1800
2263
|
);
|
|
1801
2264
|
if (degraded) {
|
|
1802
2265
|
if (fallbackFlow !== void 0) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallbackFlow });
|
|
@@ -1821,6 +2284,36 @@ var OnboardingFlow = ({
|
|
|
1821
2284
|
const step = (_a2 = progress.step) != null ? _a2 : renderedCount;
|
|
1822
2285
|
const total = (_b = progress.total) != null ? _b : approxScreens;
|
|
1823
2286
|
const isTerminal = response.action === "render" && response.component === "StatusCard";
|
|
2287
|
+
const duePermission = permissionsPending ? void 0 : selectDuePermissionScreen(
|
|
2288
|
+
permissionScreens,
|
|
2289
|
+
settledPermissions ? [...settledPermissions, ...locallySettledPermissions] : locallySettledPermissions,
|
|
2290
|
+
{ cardIndex: renderedCount - 1, isTerminal }
|
|
2291
|
+
);
|
|
2292
|
+
if (duePermission) {
|
|
2293
|
+
const { screen, id } = duePermission;
|
|
2294
|
+
const copy2 = resolvePermissionCopy(screen.permission, screen.copy);
|
|
2295
|
+
return /* @__PURE__ */ jsxRuntime.jsx(OnboardingScaffold, { step, approxScreens: total, children: /* @__PURE__ */ jsxRuntime.jsx(CardHandoff, { transitionKey: `permission:${id}`, variant: "spring", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2296
|
+
PermissionCardView,
|
|
2297
|
+
{
|
|
2298
|
+
permission: screen.permission,
|
|
2299
|
+
title: copy2.title,
|
|
2300
|
+
message: copy2.message,
|
|
2301
|
+
primaryLabel: copy2.primaryLabel,
|
|
2302
|
+
secondaryLabel: copy2.secondaryLabel,
|
|
2303
|
+
blockedTitle: copy2.blockedTitle,
|
|
2304
|
+
blockedMessage: copy2.blockedMessage,
|
|
2305
|
+
settingsLabel: copy2.settingsLabel,
|
|
2306
|
+
continueLabel: copy2.continueLabel,
|
|
2307
|
+
illustration: screen.illustration,
|
|
2308
|
+
request: screen.request,
|
|
2309
|
+
getStatus: screen.getStatus,
|
|
2310
|
+
openSettings: screen.openSettings,
|
|
2311
|
+
onStage: (stage, status) => emitPermissionStage(screen.permission, stage, status),
|
|
2312
|
+
onSettled: (outcome) => settlePermission(id, screen, outcome)
|
|
2313
|
+
},
|
|
2314
|
+
id
|
|
2315
|
+
) }) });
|
|
2316
|
+
}
|
|
1824
2317
|
if (isTerminal) {
|
|
1825
2318
|
const props = (_c = response.props) != null ? _c : {};
|
|
1826
2319
|
return /* @__PURE__ */ jsxRuntime.jsx(OnboardingScaffold, { step, complete: true, approxScreens: total, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1879,8 +2372,8 @@ var OnboardingFlow = ({
|
|
|
1879
2372
|
}
|
|
1880
2373
|
);
|
|
1881
2374
|
};
|
|
1882
|
-
var
|
|
1883
|
-
var
|
|
2375
|
+
var easeWire5 = reactNative.Easing.bezier(...WIRE_BEZIER);
|
|
2376
|
+
var schema2 = zod.z.object({
|
|
1884
2377
|
title: zod.z.string().describe("Question or section header, e.g. 'Which season?'"),
|
|
1885
2378
|
chips: zod.z.array(zod.z.string()).min(1).describe("List of option labels shown as chips, e.g. ['Spring','Summer','Autumn','Winter']"),
|
|
1886
2379
|
multiSelect: zod.z.boolean().optional().describe("Allow multiple chips selected at once, default true"),
|
|
@@ -1890,9 +2383,9 @@ var schema = zod.z.object({
|
|
|
1890
2383
|
var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
|
|
1891
2384
|
const t = useOnboardingTheme();
|
|
1892
2385
|
const reduced = useReducedMotion();
|
|
1893
|
-
const handlePress =
|
|
1894
|
-
const pressScale =
|
|
1895
|
-
const pressTo =
|
|
2386
|
+
const handlePress = React19.useCallback(() => onToggle(chip), [chip, onToggle]);
|
|
2387
|
+
const pressScale = React19.useRef(new reactNative.Animated.Value(1)).current;
|
|
2388
|
+
const pressTo = React19.useCallback(
|
|
1896
2389
|
(to) => {
|
|
1897
2390
|
if (reduced) return;
|
|
1898
2391
|
reactNative.Animated.spring(pressScale, {
|
|
@@ -1904,21 +2397,21 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
|
|
|
1904
2397
|
},
|
|
1905
2398
|
[pressScale, reduced]
|
|
1906
2399
|
);
|
|
1907
|
-
const handlePressIn =
|
|
1908
|
-
const handlePressOut =
|
|
1909
|
-
const fillT =
|
|
1910
|
-
|
|
2400
|
+
const handlePressIn = React19.useCallback(() => pressTo(CHIP_PRESS_SCALE), [pressTo]);
|
|
2401
|
+
const handlePressOut = React19.useCallback(() => pressTo(1), [pressTo]);
|
|
2402
|
+
const fillT = React19.useRef(new reactNative.Animated.Value(isSelected ? 1 : 0)).current;
|
|
2403
|
+
React19.useEffect(() => {
|
|
1911
2404
|
const anim = reactNative.Animated.timing(fillT, {
|
|
1912
2405
|
toValue: isSelected ? 1 : 0,
|
|
1913
2406
|
duration: scaledMs(stateChangeDuration(reduced, CHIP_FILL_MS)),
|
|
1914
|
-
easing:
|
|
2407
|
+
easing: easeWire5,
|
|
1915
2408
|
useNativeDriver: true
|
|
1916
2409
|
});
|
|
1917
2410
|
anim.start();
|
|
1918
2411
|
return () => anim.stop();
|
|
1919
2412
|
}, [isSelected, reduced, fillT]);
|
|
1920
|
-
const checkT =
|
|
1921
|
-
|
|
2413
|
+
const checkT = React19.useRef(new reactNative.Animated.Value(isSelected ? 1 : 0)).current;
|
|
2414
|
+
React19.useEffect(() => {
|
|
1922
2415
|
if (!isSelected) {
|
|
1923
2416
|
checkT.setValue(0);
|
|
1924
2417
|
return;
|
|
@@ -1951,7 +2444,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
|
|
|
1951
2444
|
reactNative.Animated.View,
|
|
1952
2445
|
{
|
|
1953
2446
|
style: [
|
|
1954
|
-
|
|
2447
|
+
styles11.chip,
|
|
1955
2448
|
{
|
|
1956
2449
|
borderRadius: t.radius.full,
|
|
1957
2450
|
paddingHorizontal: t.spacing.md,
|
|
@@ -1968,7 +2461,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
|
|
|
1968
2461
|
pointerEvents: "none",
|
|
1969
2462
|
style: [
|
|
1970
2463
|
reactNative.StyleSheet.absoluteFill,
|
|
1971
|
-
|
|
2464
|
+
styles11.fillOverlay,
|
|
1972
2465
|
{
|
|
1973
2466
|
borderRadius: t.radius.full,
|
|
1974
2467
|
borderColor: t.colors.primary,
|
|
@@ -1982,7 +2475,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
|
|
|
1982
2475
|
reactNative.Animated.Text,
|
|
1983
2476
|
{
|
|
1984
2477
|
style: [
|
|
1985
|
-
|
|
2478
|
+
styles11.check,
|
|
1986
2479
|
{ color: t.colors.onPrimary, opacity: checkT, transform: [{ scale: checkScale }] }
|
|
1987
2480
|
],
|
|
1988
2481
|
children: "\u2713"
|
|
@@ -1993,7 +2486,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
|
|
|
1993
2486
|
{
|
|
1994
2487
|
style: [
|
|
1995
2488
|
captionStyle(t.fonts),
|
|
1996
|
-
|
|
2489
|
+
styles11.chipLabel,
|
|
1997
2490
|
{ color: isSelected ? t.colors.onPrimary : t.colors.textMuted }
|
|
1998
2491
|
],
|
|
1999
2492
|
children: chip
|
|
@@ -2005,7 +2498,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
|
|
|
2005
2498
|
}
|
|
2006
2499
|
);
|
|
2007
2500
|
};
|
|
2008
|
-
var ChipItem =
|
|
2501
|
+
var ChipItem = React19__default.default.memo(_ChipItem);
|
|
2009
2502
|
var _ChipSelectCard = ({
|
|
2010
2503
|
title,
|
|
2011
2504
|
chips,
|
|
@@ -2016,12 +2509,12 @@ var _ChipSelectCard = ({
|
|
|
2016
2509
|
onSelectionChange
|
|
2017
2510
|
}) => {
|
|
2018
2511
|
const t = useOnboardingTheme();
|
|
2019
|
-
const [selected, setSelected] =
|
|
2020
|
-
const [submitted, setSubmitted] =
|
|
2021
|
-
|
|
2512
|
+
const [selected, setSelected] = React19.useState([]);
|
|
2513
|
+
const [submitted, setSubmitted] = React19.useState(false);
|
|
2514
|
+
React19.useEffect(() => {
|
|
2022
2515
|
if (!submitted && selected.length) onSelectionChange == null ? void 0 : onSelectionChange(multiSelect ? selected : selected[0]);
|
|
2023
2516
|
}, [selected, submitted, multiSelect, onSelectionChange]);
|
|
2024
|
-
const toggle =
|
|
2517
|
+
const toggle = React19.useCallback(
|
|
2025
2518
|
(chip) => {
|
|
2026
2519
|
if (submitted) return;
|
|
2027
2520
|
if (multiSelect) {
|
|
@@ -2036,7 +2529,7 @@ var _ChipSelectCard = ({
|
|
|
2036
2529
|
},
|
|
2037
2530
|
[submitted, multiSelect, maxSelections]
|
|
2038
2531
|
);
|
|
2039
|
-
const handleSubmit =
|
|
2532
|
+
const handleSubmit = React19.useCallback(() => {
|
|
2040
2533
|
if (!selected.length || !onSelect || submitted) return;
|
|
2041
2534
|
setSubmitted(true);
|
|
2042
2535
|
onSelect(multiSelect ? selected : selected[0]);
|
|
@@ -2055,7 +2548,7 @@ var _ChipSelectCard = ({
|
|
|
2055
2548
|
disabled: selected.length === 0 || submitted
|
|
2056
2549
|
}
|
|
2057
2550
|
),
|
|
2058
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [
|
|
2551
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles11.chipsRow, { gap: t.spacing.sm + t.spacing.xs }], children: chips.map((chip) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2059
2552
|
ChipItem,
|
|
2060
2553
|
{
|
|
2061
2554
|
chip,
|
|
@@ -2071,18 +2564,18 @@ var _ChipSelectCard = ({
|
|
|
2071
2564
|
var ChipSelectCard = {
|
|
2072
2565
|
name: "ChipSelectCard",
|
|
2073
2566
|
description: "Use for compact short labels: seasons, activities, travel styles, moods, categories. Ideal for 4\u201312 single-word or two-word options. Use SelectionCard instead when options need descriptions; use CardGridSelectCard for a smaller set (2\u20136) of visual choices that each deserve an icon.",
|
|
2074
|
-
component:
|
|
2075
|
-
propsSchema:
|
|
2567
|
+
component: React19__default.default.memo(_ChipSelectCard),
|
|
2568
|
+
propsSchema: schema2,
|
|
2076
2569
|
defaultProps: { multiSelect: true, submitLabel: "Continue" }
|
|
2077
2570
|
};
|
|
2078
|
-
var
|
|
2571
|
+
var styles11 = reactNative.StyleSheet.create({
|
|
2079
2572
|
chipsRow: { flexDirection: "row", flexWrap: "wrap" },
|
|
2080
2573
|
chip: { borderWidth: 1.5, flexDirection: "row", alignItems: "center", gap: 6, overflow: "hidden" },
|
|
2081
2574
|
fillOverlay: { borderWidth: 1.5 },
|
|
2082
2575
|
chipLabel: { fontWeight: "600" },
|
|
2083
2576
|
check: { fontSize: 13, fontWeight: "800", lineHeight: 15 }
|
|
2084
2577
|
});
|
|
2085
|
-
var
|
|
2578
|
+
var schema3 = zod.z.object({
|
|
2086
2579
|
label: zod.z.string().describe("Input field label / the question being asked"),
|
|
2087
2580
|
hint: zod.z.string().optional().describe(
|
|
2088
2581
|
"One short line under the question giving context: what the answer is used for or why you ask. Add it whenever the question alone might be unclear."
|
|
@@ -2107,14 +2600,14 @@ var _TextInputCard = ({
|
|
|
2107
2600
|
lines = 6
|
|
2108
2601
|
}) => {
|
|
2109
2602
|
const t = useOnboardingTheme();
|
|
2110
|
-
const styles18 =
|
|
2603
|
+
const styles18 = React19.useMemo(() => makeStyles3(t), [t]);
|
|
2111
2604
|
const { height: screenHeight } = reactNative.useWindowDimensions();
|
|
2112
|
-
const [value, setValue] =
|
|
2113
|
-
const [submitted, setSubmitted] =
|
|
2605
|
+
const [value, setValue] = React19.useState("");
|
|
2606
|
+
const [submitted, setSubmitted] = React19.useState(false);
|
|
2114
2607
|
const locked = submitted && !validationError;
|
|
2115
2608
|
const APPROX_ROW_HEIGHT = 26;
|
|
2116
2609
|
const minHeight = multiline ? Math.min(Math.max(lines, 4) * APPROX_ROW_HEIGHT + t.spacing.md * 2, screenHeight * 0.4) : 52;
|
|
2117
|
-
const handleSubmit =
|
|
2610
|
+
const handleSubmit = React19.useCallback(() => {
|
|
2118
2611
|
if (value.trim() && onSubmit && !locked) {
|
|
2119
2612
|
setSubmitted(true);
|
|
2120
2613
|
onSubmit(value.trim());
|
|
@@ -2168,8 +2661,8 @@ var _TextInputCard = ({
|
|
|
2168
2661
|
var TextInputCard = {
|
|
2169
2662
|
name: "TextInputCard",
|
|
2170
2663
|
description: "Use when the user needs to type a free-text answer: an open-ended goal, notes, feedback, a name, a destination, a search query. Multi-line by default (great for open-ended goals); set multiline:false for a short single-line field like a name. Use NumberStepperCard for numeric ranges with known bounds.",
|
|
2171
|
-
component:
|
|
2172
|
-
propsSchema:
|
|
2664
|
+
component: React19__default.default.memo(_TextInputCard),
|
|
2665
|
+
propsSchema: schema3,
|
|
2173
2666
|
defaultProps: { submitLabel: "Submit", multiline: true, lines: 6 }
|
|
2174
2667
|
};
|
|
2175
2668
|
function makeStyles3(t) {
|
|
@@ -2324,7 +2817,7 @@ var _WireIcon = ({ name: name2, size = 20, color, requireModule }) => {
|
|
|
2324
2817
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2325
2818
|
reactNative.View,
|
|
2326
2819
|
{
|
|
2327
|
-
style:
|
|
2820
|
+
style: styles12.decorative,
|
|
2328
2821
|
accessible: false,
|
|
2329
2822
|
importantForAccessibility: "no-hide-descendants",
|
|
2330
2823
|
children: hostIcon
|
|
@@ -2338,7 +2831,7 @@ var _WireIcon = ({ name: name2, size = 20, color, requireModule }) => {
|
|
|
2338
2831
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2339
2832
|
reactNative.View,
|
|
2340
2833
|
{
|
|
2341
|
-
style:
|
|
2834
|
+
style: styles12.decorative,
|
|
2342
2835
|
accessible: false,
|
|
2343
2836
|
importantForAccessibility: "no-hide-descendants",
|
|
2344
2837
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2353,9 +2846,9 @@ var _WireIcon = ({ name: name2, size = 20, color, requireModule }) => {
|
|
|
2353
2846
|
}
|
|
2354
2847
|
);
|
|
2355
2848
|
};
|
|
2356
|
-
var WireIcon =
|
|
2849
|
+
var WireIcon = React19__default.default.memo(_WireIcon);
|
|
2357
2850
|
WireIcon.displayName = "WireIcon";
|
|
2358
|
-
var
|
|
2851
|
+
var styles12 = reactNative.StyleSheet.create({
|
|
2359
2852
|
// No size/margin of its own: the glyph decides its footprint, so an absent icon and a
|
|
2360
2853
|
// present one differ by exactly the glyph — never by a wrapper.
|
|
2361
2854
|
decorative: {}
|
|
@@ -2369,8 +2862,8 @@ var optionObjectSchema = zod.z.object({
|
|
|
2369
2862
|
var coerceOptions = (arr) => Array.isArray(arr) ? arr.map((item) => typeof item === "string" ? { value: item, label: item } : item) : arr;
|
|
2370
2863
|
var optionsField = (description) => zod.z.preprocess(coerceOptions, zod.z.array(optionObjectSchema).min(1)).describe(description);
|
|
2371
2864
|
var normalizeOptions = (options) => options.map((opt) => typeof opt === "string" ? { value: opt, label: opt } : opt);
|
|
2372
|
-
var
|
|
2373
|
-
var
|
|
2865
|
+
var easeWire6 = reactNative.Easing.bezier(...WIRE_BEZIER);
|
|
2866
|
+
var schema4 = zod.z.object({
|
|
2374
2867
|
title: zod.z.string().describe("Question or prompt for the user"),
|
|
2375
2868
|
options: optionsField("List of choices"),
|
|
2376
2869
|
submitLabel: zod.z.string().optional().describe("Submit button label, default: Continue"),
|
|
@@ -2379,9 +2872,9 @@ var schema3 = zod.z.object({
|
|
|
2379
2872
|
var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
|
|
2380
2873
|
const t = useOnboardingTheme();
|
|
2381
2874
|
const reduced = useReducedMotion();
|
|
2382
|
-
const handlePress =
|
|
2383
|
-
const pressScale =
|
|
2384
|
-
const pressTo =
|
|
2875
|
+
const handlePress = React19.useCallback(() => onToggle(opt.value), [opt.value, onToggle]);
|
|
2876
|
+
const pressScale = React19.useRef(new reactNative.Animated.Value(1)).current;
|
|
2877
|
+
const pressTo = React19.useCallback(
|
|
2385
2878
|
(to) => {
|
|
2386
2879
|
if (reduced) return;
|
|
2387
2880
|
reactNative.Animated.spring(pressScale, {
|
|
@@ -2393,21 +2886,21 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
|
|
|
2393
2886
|
},
|
|
2394
2887
|
[pressScale, reduced]
|
|
2395
2888
|
);
|
|
2396
|
-
const handlePressIn =
|
|
2397
|
-
const handlePressOut =
|
|
2398
|
-
const selectT =
|
|
2399
|
-
|
|
2889
|
+
const handlePressIn = React19.useCallback(() => pressTo(ROW_PRESS_SCALE), [pressTo]);
|
|
2890
|
+
const handlePressOut = React19.useCallback(() => pressTo(1), [pressTo]);
|
|
2891
|
+
const selectT = React19.useRef(new reactNative.Animated.Value(isSelected ? 1 : 0)).current;
|
|
2892
|
+
React19.useEffect(() => {
|
|
2400
2893
|
const anim = reactNative.Animated.timing(selectT, {
|
|
2401
2894
|
toValue: isSelected ? 1 : 0,
|
|
2402
2895
|
duration: scaledMs(stateChangeDuration(reduced, SELECT_FILL_MS)),
|
|
2403
|
-
easing:
|
|
2896
|
+
easing: easeWire6,
|
|
2404
2897
|
useNativeDriver: true
|
|
2405
2898
|
});
|
|
2406
2899
|
anim.start();
|
|
2407
2900
|
return () => anim.stop();
|
|
2408
2901
|
}, [isSelected, reduced, selectT]);
|
|
2409
|
-
const checkT =
|
|
2410
|
-
|
|
2902
|
+
const checkT = React19.useRef(new reactNative.Animated.Value(isSelected ? 1 : 0)).current;
|
|
2903
|
+
React19.useEffect(() => {
|
|
2411
2904
|
if (!isSelected) {
|
|
2412
2905
|
checkT.setValue(0);
|
|
2413
2906
|
return;
|
|
@@ -2440,7 +2933,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
|
|
|
2440
2933
|
reactNative.Animated.View,
|
|
2441
2934
|
{
|
|
2442
2935
|
style: [
|
|
2443
|
-
|
|
2936
|
+
styles13.option,
|
|
2444
2937
|
{
|
|
2445
2938
|
padding: t.spacing.md,
|
|
2446
2939
|
borderRadius: t.radius.md,
|
|
@@ -2457,7 +2950,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
|
|
|
2457
2950
|
pointerEvents: "none",
|
|
2458
2951
|
style: [
|
|
2459
2952
|
reactNative.StyleSheet.absoluteFill,
|
|
2460
|
-
|
|
2953
|
+
styles13.selectedOverlay,
|
|
2461
2954
|
{
|
|
2462
2955
|
borderRadius: t.radius.md,
|
|
2463
2956
|
borderColor: t.colors.primary,
|
|
@@ -2471,7 +2964,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
|
|
|
2471
2964
|
reactNative.View,
|
|
2472
2965
|
{
|
|
2473
2966
|
style: [
|
|
2474
|
-
|
|
2967
|
+
styles13.indicator,
|
|
2475
2968
|
{ borderRadius: t.radius.full },
|
|
2476
2969
|
isSelected ? { borderColor: t.colors.primary, backgroundColor: t.colors.primary } : { borderColor: t.colors.borderStrong, backgroundColor: "transparent" }
|
|
2477
2970
|
],
|
|
@@ -2479,7 +2972,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
|
|
|
2479
2972
|
reactNative.Animated.Text,
|
|
2480
2973
|
{
|
|
2481
2974
|
style: [
|
|
2482
|
-
|
|
2975
|
+
styles13.check,
|
|
2483
2976
|
{ color: t.colors.onPrimary, opacity: checkT, transform: [{ scale: checkScale }] }
|
|
2484
2977
|
],
|
|
2485
2978
|
children: "\u2713"
|
|
@@ -2495,7 +2988,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
|
|
|
2495
2988
|
}
|
|
2496
2989
|
);
|
|
2497
2990
|
};
|
|
2498
|
-
var OptionRow =
|
|
2991
|
+
var OptionRow = React19__default.default.memo(_OptionRow);
|
|
2499
2992
|
var _SelectionCard = ({
|
|
2500
2993
|
title,
|
|
2501
2994
|
options,
|
|
@@ -2505,13 +2998,13 @@ var _SelectionCard = ({
|
|
|
2505
2998
|
onSelectionChange
|
|
2506
2999
|
}) => {
|
|
2507
3000
|
const t = useOnboardingTheme();
|
|
2508
|
-
const opts =
|
|
2509
|
-
const [selected, setSelected] =
|
|
2510
|
-
const [submitted, setSubmitted] =
|
|
2511
|
-
|
|
3001
|
+
const opts = React19__default.default.useMemo(() => normalizeOptions(options), [options]);
|
|
3002
|
+
const [selected, setSelected] = React19.useState([]);
|
|
3003
|
+
const [submitted, setSubmitted] = React19.useState(false);
|
|
3004
|
+
React19.useEffect(() => {
|
|
2512
3005
|
if (!submitted && selected.length) onSelectionChange == null ? void 0 : onSelectionChange(multiSelect ? selected : selected[0]);
|
|
2513
3006
|
}, [selected, submitted, multiSelect, onSelectionChange]);
|
|
2514
|
-
const toggle =
|
|
3007
|
+
const toggle = React19.useCallback(
|
|
2515
3008
|
(value) => {
|
|
2516
3009
|
if (submitted) return;
|
|
2517
3010
|
if (multiSelect) {
|
|
@@ -2522,7 +3015,7 @@ var _SelectionCard = ({
|
|
|
2522
3015
|
},
|
|
2523
3016
|
[submitted, multiSelect]
|
|
2524
3017
|
);
|
|
2525
|
-
const handleSubmit =
|
|
3018
|
+
const handleSubmit = React19.useCallback(() => {
|
|
2526
3019
|
if (!selected.length || !onSelect || submitted) return;
|
|
2527
3020
|
setSubmitted(true);
|
|
2528
3021
|
onSelect(multiSelect ? selected : selected[0]);
|
|
@@ -2556,18 +3049,18 @@ var _SelectionCard = ({
|
|
|
2556
3049
|
var SelectionCard = {
|
|
2557
3050
|
name: "SelectionCard",
|
|
2558
3051
|
description: "Use for mutually exclusive (radio) or multi-select (checkbox) choices from 2\u20138 options, as a vertical list of rows. Best when options are wordy or need reading. Options may carry an optional `icon` from the vocabulary to make the list scannable. Use ChipSelectCard instead when options are short single words; use CardGridSelectCard when the options are visual and each has an obvious icon (occasions, channels, categories).",
|
|
2559
|
-
component:
|
|
2560
|
-
propsSchema:
|
|
3052
|
+
component: React19__default.default.memo(_SelectionCard),
|
|
3053
|
+
propsSchema: schema4,
|
|
2561
3054
|
defaultProps: { submitLabel: "Continue", multiSelect: false }
|
|
2562
3055
|
};
|
|
2563
|
-
var
|
|
3056
|
+
var styles13 = reactNative.StyleSheet.create({
|
|
2564
3057
|
option: { borderWidth: 1.5, flexDirection: "row", alignItems: "center", overflow: "hidden" },
|
|
2565
3058
|
selectedOverlay: { borderWidth: 1.5 },
|
|
2566
3059
|
indicator: { width: 24, height: 24, borderWidth: 2, alignItems: "center", justifyContent: "center" },
|
|
2567
3060
|
check: { fontSize: 13, fontWeight: "800", lineHeight: 15 }
|
|
2568
3061
|
});
|
|
2569
|
-
var
|
|
2570
|
-
var
|
|
3062
|
+
var easeWire7 = reactNative.Easing.bezier(...WIRE_BEZIER);
|
|
3063
|
+
var schema5 = zod.z.object({
|
|
2571
3064
|
title: zod.z.string().describe("Question or prompt for the user, e.g. 'What brings you here?'"),
|
|
2572
3065
|
options: optionsField(
|
|
2573
3066
|
"The choices, 2-6 of them. Give EVERY option an `icon` from the vocabulary \u2014 the icon is the point of this card, and an option without one renders as a bare label next to icon-bearing neighbours. Keep labels short (1-3 words) so they fit a half-width card."
|
|
@@ -2578,9 +3071,9 @@ var schema4 = zod.z.object({
|
|
|
2578
3071
|
var _OptionCard = ({ opt, isSelected, multiSelect, onToggle }) => {
|
|
2579
3072
|
const t = useOnboardingTheme();
|
|
2580
3073
|
const reduced = useReducedMotion();
|
|
2581
|
-
const handlePress =
|
|
2582
|
-
const pressScale =
|
|
2583
|
-
const pressTo =
|
|
3074
|
+
const handlePress = React19.useCallback(() => onToggle(opt.value), [opt.value, onToggle]);
|
|
3075
|
+
const pressScale = React19.useRef(new reactNative.Animated.Value(1)).current;
|
|
3076
|
+
const pressTo = React19.useCallback(
|
|
2584
3077
|
(to) => {
|
|
2585
3078
|
if (reduced) return;
|
|
2586
3079
|
reactNative.Animated.spring(pressScale, {
|
|
@@ -2592,14 +3085,14 @@ var _OptionCard = ({ opt, isSelected, multiSelect, onToggle }) => {
|
|
|
2592
3085
|
},
|
|
2593
3086
|
[pressScale, reduced]
|
|
2594
3087
|
);
|
|
2595
|
-
const handlePressIn =
|
|
2596
|
-
const handlePressOut =
|
|
2597
|
-
const selectT =
|
|
2598
|
-
|
|
3088
|
+
const handlePressIn = React19.useCallback(() => pressTo(ROW_PRESS_SCALE), [pressTo]);
|
|
3089
|
+
const handlePressOut = React19.useCallback(() => pressTo(1), [pressTo]);
|
|
3090
|
+
const selectT = React19.useRef(new reactNative.Animated.Value(isSelected ? 1 : 0)).current;
|
|
3091
|
+
React19.useEffect(() => {
|
|
2599
3092
|
const anim = reactNative.Animated.timing(selectT, {
|
|
2600
3093
|
toValue: isSelected ? 1 : 0,
|
|
2601
3094
|
duration: scaledMs(stateChangeDuration(reduced, SELECT_FILL_MS)),
|
|
2602
|
-
easing:
|
|
3095
|
+
easing: easeWire7,
|
|
2603
3096
|
useNativeDriver: true
|
|
2604
3097
|
});
|
|
2605
3098
|
anim.start();
|
|
@@ -2614,12 +3107,12 @@ var _OptionCard = ({ opt, isSelected, multiSelect, onToggle }) => {
|
|
|
2614
3107
|
accessibilityRole: multiSelect ? "checkbox" : "radio",
|
|
2615
3108
|
accessibilityState: multiSelect ? { checked: isSelected } : { selected: isSelected },
|
|
2616
3109
|
accessibilityLabel: opt.label,
|
|
2617
|
-
style:
|
|
3110
|
+
style: styles14.cell,
|
|
2618
3111
|
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2619
3112
|
reactNative.Animated.View,
|
|
2620
3113
|
{
|
|
2621
3114
|
style: [
|
|
2622
|
-
|
|
3115
|
+
styles14.card,
|
|
2623
3116
|
{
|
|
2624
3117
|
padding: t.spacing.md,
|
|
2625
3118
|
borderRadius: t.radius.lg,
|
|
@@ -2636,7 +3129,7 @@ var _OptionCard = ({ opt, isSelected, multiSelect, onToggle }) => {
|
|
|
2636
3129
|
pointerEvents: "none",
|
|
2637
3130
|
style: [
|
|
2638
3131
|
reactNative.StyleSheet.absoluteFill,
|
|
2639
|
-
|
|
3132
|
+
styles14.selectedOverlay,
|
|
2640
3133
|
{
|
|
2641
3134
|
borderRadius: t.radius.lg,
|
|
2642
3135
|
borderColor: t.colors.primary,
|
|
@@ -2654,7 +3147,7 @@ var _OptionCard = ({ opt, isSelected, multiSelect, onToggle }) => {
|
|
|
2654
3147
|
}
|
|
2655
3148
|
);
|
|
2656
3149
|
};
|
|
2657
|
-
var OptionCard =
|
|
3150
|
+
var OptionCard = React19__default.default.memo(_OptionCard);
|
|
2658
3151
|
var _CardGridSelectCard = ({
|
|
2659
3152
|
title,
|
|
2660
3153
|
options,
|
|
@@ -2664,13 +3157,13 @@ var _CardGridSelectCard = ({
|
|
|
2664
3157
|
onSelectionChange
|
|
2665
3158
|
}) => {
|
|
2666
3159
|
const t = useOnboardingTheme();
|
|
2667
|
-
const opts =
|
|
2668
|
-
const [selected, setSelected] =
|
|
2669
|
-
const [submitted, setSubmitted] =
|
|
2670
|
-
|
|
3160
|
+
const opts = React19__default.default.useMemo(() => normalizeOptions(options), [options]);
|
|
3161
|
+
const [selected, setSelected] = React19.useState([]);
|
|
3162
|
+
const [submitted, setSubmitted] = React19.useState(false);
|
|
3163
|
+
React19.useEffect(() => {
|
|
2671
3164
|
if (!submitted && selected.length) onSelectionChange == null ? void 0 : onSelectionChange(multiSelect ? selected : selected[0]);
|
|
2672
3165
|
}, [selected, submitted, multiSelect, onSelectionChange]);
|
|
2673
|
-
const toggle =
|
|
3166
|
+
const toggle = React19.useCallback(
|
|
2674
3167
|
(value) => {
|
|
2675
3168
|
if (submitted) return;
|
|
2676
3169
|
if (multiSelect) {
|
|
@@ -2683,7 +3176,7 @@ var _CardGridSelectCard = ({
|
|
|
2683
3176
|
},
|
|
2684
3177
|
[submitted, multiSelect]
|
|
2685
3178
|
);
|
|
2686
|
-
const handleSubmit =
|
|
3179
|
+
const handleSubmit = React19.useCallback(() => {
|
|
2687
3180
|
if (!selected.length || !onSelect || submitted) return;
|
|
2688
3181
|
setSubmitted(true);
|
|
2689
3182
|
onSelect(multiSelect ? selected : selected[0]);
|
|
@@ -2701,7 +3194,7 @@ var _CardGridSelectCard = ({
|
|
|
2701
3194
|
disabled: selected.length === 0 || submitted
|
|
2702
3195
|
}
|
|
2703
3196
|
),
|
|
2704
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [
|
|
3197
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles14.grid, { gap: t.spacing.sm }], children: opts.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2705
3198
|
OptionCard,
|
|
2706
3199
|
{
|
|
2707
3200
|
opt,
|
|
@@ -2717,19 +3210,19 @@ var _CardGridSelectCard = ({
|
|
|
2717
3210
|
var CardGridSelectCard = {
|
|
2718
3211
|
name: "CardGridSelectCard",
|
|
2719
3212
|
description: "Use for VISUAL, icon-led choices shown as a 2-column grid of tappable cards (icon on top, short label beneath). Ideal for 2\u20136 concrete options that each have an obvious icon \u2014 occasions, channels, goals, categories \u2014 e.g. 'What brings you here?' or 'Where did you hear about us?'. Multi-select by default. Every option should carry an `icon`. Use SelectionCard instead when options need descriptions or have no natural icon; use ChipSelectCard for many short word-like tags.",
|
|
2720
|
-
component:
|
|
2721
|
-
propsSchema:
|
|
3213
|
+
component: React19__default.default.memo(_CardGridSelectCard),
|
|
3214
|
+
propsSchema: schema5,
|
|
2722
3215
|
defaultProps: { submitLabel: "Continue", multiSelect: true }
|
|
2723
3216
|
};
|
|
2724
|
-
var
|
|
3217
|
+
var styles14 = reactNative.StyleSheet.create({
|
|
2725
3218
|
grid: { flexDirection: "row", flexWrap: "wrap" },
|
|
2726
3219
|
// Two per row: ~47% basis leaves room for the gap, flexGrow fills the remainder exactly.
|
|
2727
3220
|
cell: { flexBasis: "47%", flexGrow: 1 },
|
|
2728
3221
|
card: { borderWidth: 1.5, alignItems: "flex-start", overflow: "hidden" },
|
|
2729
3222
|
selectedOverlay: { borderWidth: 1.5 }
|
|
2730
3223
|
});
|
|
2731
|
-
var
|
|
2732
|
-
var
|
|
3224
|
+
var easeWire8 = reactNative.Easing.bezier(...WIRE_BEZIER);
|
|
3225
|
+
var schema6 = zod.z.object({
|
|
2733
3226
|
status: zod.z.enum(["success", "warning", "error", "info"]).describe("Status type"),
|
|
2734
3227
|
title: zod.z.string().describe("Status title"),
|
|
2735
3228
|
message: zod.z.string().optional().describe("Supporting message"),
|
|
@@ -2747,8 +3240,8 @@ var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSu
|
|
|
2747
3240
|
var _a2;
|
|
2748
3241
|
const t = useOnboardingTheme();
|
|
2749
3242
|
const reduced = useReducedMotion();
|
|
2750
|
-
const [submitted, setSubmitted] =
|
|
2751
|
-
const color =
|
|
3243
|
+
const [submitted, setSubmitted] = React19.useState(false);
|
|
3244
|
+
const color = React19.useMemo(() => {
|
|
2752
3245
|
switch (status) {
|
|
2753
3246
|
case "success":
|
|
2754
3247
|
return t.colors.success;
|
|
@@ -2760,9 +3253,9 @@ var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSu
|
|
|
2760
3253
|
return t.colors.info;
|
|
2761
3254
|
}
|
|
2762
3255
|
}, [status, t.colors]);
|
|
2763
|
-
const popT =
|
|
2764
|
-
const copyT =
|
|
2765
|
-
|
|
3256
|
+
const popT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
|
|
3257
|
+
const copyT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
|
|
3258
|
+
React19.useEffect(() => {
|
|
2766
3259
|
if (reduced) {
|
|
2767
3260
|
popT.setValue(1);
|
|
2768
3261
|
copyT.setValue(1);
|
|
@@ -2779,7 +3272,7 @@ var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSu
|
|
|
2779
3272
|
toValue: 1,
|
|
2780
3273
|
duration: scaledMs(STATUS_COPY_MS),
|
|
2781
3274
|
delay: scaledMs(STATUS_COPY_STAGGER_MS),
|
|
2782
|
-
easing:
|
|
3275
|
+
easing: easeWire8,
|
|
2783
3276
|
useNativeDriver: true
|
|
2784
3277
|
})
|
|
2785
3278
|
];
|
|
@@ -2788,7 +3281,7 @@ var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSu
|
|
|
2788
3281
|
}, [reduced, popT, copyT]);
|
|
2789
3282
|
const popScale = popT.interpolate({ inputRange: [0, 1], outputRange: [STATUS_POP_SCALE_FROM, 1] });
|
|
2790
3283
|
const copyRise = copyT.interpolate({ inputRange: [0, 1], outputRange: [10, 0] });
|
|
2791
|
-
const handleContinue =
|
|
3284
|
+
const handleContinue = React19.useCallback(() => {
|
|
2792
3285
|
if (!submitted) {
|
|
2793
3286
|
setSubmitted(true);
|
|
2794
3287
|
if (onContinue) onContinue();
|
|
@@ -2801,8 +3294,8 @@ var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSu
|
|
|
2801
3294
|
{
|
|
2802
3295
|
align: "center",
|
|
2803
3296
|
footer: ctaLabel ? /* @__PURE__ */ jsxRuntime.jsx(Button, { title: ctaLabel, onPress: handleContinue, variant: "primary", disabled: submitted }) : void 0,
|
|
2804
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [
|
|
2805
|
-
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [
|
|
3297
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles15.center, { gap: t.spacing.sm }], children: [
|
|
3298
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles15.titleRow, { gap: t.spacing.sm }], children: [
|
|
2806
3299
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2807
3300
|
reactNative.Animated.Text,
|
|
2808
3301
|
{
|
|
@@ -2846,14 +3339,14 @@ var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSu
|
|
|
2846
3339
|
var StatusCard = {
|
|
2847
3340
|
name: "StatusCard",
|
|
2848
3341
|
description: "Use ONLY for real outcomes: booking confirmed, data saved, action completed. NEVER for 'you just viewed X' acknowledgments. Always include ctaLabel so the user can continue after seeing the status.",
|
|
2849
|
-
component:
|
|
2850
|
-
propsSchema:
|
|
3342
|
+
component: React19__default.default.memo(_StatusCard),
|
|
3343
|
+
propsSchema: schema6
|
|
2851
3344
|
};
|
|
2852
|
-
var
|
|
3345
|
+
var styles15 = reactNative.StyleSheet.create({
|
|
2853
3346
|
center: { width: "100%", alignItems: "center", justifyContent: "center" },
|
|
2854
3347
|
titleRow: { flexDirection: "row", alignItems: "center", justifyContent: "center" }
|
|
2855
3348
|
});
|
|
2856
|
-
var
|
|
3349
|
+
var schema7 = zod.z.object({
|
|
2857
3350
|
label: zod.z.string().describe("What is being counted, e.g. 'Number of days' or 'Travellers'"),
|
|
2858
3351
|
min: zod.z.number().optional().describe("Minimum value, default 1"),
|
|
2859
3352
|
max: zod.z.number().optional().describe("Maximum value, default 30"),
|
|
@@ -2874,22 +3367,22 @@ var _NumberStepperCard = ({
|
|
|
2874
3367
|
onSubmit
|
|
2875
3368
|
}) => {
|
|
2876
3369
|
const t = useOnboardingTheme();
|
|
2877
|
-
const styles18 =
|
|
3370
|
+
const styles18 = React19.useMemo(() => makeStyles4(t), [t]);
|
|
2878
3371
|
const safeMin = safeNum(min, 1);
|
|
2879
3372
|
const safeMaxRaw = safeNum(max, 30);
|
|
2880
3373
|
const safeMax = safeMaxRaw < safeMin ? safeMin : safeMaxRaw;
|
|
2881
3374
|
const safeStepRaw = safeNum(step, 1);
|
|
2882
3375
|
const safeStep = safeStepRaw > 0 ? safeStepRaw : 1;
|
|
2883
3376
|
const safeDefault = safeNum(defaultValue, safeMin);
|
|
2884
|
-
const [value, setValue] =
|
|
2885
|
-
const [submitted, setSubmitted] =
|
|
2886
|
-
const handleDecrement =
|
|
3377
|
+
const [value, setValue] = React19.useState(Math.min(Math.max(safeDefault, safeMin), safeMax));
|
|
3378
|
+
const [submitted, setSubmitted] = React19.useState(false);
|
|
3379
|
+
const handleDecrement = React19.useCallback(() => {
|
|
2887
3380
|
if (!submitted) setValue((v) => Math.max(v - safeStep, safeMin));
|
|
2888
3381
|
}, [submitted, safeStep, safeMin]);
|
|
2889
|
-
const handleIncrement =
|
|
3382
|
+
const handleIncrement = React19.useCallback(() => {
|
|
2890
3383
|
if (!submitted) setValue((v) => Math.min(v + safeStep, safeMax));
|
|
2891
3384
|
}, [submitted, safeStep, safeMax]);
|
|
2892
|
-
const handleSubmit =
|
|
3385
|
+
const handleSubmit = React19.useCallback(() => {
|
|
2893
3386
|
if (!submitted) {
|
|
2894
3387
|
setSubmitted(true);
|
|
2895
3388
|
onSubmit == null ? void 0 : onSubmit(value);
|
|
@@ -2924,8 +3417,8 @@ var _NumberStepperCard = ({
|
|
|
2924
3417
|
var NumberStepperCard = {
|
|
2925
3418
|
name: "NumberStepperCard",
|
|
2926
3419
|
description: "Use for any bounded numeric input: days, people, portions, budget tiers, ratings. Best for ranges 1\u201399. Set unit (e.g. 'days', 'people') for clarity.",
|
|
2927
|
-
component:
|
|
2928
|
-
propsSchema:
|
|
3420
|
+
component: React19__default.default.memo(_NumberStepperCard),
|
|
3421
|
+
propsSchema: schema7,
|
|
2929
3422
|
defaultProps: { min: 1, max: 30, defaultValue: 1, step: 1, submitLabel: "Confirm" }
|
|
2930
3423
|
};
|
|
2931
3424
|
function makeStyles4(t) {
|
|
@@ -2936,9 +3429,9 @@ function makeStyles4(t) {
|
|
|
2936
3429
|
valueBox: { alignItems: "center", minWidth: 80 }
|
|
2937
3430
|
});
|
|
2938
3431
|
}
|
|
2939
|
-
var
|
|
2940
|
-
var
|
|
2941
|
-
var
|
|
3432
|
+
var easeWire9 = reactNative.Easing.bezier(...WIRE_BEZIER);
|
|
3433
|
+
var RISE_PX2 = 10;
|
|
3434
|
+
var schema8 = zod.z.object({
|
|
2942
3435
|
title: zod.z.string().describe("Short, warm headline for the value/momentum moment"),
|
|
2943
3436
|
body: zod.z.string().optional().describe("One or two supporting lines that reflect the user's answers back and build momentum"),
|
|
2944
3437
|
items: zod.z.array(zod.z.string()).max(4).optional().describe(
|
|
@@ -2962,9 +3455,9 @@ var _WorkItem = ({
|
|
|
2962
3455
|
reduced
|
|
2963
3456
|
}) => {
|
|
2964
3457
|
const t = useOnboardingTheme();
|
|
2965
|
-
const rowT =
|
|
2966
|
-
const checkT =
|
|
2967
|
-
|
|
3458
|
+
const rowT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
|
|
3459
|
+
const checkT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
|
|
3460
|
+
React19.useEffect(() => {
|
|
2968
3461
|
if (reduced) {
|
|
2969
3462
|
rowT.setValue(1);
|
|
2970
3463
|
checkT.setValue(1);
|
|
@@ -2975,7 +3468,7 @@ var _WorkItem = ({
|
|
|
2975
3468
|
toValue: 1,
|
|
2976
3469
|
duration: scaledMs(INTERSTITIAL_ITEM_MS),
|
|
2977
3470
|
delay: scaledMs(interstitialItemDelay(index)),
|
|
2978
|
-
easing:
|
|
3471
|
+
easing: easeWire9,
|
|
2979
3472
|
useNativeDriver: true
|
|
2980
3473
|
}),
|
|
2981
3474
|
reactNative.Animated.spring(checkT, {
|
|
@@ -2989,7 +3482,7 @@ var _WorkItem = ({
|
|
|
2989
3482
|
anims.forEach((a) => a.start());
|
|
2990
3483
|
return () => anims.forEach((a) => a.stop());
|
|
2991
3484
|
}, [reduced, index, rowT, checkT]);
|
|
2992
|
-
const rise = rowT.interpolate({ inputRange: [0, 1], outputRange: [
|
|
3485
|
+
const rise = rowT.interpolate({ inputRange: [0, 1], outputRange: [RISE_PX2, 0] });
|
|
2993
3486
|
const checkScale = checkT.interpolate({
|
|
2994
3487
|
inputRange: [0, 1],
|
|
2995
3488
|
outputRange: [INTERSTITIAL_CHECK_SCALE_FROM, 1]
|
|
@@ -2997,20 +3490,20 @@ var _WorkItem = ({
|
|
|
2997
3490
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2998
3491
|
reactNative.Animated.View,
|
|
2999
3492
|
{
|
|
3000
|
-
style: [
|
|
3493
|
+
style: [styles16.itemRow, { gap: t.spacing.sm + t.spacing.xs, opacity: rowT, transform: [{ translateY: rise }] }],
|
|
3001
3494
|
children: [
|
|
3002
3495
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3003
3496
|
reactNative.View,
|
|
3004
3497
|
{
|
|
3005
3498
|
style: [
|
|
3006
|
-
|
|
3499
|
+
styles16.itemBadge,
|
|
3007
3500
|
{ backgroundColor: t.colors.primarySoft, borderRadius: t.radius.full }
|
|
3008
3501
|
],
|
|
3009
3502
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3010
3503
|
reactNative.Animated.Text,
|
|
3011
3504
|
{
|
|
3012
3505
|
style: [
|
|
3013
|
-
|
|
3506
|
+
styles16.itemCheck,
|
|
3014
3507
|
{ color: t.colors.primary, opacity: checkT, transform: [{ scale: checkScale }] }
|
|
3015
3508
|
],
|
|
3016
3509
|
children: "\u2713"
|
|
@@ -3018,12 +3511,12 @@ var _WorkItem = ({
|
|
|
3018
3511
|
)
|
|
3019
3512
|
}
|
|
3020
3513
|
),
|
|
3021
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [bodyStyle(t.fonts),
|
|
3514
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [bodyStyle(t.fonts), styles16.itemText, { color: t.colors.text }], children: text })
|
|
3022
3515
|
]
|
|
3023
3516
|
}
|
|
3024
3517
|
);
|
|
3025
3518
|
};
|
|
3026
|
-
var WorkItem =
|
|
3519
|
+
var WorkItem = React19__default.default.memo(_WorkItem);
|
|
3027
3520
|
var _InterstitialCard = ({
|
|
3028
3521
|
title,
|
|
3029
3522
|
body,
|
|
@@ -3039,17 +3532,17 @@ var _InterstitialCard = ({
|
|
|
3039
3532
|
const reduced = useReducedMotion();
|
|
3040
3533
|
const node = useIllustration(illustration);
|
|
3041
3534
|
const fallbackNode = useIllustration(illustrationFallback);
|
|
3042
|
-
const [submitted, setSubmitted] =
|
|
3043
|
-
const [imgFailed, setImgFailed] =
|
|
3044
|
-
const [canAdvance, setCanAdvance] =
|
|
3045
|
-
|
|
3535
|
+
const [submitted, setSubmitted] = React19.useState(false);
|
|
3536
|
+
const [imgFailed, setImgFailed] = React19.useState(false);
|
|
3537
|
+
const [canAdvance, setCanAdvance] = React19.useState(false);
|
|
3538
|
+
React19.useEffect(() => {
|
|
3046
3539
|
const timer = setTimeout(() => setCanAdvance(true), MIN_DISPLAY_MS);
|
|
3047
3540
|
return () => clearTimeout(timer);
|
|
3048
3541
|
}, []);
|
|
3049
|
-
const artT =
|
|
3050
|
-
const titleT =
|
|
3051
|
-
const bodyT =
|
|
3052
|
-
|
|
3542
|
+
const artT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
|
|
3543
|
+
const titleT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
|
|
3544
|
+
const bodyT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
|
|
3545
|
+
React19.useEffect(() => {
|
|
3053
3546
|
if (reduced) {
|
|
3054
3547
|
artT.setValue(1);
|
|
3055
3548
|
titleT.setValue(1);
|
|
@@ -3060,7 +3553,7 @@ var _InterstitialCard = ({
|
|
|
3060
3553
|
toValue: 1,
|
|
3061
3554
|
duration: scaledMs(INTERSTITIAL_HEAD_MS),
|
|
3062
3555
|
delay,
|
|
3063
|
-
easing:
|
|
3556
|
+
easing: easeWire9,
|
|
3064
3557
|
useNativeDriver: true
|
|
3065
3558
|
});
|
|
3066
3559
|
const anims = [
|
|
@@ -3073,9 +3566,9 @@ var _InterstitialCard = ({
|
|
|
3073
3566
|
}, [reduced, artT, titleT, bodyT]);
|
|
3074
3567
|
const riseStyle = (value) => ({
|
|
3075
3568
|
opacity: value,
|
|
3076
|
-
transform: [{ translateY: value.interpolate({ inputRange: [0, 1], outputRange: [
|
|
3569
|
+
transform: [{ translateY: value.interpolate({ inputRange: [0, 1], outputRange: [RISE_PX2, 0] }) }]
|
|
3077
3570
|
});
|
|
3078
|
-
const handleContinue =
|
|
3571
|
+
const handleContinue = React19.useCallback(() => {
|
|
3079
3572
|
if (submitted || !canAdvance) return;
|
|
3080
3573
|
setSubmitted(true);
|
|
3081
3574
|
if (onSubmit) onSubmit("continue");
|
|
@@ -3096,18 +3589,18 @@ var _InterstitialCard = ({
|
|
|
3096
3589
|
disabled: submitted || !canAdvance
|
|
3097
3590
|
}
|
|
3098
3591
|
),
|
|
3099
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [
|
|
3100
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Animated.View, { style: [
|
|
3592
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles16.center, { gap: t.spacing.md }], children: [
|
|
3593
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Animated.View, { style: [styles16.fullWidth, riseStyle(artT)], children: showImage ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3101
3594
|
reactNative.Image,
|
|
3102
3595
|
{
|
|
3103
3596
|
source: { uri: imageUrl },
|
|
3104
|
-
style:
|
|
3597
|
+
style: styles16.image,
|
|
3105
3598
|
resizeMode: "contain",
|
|
3106
3599
|
onError: () => setImgFailed(true)
|
|
3107
3600
|
}
|
|
3108
|
-
) : art ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style:
|
|
3601
|
+
) : art ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles16.art, children: art }) : null }),
|
|
3109
3602
|
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Animated.View, { style: riseStyle(titleT), children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [headingStyle(t.fonts), { color: t.colors.text, textAlign: "center" }], children: title }) }),
|
|
3110
|
-
(items == null ? void 0 : items.length) ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [
|
|
3603
|
+
(items == null ? void 0 : items.length) ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles16.items, { gap: t.spacing.md, marginTop: t.spacing.sm }], children: items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(WorkItem, { text: item, index: i, reduced }, `${item}-${i}`)) }) : body ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Animated.View, { style: riseStyle(bodyT), children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted, textAlign: "center" }], children: body }) }) : null
|
|
3111
3604
|
] })
|
|
3112
3605
|
}
|
|
3113
3606
|
);
|
|
@@ -3115,11 +3608,11 @@ var _InterstitialCard = ({
|
|
|
3115
3608
|
var InterstitialCard = {
|
|
3116
3609
|
name: "InterstitialCard",
|
|
3117
3610
|
description: "Use ONCE mid-flow (never first, never last) as a momentum/value screen between questions \u2014 an illustration + a warm line that reflects answers back, like Duolingo/Cal-AI. NOT a question; the user just taps Continue. Set `illustration` to an app-registered artwork name. Prefer `items` (2-3 short ticked lines, each reflecting a real answer) when you have the signal. Skip it if you don't have enough signal to make the line specific.",
|
|
3118
|
-
component:
|
|
3119
|
-
propsSchema:
|
|
3611
|
+
component: React19__default.default.memo(_InterstitialCard),
|
|
3612
|
+
propsSchema: schema8,
|
|
3120
3613
|
defaultProps: { ctaLabel: "Continue", illustrationFallback: "momentum" }
|
|
3121
3614
|
};
|
|
3122
|
-
var
|
|
3615
|
+
var styles16 = reactNative.StyleSheet.create({
|
|
3123
3616
|
center: { width: "100%", alignItems: "center", justifyContent: "center" },
|
|
3124
3617
|
fullWidth: { width: "100%", alignItems: "center" },
|
|
3125
3618
|
art: { alignItems: "center", justifyContent: "center" },
|
|
@@ -3317,6 +3810,33 @@ var collectDeviceContext = () => {
|
|
|
3317
3810
|
return ctx;
|
|
3318
3811
|
};
|
|
3319
3812
|
|
|
3813
|
+
// src/identity/identityRecord.ts
|
|
3814
|
+
var IDENTITY_PROVENANCE_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:identityProvenance");
|
|
3815
|
+
var provenanceGlobal = globalThis;
|
|
3816
|
+
var provenanceRegistry = () => {
|
|
3817
|
+
const existing = provenanceGlobal[IDENTITY_PROVENANCE_SLOT];
|
|
3818
|
+
if (existing) return existing;
|
|
3819
|
+
const created = { host: /* @__PURE__ */ new Map() };
|
|
3820
|
+
provenanceGlobal[IDENTITY_PROVENANCE_SLOT] = created;
|
|
3821
|
+
return created;
|
|
3822
|
+
};
|
|
3823
|
+
var provenanceKey = (space, scope) => `${space}:${scope != null ? scope : "default"}`;
|
|
3824
|
+
var resolveIdentity = (input) => {
|
|
3825
|
+
var _a2;
|
|
3826
|
+
if (typeof input.value !== "string") return void 0;
|
|
3827
|
+
const value = input.value.trim();
|
|
3828
|
+
if (!value) return void 0;
|
|
3829
|
+
const durable = (_a2 = input.durable) != null ? _a2 : input.source === "host";
|
|
3830
|
+
if (input.source === "host") {
|
|
3831
|
+
provenanceRegistry().host.set(provenanceKey(input.space, input.scope), value);
|
|
3832
|
+
}
|
|
3833
|
+
return { value, space: input.space, source: input.source, durable };
|
|
3834
|
+
};
|
|
3835
|
+
var hostIdentity = (space, scope) => provenanceRegistry().host.get(provenanceKey(space, scope));
|
|
3836
|
+
var resetIdentityProvenance = () => {
|
|
3837
|
+
provenanceRegistry().host.clear();
|
|
3838
|
+
};
|
|
3839
|
+
|
|
3320
3840
|
// src/context/deviceId.ts
|
|
3321
3841
|
var AUTO_DEVICE_ID_PREFIX = "wdev_";
|
|
3322
3842
|
var deviceIdStorageKey = (appId) => `wireai:analytics:deviceKey:${appId != null ? appId : "default"}`;
|
|
@@ -3339,24 +3859,37 @@ var startHydration = (registry, appId, storage, minted) => {
|
|
|
3339
3859
|
const existing = registry.pending.get(appId);
|
|
3340
3860
|
if (existing) return existing;
|
|
3341
3861
|
const slot = deviceIdStorageKey(appId);
|
|
3342
|
-
const
|
|
3862
|
+
const degraded = () => {
|
|
3343
3863
|
var _a2;
|
|
3344
|
-
return (_a2 = registry.keys.get(appId)) != null ? _a2 : minted;
|
|
3864
|
+
return { value: (_a2 = registry.keys.get(appId)) != null ? _a2 : minted, durable: false };
|
|
3345
3865
|
};
|
|
3866
|
+
const adopted = (value) => ({ value, durable: true });
|
|
3346
3867
|
let run;
|
|
3347
3868
|
try {
|
|
3348
3869
|
run = Promise.resolve(storage.getItem(slot)).then((saved) => {
|
|
3349
3870
|
const persisted = typeof saved === "string" && saved.trim() ? saved.trim() : void 0;
|
|
3350
3871
|
if (persisted) {
|
|
3351
3872
|
registry.keys.set(appId, persisted);
|
|
3352
|
-
return persisted;
|
|
3873
|
+
return adopted(persisted);
|
|
3353
3874
|
}
|
|
3354
|
-
return Promise.resolve(storage.setItem(slot, minted)).then(
|
|
3355
|
-
|
|
3875
|
+
return Promise.resolve(storage.setItem(slot, minted)).then(
|
|
3876
|
+
() => {
|
|
3877
|
+
var _a2;
|
|
3878
|
+
return adopted((_a2 = registry.keys.get(appId)) != null ? _a2 : minted);
|
|
3879
|
+
},
|
|
3880
|
+
degraded
|
|
3881
|
+
);
|
|
3882
|
+
}).catch(degraded);
|
|
3356
3883
|
} catch {
|
|
3357
|
-
run = Promise.resolve(
|
|
3884
|
+
run = Promise.resolve(degraded());
|
|
3358
3885
|
}
|
|
3359
3886
|
registry.pending.set(appId, run);
|
|
3887
|
+
void run.then((outcome) => {
|
|
3888
|
+
var _a2;
|
|
3889
|
+
if (outcome.durable) return;
|
|
3890
|
+
(_a2 = registry.pending) == null ? void 0 : _a2.delete(appId);
|
|
3891
|
+
registry.hydrating.delete(appId);
|
|
3892
|
+
});
|
|
3360
3893
|
return run;
|
|
3361
3894
|
};
|
|
3362
3895
|
var resolveAutoDeviceKey = (opts = {}) => {
|
|
@@ -3376,14 +3909,20 @@ var resolveAutoDeviceKey = (opts = {}) => {
|
|
|
3376
3909
|
return (_b = registry.keys.get(appId)) != null ? _b : id;
|
|
3377
3910
|
};
|
|
3378
3911
|
var hydrateAutoDeviceKey = async (opts = {}) => {
|
|
3912
|
+
var _a2, _b;
|
|
3913
|
+
return (_b = (_a2 = await hydrateDeviceIdentity(opts)) == null ? void 0 : _a2.value) != null ? _b : resolveAutoDeviceKey(opts);
|
|
3914
|
+
};
|
|
3915
|
+
var hydrateDeviceIdentity = async (opts = {}) => {
|
|
3379
3916
|
var _a2, _b, _c;
|
|
3380
3917
|
const id = resolveAutoDeviceKey(opts);
|
|
3381
|
-
if (!opts.storage) return id;
|
|
3382
|
-
const registry = autoDeviceKeyRegistry();
|
|
3383
3918
|
const appId = (_a2 = opts.appId) != null ? _a2 : "default";
|
|
3919
|
+
const record = (value, durable) => resolveIdentity({ value, space: "device", source: "auto", durable, scope: appId });
|
|
3920
|
+
if (!opts.storage) return record(id, false);
|
|
3921
|
+
const registry = autoDeviceKeyRegistry();
|
|
3384
3922
|
const pending = (_b = registry.pending) == null ? void 0 : _b.get(appId);
|
|
3385
|
-
if (pending)
|
|
3386
|
-
|
|
3923
|
+
if (!pending) return record((_c = registry.keys.get(appId)) != null ? _c : id, false);
|
|
3924
|
+
const outcome = await pending;
|
|
3925
|
+
return record(outcome.value, outcome.durable);
|
|
3387
3926
|
};
|
|
3388
3927
|
var resetAutoDeviceKeys = () => {
|
|
3389
3928
|
var _a2;
|
|
@@ -3407,13 +3946,6 @@ var getCurrentSessionId = () => globalSlot[CURRENT_SESSION_ID_SLOT];
|
|
|
3407
3946
|
var resetCurrentSessionId = () => {
|
|
3408
3947
|
globalSlot[CURRENT_SESSION_ID_SLOT] = void 0;
|
|
3409
3948
|
};
|
|
3410
|
-
var warnInDev = (message) => {
|
|
3411
|
-
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
3412
|
-
console.warn(message);
|
|
3413
|
-
return true;
|
|
3414
|
-
}
|
|
3415
|
-
return false;
|
|
3416
|
-
};
|
|
3417
3949
|
var MINT_WARNING = "[wireai] No app-open session was registered, so a session id was minted for this event (the server drops an event that has no session_id, and still answers 200). Mount useLifecycleEvents at your app root so events correlate to a real app-open.";
|
|
3418
3950
|
var MINT_WARNED_SLOT = /* @__PURE__ */ Symbol.for(
|
|
3419
3951
|
"@wireai/activation:currentSessionIdMintWarned"
|
|
@@ -3510,27 +4042,20 @@ var identifyOnboarding = async (opts) => {
|
|
|
3510
4042
|
contextId = stored == null ? void 0 : stored.id;
|
|
3511
4043
|
}
|
|
3512
4044
|
}
|
|
3513
|
-
|
|
4045
|
+
let space = "onboarding";
|
|
4046
|
+
if (!contextId && opts.allowAppSessionFallback) {
|
|
4047
|
+
contextId = getCurrentSessionId();
|
|
4048
|
+
space = "app_session";
|
|
4049
|
+
}
|
|
3514
4050
|
if (!contextId) return false;
|
|
3515
4051
|
reportClientEvent(
|
|
3516
4052
|
{ serverUrl: opts.config.serverUrl, apiKey: opts.config.apiKey },
|
|
3517
4053
|
{ event_type: "identify", session_id: contextId, user_id: userId }
|
|
3518
4054
|
);
|
|
3519
|
-
return
|
|
4055
|
+
return space;
|
|
3520
4056
|
};
|
|
3521
4057
|
|
|
3522
4058
|
// src/context/userContext.ts
|
|
3523
|
-
var RESERVED_USER_CONTEXT_KEYS = [
|
|
3524
|
-
"device_key",
|
|
3525
|
-
"app_version",
|
|
3526
|
-
"app_build",
|
|
3527
|
-
"network_type",
|
|
3528
|
-
"session_count",
|
|
3529
|
-
"returning",
|
|
3530
|
-
"platform",
|
|
3531
|
-
"user_email",
|
|
3532
|
-
"user_email_hashed"
|
|
3533
|
-
];
|
|
3534
4059
|
var EXTRA_KEY_PREFIX = "custom.";
|
|
3535
4060
|
var isWireScalar = (value) => {
|
|
3536
4061
|
const t = typeof value;
|
|
@@ -3611,9 +4136,41 @@ var resolveUserContext = (ctx = {}, opts = {}) => {
|
|
|
3611
4136
|
if (Object.keys(bucket).length > 0) result.userContext = bucket;
|
|
3612
4137
|
return result;
|
|
3613
4138
|
};
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
4139
|
+
|
|
4140
|
+
// src/permissions/permissionMemory.ts
|
|
4141
|
+
var permissionStorageKey = (appId) => `wireai:permissions:${appId}`;
|
|
4142
|
+
var readSettledPermissions = (raw, sessionId) => {
|
|
4143
|
+
if (!raw || !sessionId) return [];
|
|
4144
|
+
try {
|
|
4145
|
+
const parsed = JSON.parse(raw);
|
|
4146
|
+
if (typeof parsed !== "object" || parsed === null) return [];
|
|
4147
|
+
const record = parsed;
|
|
4148
|
+
if (record.sessionId !== sessionId || !Array.isArray(record.ids)) return [];
|
|
4149
|
+
return record.ids.filter((id) => typeof id === "string" && id.length > 0);
|
|
4150
|
+
} catch {
|
|
4151
|
+
return [];
|
|
4152
|
+
}
|
|
4153
|
+
};
|
|
4154
|
+
var loadSettledPermissions = async (storage, key, sessionId) => {
|
|
4155
|
+
try {
|
|
4156
|
+
return readSettledPermissions(await withTimeout(storage.getItem(key), READ_TIMEOUT_MS), sessionId);
|
|
4157
|
+
} catch {
|
|
4158
|
+
return [];
|
|
4159
|
+
}
|
|
4160
|
+
};
|
|
4161
|
+
var saveSettledPermissions = (storage, key, sessionId, ids) => {
|
|
4162
|
+
try {
|
|
4163
|
+
const record = { sessionId, ids: [...ids] };
|
|
4164
|
+
void storage.setItem(key, JSON.stringify(record)).catch(() => {
|
|
4165
|
+
});
|
|
4166
|
+
} catch {
|
|
4167
|
+
}
|
|
4168
|
+
};
|
|
4169
|
+
var clearSettledPermissions = (storage, key) => {
|
|
4170
|
+
try {
|
|
4171
|
+
void storage.removeItem(key).catch(() => {
|
|
4172
|
+
});
|
|
4173
|
+
} catch {
|
|
3617
4174
|
}
|
|
3618
4175
|
};
|
|
3619
4176
|
var AUTO_JOIN_HYDRATION_TIMEOUT_MS = 1500;
|
|
@@ -3640,69 +4197,99 @@ var WireOnboarding = ({
|
|
|
3640
4197
|
retainSessionOnComplete,
|
|
3641
4198
|
userContext,
|
|
3642
4199
|
userId,
|
|
3643
|
-
autoJoinKey = true
|
|
4200
|
+
autoJoinKey = true,
|
|
4201
|
+
permissionScreens
|
|
3644
4202
|
}) => {
|
|
3645
|
-
var _a2, _b, _c;
|
|
3646
|
-
const boundUserId =
|
|
3647
|
-
const device =
|
|
4203
|
+
var _a2, _b, _c, _d, _e, _f;
|
|
4204
|
+
const boundUserId = React19.useMemo(() => sanitizeUserId(userId), [userId]);
|
|
4205
|
+
const device = React19.useMemo(() => {
|
|
3648
4206
|
const collected = collectDeviceContext();
|
|
3649
4207
|
return config.appVersion ? { ...collected, appVersion: config.appVersion } : collected;
|
|
3650
4208
|
}, [config.appVersion]);
|
|
3651
4209
|
const userContextKey = userContext ? JSON.stringify(userContext) : "";
|
|
3652
|
-
const userContextStable =
|
|
4210
|
+
const userContextStable = React19.useMemo(
|
|
3653
4211
|
() => userContextKey ? JSON.parse(userContextKey) : void 0,
|
|
3654
4212
|
[userContextKey]
|
|
3655
4213
|
);
|
|
3656
|
-
const
|
|
4214
|
+
const hostJoinIdentity = React19.useMemo(
|
|
4215
|
+
() => resolveIdentity({
|
|
4216
|
+
value: userContextStable == null ? void 0 : userContextStable.device_key,
|
|
4217
|
+
space: "device",
|
|
4218
|
+
source: "host",
|
|
4219
|
+
scope: config.appId
|
|
4220
|
+
}),
|
|
4221
|
+
[userContextStable, config.appId]
|
|
4222
|
+
);
|
|
4223
|
+
const missingJoinKey = !hostJoinIdentity;
|
|
3657
4224
|
const autoJoinPossible = Boolean(storage) && autoJoinKey !== false;
|
|
3658
4225
|
const wantsAutoJoin = missingJoinKey && autoJoinPossible;
|
|
3659
|
-
const [
|
|
3660
|
-
|
|
3661
|
-
if (!wantsAutoJoin ||
|
|
4226
|
+
const [autoJoinOutcome, setAutoJoinOutcome] = React19.useState(void 0);
|
|
4227
|
+
React19.useEffect(() => {
|
|
4228
|
+
if (!wantsAutoJoin || autoJoinOutcome !== void 0 || !storage) return;
|
|
3662
4229
|
let cancelled = false;
|
|
3663
4230
|
const timer = setTimeout(() => {
|
|
3664
|
-
if (!cancelled)
|
|
4231
|
+
if (!cancelled) setAutoJoinOutcome({ key: null, reason: "timeout" });
|
|
3665
4232
|
}, AUTO_JOIN_HYDRATION_TIMEOUT_MS);
|
|
3666
|
-
void
|
|
4233
|
+
void hydrateDeviceIdentity({ appId: config.appId, storage }).then((identity) => {
|
|
3667
4234
|
if (cancelled) return;
|
|
3668
4235
|
clearTimeout(timer);
|
|
3669
|
-
|
|
4236
|
+
setAutoJoinOutcome(
|
|
4237
|
+
(identity == null ? void 0 : identity.durable) ? { key: identity.value } : { key: null, reason: "not-durable" }
|
|
4238
|
+
);
|
|
3670
4239
|
});
|
|
3671
4240
|
return () => {
|
|
3672
4241
|
cancelled = true;
|
|
3673
4242
|
clearTimeout(timer);
|
|
3674
4243
|
};
|
|
3675
|
-
}, [wantsAutoJoin,
|
|
3676
|
-
const injectedJoinKey = wantsAutoJoin
|
|
3677
|
-
const autoJoinPending = wantsAutoJoin &&
|
|
3678
|
-
const effectiveUserContext =
|
|
4244
|
+
}, [wantsAutoJoin, autoJoinOutcome, config.appId, storage]);
|
|
4245
|
+
const injectedJoinKey = wantsAutoJoin ? (_a2 = autoJoinOutcome == null ? void 0 : autoJoinOutcome.key) != null ? _a2 : void 0 : void 0;
|
|
4246
|
+
const autoJoinPending = wantsAutoJoin && autoJoinOutcome === void 0;
|
|
4247
|
+
const effectiveUserContext = React19.useMemo(
|
|
3679
4248
|
() => injectedJoinKey ? { ...userContextStable, ...activationJoinContext(injectedJoinKey) } : userContextStable,
|
|
3680
4249
|
[userContextStable, injectedJoinKey]
|
|
3681
4250
|
);
|
|
4251
|
+
const frozenWireContextRef = React19.useRef(null);
|
|
4252
|
+
if (!autoJoinPending && frozenWireContextRef.current === null) {
|
|
4253
|
+
frozenWireContextRef.current = effectiveUserContext ? JSON.stringify(effectiveUserContext) : "";
|
|
4254
|
+
}
|
|
4255
|
+
const startupUserContextKey = (_b = frozenWireContextRef.current) != null ? _b : "";
|
|
4256
|
+
const startupUserContext = React19.useMemo(
|
|
4257
|
+
() => startupUserContextKey ? JSON.parse(startupUserContextKey) : void 0,
|
|
4258
|
+
[startupUserContextKey]
|
|
4259
|
+
);
|
|
3682
4260
|
const warnMissingJoinKey = missingJoinKey && !injectedJoinKey && !autoJoinPending;
|
|
3683
|
-
const autoJoinReason = autoJoinKey === false ? "you passed autoJoinKey={false}." : !storage ? "it got no `storage` prop, and without persistence its key would be different on every launch, which corrupts min_sessions instead of merely leaving the join empty." : "your `storage` adapter did not answer in time.";
|
|
3684
|
-
|
|
4261
|
+
const autoJoinReason = autoJoinKey === false ? "you passed autoJoinKey={false}." : !storage ? "it got no `storage` prop, and without persistence its key would be different on every launch, which corrupts min_sessions instead of merely leaving the join empty." : (autoJoinOutcome == null ? void 0 : autoJoinOutcome.key) === null && autoJoinOutcome.reason === "not-durable" ? "your `storage` adapter answered but could not persist the key (the read or the write failed), so the key would be different on every launch, which corrupts min_sessions instead of merely leaving the join empty. Check the adapter you passed as `storage`." : "your `storage` adapter did not answer in time.";
|
|
4262
|
+
React19.useEffect(() => {
|
|
3685
4263
|
if (!warnMissingJoinKey) return;
|
|
3686
|
-
|
|
4264
|
+
warnInDev(
|
|
3687
4265
|
"[wireai] <WireOnboarding> got no user_context.device_key, so this onboarding session can never be joined to the app's later events and the `activated` funnel will read zero. Pass userContext={activationJoinContext(deviceKey)} \u2014 and if your app owns no device id, userContext={activationJoinContext(resolveAutoDeviceKey({ appId, storage }))} returns the SAME id the analytics side stamps. Never hand-write userContext={{ deviceKey }}. The kit did NOT auto-inject its own key here because " + autoJoinReason
|
|
3688
4266
|
);
|
|
3689
4267
|
}, [warnMissingJoinKey, autoJoinReason]);
|
|
4268
|
+
const hostKeyElsewhere = missingJoinKey ? hostIdentity("device", config.appId) : void 0;
|
|
4269
|
+
const warnHostKeyElsewhere = Boolean(hostKeyElsewhere) && Boolean(injectedJoinKey);
|
|
4270
|
+
React19.useEffect(() => {
|
|
4271
|
+
if (!warnHostKeyElsewhere) return;
|
|
4272
|
+
warnInDev(
|
|
4273
|
+
`[wireai] <WireOnboarding> got no user_context.device_key, so the kit injected its own \u2014 but another surface in this app was constructed WITH a host-supplied device key ("${hostKeyElsewhere}"). Your app events carry that id and this onboarding session carries the kit's, so the two land in disjoint id spaces and the \`activated\` funnel still reads zero. Pass the SAME id here: userContext={activationJoinContext(deviceKey)}.`
|
|
4274
|
+
);
|
|
4275
|
+
}, [warnHostKeyElsewhere, hostKeyElsewhere]);
|
|
3690
4276
|
const misspelledJoinKey = missingJoinKey && typeof (userContextStable == null ? void 0 : userContextStable.deviceKey) === "string" && Boolean(userContextStable.deviceKey.trim());
|
|
3691
|
-
|
|
4277
|
+
React19.useEffect(() => {
|
|
3692
4278
|
if (!misspelledJoinKey || !injectedJoinKey) return;
|
|
3693
|
-
|
|
4279
|
+
warnInDev(
|
|
3694
4280
|
"[wireai] <WireOnboarding> got userContext={{ deviceKey }}, which is NOT the wire key \u2014 the server's device lookup reads `device_key`. The kit auto-injected its own device_key so this session is at least self-consistent, but your app's own events carry YOUR id, so the two still will not join and the `activated` funnel stays zero. Pass userContext={activationJoinContext(deviceKey)} instead."
|
|
3695
4281
|
);
|
|
3696
4282
|
}, [misspelledJoinKey, injectedJoinKey]);
|
|
3697
|
-
const clientContext =
|
|
4283
|
+
const clientContext = React19.useMemo(
|
|
3698
4284
|
() => ({ device, userContext: effectiveUserContext, userId: boundUserId }),
|
|
3699
4285
|
[device, effectiveUserContext, boundUserId]
|
|
3700
4286
|
);
|
|
3701
|
-
const [session, setSession] =
|
|
4287
|
+
const [session, setSession] = React19.useState(
|
|
3702
4288
|
() => storage ? null : { id: makeSessionId(), resumed: false }
|
|
3703
4289
|
);
|
|
3704
4290
|
const storageKey = persistKey != null ? persistKey : sessionStorageKey(config.appId);
|
|
3705
|
-
|
|
4291
|
+
const permissionsKey = permissionStorageKey(config.appId);
|
|
4292
|
+
React19.useEffect(() => {
|
|
3706
4293
|
if (!storage || session) return;
|
|
3707
4294
|
let cancelled = false;
|
|
3708
4295
|
void loadPersistedSession(storage, storageKey, sessionTtlMs).then((loaded) => {
|
|
@@ -3712,20 +4299,51 @@ var WireOnboarding = ({
|
|
|
3712
4299
|
cancelled = true;
|
|
3713
4300
|
};
|
|
3714
4301
|
}, [storage, storageKey, sessionTtlMs, session]);
|
|
3715
|
-
const handleComplete =
|
|
4302
|
+
const handleComplete = React19.useCallback(
|
|
3716
4303
|
(result) => {
|
|
3717
|
-
if (storage && shouldClearOnComplete(retainSessionOnComplete))
|
|
4304
|
+
if (storage && shouldClearOnComplete(retainSessionOnComplete)) {
|
|
4305
|
+
clearPersistedSession(storage, storageKey);
|
|
4306
|
+
clearSettledPermissions(storage, permissionsKey);
|
|
4307
|
+
}
|
|
3718
4308
|
onComplete(result);
|
|
3719
4309
|
},
|
|
3720
|
-
[storage, storageKey, retainSessionOnComplete, onComplete]
|
|
4310
|
+
[storage, storageKey, permissionsKey, retainSessionOnComplete, onComplete]
|
|
4311
|
+
);
|
|
4312
|
+
const sessionId = (_c = session == null ? void 0 : session.id) != null ? _c : "";
|
|
4313
|
+
const wantsPermissionMemory = Boolean(storage) && ((_d = permissionScreens == null ? void 0 : permissionScreens.length) != null ? _d : 0) > 0;
|
|
4314
|
+
const [settledPermissions, setSettledPermissions] = React19.useState(void 0);
|
|
4315
|
+
React19.useEffect(() => {
|
|
4316
|
+
if (!wantsPermissionMemory || !storage || !sessionId || settledPermissions !== void 0) return;
|
|
4317
|
+
let cancelled = false;
|
|
4318
|
+
void loadSettledPermissions(storage, permissionsKey, sessionId).then((ids) => {
|
|
4319
|
+
if (!cancelled) setSettledPermissions(ids);
|
|
4320
|
+
});
|
|
4321
|
+
return () => {
|
|
4322
|
+
cancelled = true;
|
|
4323
|
+
};
|
|
4324
|
+
}, [wantsPermissionMemory, storage, permissionsKey, sessionId, settledPermissions]);
|
|
4325
|
+
const settledPermissionsRef = React19.useRef(void 0);
|
|
4326
|
+
settledPermissionsRef.current = settledPermissions;
|
|
4327
|
+
const handlePermissionSettled = React19.useCallback(
|
|
4328
|
+
(id) => {
|
|
4329
|
+
var _a3;
|
|
4330
|
+
const current = (_a3 = settledPermissionsRef.current) != null ? _a3 : [];
|
|
4331
|
+
if (current.includes(id)) return;
|
|
4332
|
+
const next = [...current, id];
|
|
4333
|
+
settledPermissionsRef.current = next;
|
|
4334
|
+
setSettledPermissions(next);
|
|
4335
|
+
if (storage && wantsPermissionMemory && sessionId) {
|
|
4336
|
+
saveSettledPermissions(storage, permissionsKey, sessionId, next);
|
|
4337
|
+
}
|
|
4338
|
+
},
|
|
4339
|
+
[storage, wantsPermissionMemory, permissionsKey, sessionId]
|
|
3721
4340
|
);
|
|
3722
|
-
const
|
|
3723
|
-
const startupUserIdRef = React18.useRef(boundUserId);
|
|
4341
|
+
const startupUserIdRef = React19.useRef(boundUserId);
|
|
3724
4342
|
const metadataKey = config.metadata ? JSON.stringify(config.metadata) : "";
|
|
3725
|
-
const metadataStable =
|
|
4343
|
+
const metadataStable = React19.useMemo(() => metadataKey ? JSON.parse(metadataKey) : void 0, [metadataKey]);
|
|
3726
4344
|
const componentNamesKey = (components != null ? components : onboardingComponents).map((c) => c.name).join(",");
|
|
3727
|
-
const componentsStable =
|
|
3728
|
-
const llm =
|
|
4345
|
+
const componentsStable = React19.useMemo(() => components != null ? components : onboardingComponents, [componentNamesKey]);
|
|
4346
|
+
const llm = React19.useMemo(() => {
|
|
3729
4347
|
const baseUrl = config.serverUrl.replace(/\/$/, "");
|
|
3730
4348
|
return {
|
|
3731
4349
|
provider: "a2a",
|
|
@@ -3743,7 +4361,9 @@ var WireOnboarding = ({
|
|
|
3743
4361
|
// Device snapshot + host-injected user context ride the session-start metadata so the
|
|
3744
4362
|
// backend can segment the funnel. Old servers ignore these unknown keys (backward compat).
|
|
3745
4363
|
device,
|
|
3746
|
-
|
|
4364
|
+
// The FROZEN wire context, never the live one — see the freeze note above. A change here
|
|
4365
|
+
// recreates the A2A adapter and drops `contextId`.
|
|
4366
|
+
...startupUserContext ? { userContext: startupUserContext } : {},
|
|
3747
4367
|
// The session-start user id (from the ref) rides the session-start metadata so the
|
|
3748
4368
|
// server binds the session to a real user at creation. Reading the ref — not
|
|
3749
4369
|
// `boundUserId` — keeps this memo off the userId dependency, so a mid-session change
|
|
@@ -3752,14 +4372,14 @@ var WireOnboarding = ({
|
|
|
3752
4372
|
},
|
|
3753
4373
|
timeoutMs: 6e4
|
|
3754
4374
|
};
|
|
3755
|
-
}, [config.serverUrl, config.appId, config.apiKey, metadataStable, sessionId, componentsStable, device,
|
|
3756
|
-
const reportTarget =
|
|
4375
|
+
}, [config.serverUrl, config.appId, config.apiKey, metadataStable, sessionId, componentsStable, device, startupUserContext]);
|
|
4376
|
+
const reportTarget = React19.useMemo(
|
|
3757
4377
|
() => ({ serverUrl: config.serverUrl, apiKey: config.apiKey }),
|
|
3758
4378
|
[config.serverUrl, config.apiKey]
|
|
3759
4379
|
);
|
|
3760
|
-
const identifiedRef =
|
|
3761
|
-
const identifySeededRef =
|
|
3762
|
-
|
|
4380
|
+
const identifiedRef = React19.useRef(void 0);
|
|
4381
|
+
const identifySeededRef = React19.useRef(false);
|
|
4382
|
+
React19.useEffect(() => {
|
|
3763
4383
|
if (!session || !sessionId) return;
|
|
3764
4384
|
if (!identifySeededRef.current) {
|
|
3765
4385
|
identifySeededRef.current = true;
|
|
@@ -3778,8 +4398,8 @@ var WireOnboarding = ({
|
|
|
3778
4398
|
return /* @__PURE__ */ jsxRuntime.jsx(OnboardingThemeProvider, { theme, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3779
4399
|
LoadingScreen,
|
|
3780
4400
|
{
|
|
3781
|
-
title: (
|
|
3782
|
-
hint: (
|
|
4401
|
+
title: (_e = copy == null ? void 0 : copy.restoringTitle) != null ? _e : DEFAULT_COPY.restoringTitle,
|
|
4402
|
+
hint: (_f = copy == null ? void 0 : copy.restoringHint) != null ? _f : DEFAULT_COPY.restoringHint
|
|
3783
4403
|
}
|
|
3784
4404
|
) });
|
|
3785
4405
|
}
|
|
@@ -3800,7 +4420,11 @@ var WireOnboarding = ({
|
|
|
3800
4420
|
reportTarget,
|
|
3801
4421
|
sessionId,
|
|
3802
4422
|
resumed: session.resumed,
|
|
3803
|
-
clientContext
|
|
4423
|
+
clientContext,
|
|
4424
|
+
permissionScreens,
|
|
4425
|
+
settledPermissions,
|
|
4426
|
+
permissionsPending: wantsPermissionMemory && settledPermissions === void 0,
|
|
4427
|
+
onPermissionSettled: handlePermissionSettled
|
|
3804
4428
|
}
|
|
3805
4429
|
) }) }) }) });
|
|
3806
4430
|
};
|
|
@@ -3814,16 +4438,16 @@ function DemoOnboarding({
|
|
|
3814
4438
|
onComplete,
|
|
3815
4439
|
renderTrigger
|
|
3816
4440
|
}) {
|
|
3817
|
-
const [open, setOpen] =
|
|
3818
|
-
const [runId, setRunId] =
|
|
3819
|
-
const t =
|
|
3820
|
-
const styles18 =
|
|
3821
|
-
const openDemo =
|
|
4441
|
+
const [open, setOpen] = React19.useState(false);
|
|
4442
|
+
const [runId, setRunId] = React19.useState(0);
|
|
4443
|
+
const t = React19.useMemo(() => mergeTheme(theme != null ? theme : {}), [theme]);
|
|
4444
|
+
const styles18 = React19.useMemo(() => makeStyles5(t), [t]);
|
|
4445
|
+
const openDemo = React19.useCallback(() => {
|
|
3822
4446
|
setRunId((n) => n + 1);
|
|
3823
4447
|
setOpen(true);
|
|
3824
4448
|
}, []);
|
|
3825
|
-
const close =
|
|
3826
|
-
const finish =
|
|
4449
|
+
const close = React19.useCallback(() => setOpen(false), []);
|
|
4450
|
+
const finish = React19.useCallback(
|
|
3827
4451
|
(result) => {
|
|
3828
4452
|
onComplete == null ? void 0 : onComplete(result);
|
|
3829
4453
|
setOpen(false);
|
|
@@ -3929,20 +4553,6 @@ var themeFromBrand = (input) => {
|
|
|
3929
4553
|
button: { ...base.button, ...extra.button }
|
|
3930
4554
|
};
|
|
3931
4555
|
};
|
|
3932
|
-
var _DoneBlock = ({
|
|
3933
|
-
title = "You're all set",
|
|
3934
|
-
message = "Personalizing your experience\u2026"
|
|
3935
|
-
}) => {
|
|
3936
|
-
const t = useOnboardingTheme();
|
|
3937
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles16.center, { gap: t.spacing.sm, padding: t.spacing.lg }], children: [
|
|
3938
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [headingStyle(t.fonts), { color: t.colors.success, textAlign: "center" }], children: title }),
|
|
3939
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted, textAlign: "center" }], children: message })
|
|
3940
|
-
] });
|
|
3941
|
-
};
|
|
3942
|
-
var DoneBlock = React18__default.default.memo(_DoneBlock);
|
|
3943
|
-
var styles16 = reactNative.StyleSheet.create({
|
|
3944
|
-
center: { flex: 1, alignItems: "center", justifyContent: "center" }
|
|
3945
|
-
});
|
|
3946
4556
|
|
|
3947
4557
|
// src/motion/centeredModalMotion.ts
|
|
3948
4558
|
var CENTERED_MODAL_ANIM = {
|
|
@@ -3955,13 +4565,13 @@ var CENTERED_MODAL_ANIM = {
|
|
|
3955
4565
|
/** Peak dim of the backdrop at progress 1. */
|
|
3956
4566
|
backdropOpacity: 0.55
|
|
3957
4567
|
};
|
|
3958
|
-
var _CenteredModal =
|
|
4568
|
+
var _CenteredModal = React19.forwardRef(
|
|
3959
4569
|
({ theme, dismissible = true, onDismiss, children }, ref) => {
|
|
3960
|
-
const progress =
|
|
3961
|
-
const [open, setOpen] =
|
|
3962
|
-
const closingRef =
|
|
4570
|
+
const progress = React19.useRef(new reactNative.Animated.Value(0)).current;
|
|
4571
|
+
const [open, setOpen] = React19.useState(true);
|
|
4572
|
+
const closingRef = React19.useRef(false);
|
|
3963
4573
|
const reduced = useReducedMotion();
|
|
3964
|
-
|
|
4574
|
+
React19.useEffect(() => {
|
|
3965
4575
|
const anim = reduced ? reactNative.Animated.timing(progress, {
|
|
3966
4576
|
toValue: 1,
|
|
3967
4577
|
duration: REDUCED_FADE_MS,
|
|
@@ -3975,7 +4585,7 @@ var _CenteredModal = React18.forwardRef(
|
|
|
3975
4585
|
anim.start();
|
|
3976
4586
|
return () => anim.stop();
|
|
3977
4587
|
}, [progress, reduced]);
|
|
3978
|
-
const close =
|
|
4588
|
+
const close = React19.useCallback(() => {
|
|
3979
4589
|
if (closingRef.current) return;
|
|
3980
4590
|
closingRef.current = true;
|
|
3981
4591
|
reactNative.Animated.timing(progress, {
|
|
@@ -3988,15 +4598,15 @@ var _CenteredModal = React18.forwardRef(
|
|
|
3988
4598
|
onDismiss();
|
|
3989
4599
|
});
|
|
3990
4600
|
}, [progress, onDismiss, reduced]);
|
|
3991
|
-
|
|
3992
|
-
|
|
4601
|
+
React19.useImperativeHandle(ref, () => ({ close }), [close]);
|
|
4602
|
+
React19.useEffect(() => {
|
|
3993
4603
|
const sub = reactNative.BackHandler.addEventListener("hardwareBackPress", () => {
|
|
3994
4604
|
if (dismissible) close();
|
|
3995
4605
|
return true;
|
|
3996
4606
|
});
|
|
3997
4607
|
return () => sub.remove();
|
|
3998
4608
|
}, [dismissible, close]);
|
|
3999
|
-
const onBackdrop =
|
|
4609
|
+
const onBackdrop = React19.useCallback(() => {
|
|
4000
4610
|
if (dismissible) close();
|
|
4001
4611
|
}, [dismissible, close]);
|
|
4002
4612
|
const backdropOpacity = progress.interpolate({
|
|
@@ -4058,7 +4668,7 @@ var _CenteredModal = React18.forwardRef(
|
|
|
4058
4668
|
}
|
|
4059
4669
|
);
|
|
4060
4670
|
_CenteredModal.displayName = "CenteredModal";
|
|
4061
|
-
var CenteredModal =
|
|
4671
|
+
var CenteredModal = React19__default.default.memo(_CenteredModal);
|
|
4062
4672
|
var styles17 = reactNative.StyleSheet.create({
|
|
4063
4673
|
fill: { flex: 1 },
|
|
4064
4674
|
backdrop: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, backgroundColor: "#000" },
|
|
@@ -4188,16 +4798,16 @@ var fetchWireFeatures = async (config) => {
|
|
|
4188
4798
|
}
|
|
4189
4799
|
};
|
|
4190
4800
|
var useStableValue = (value, isEqual) => {
|
|
4191
|
-
const ref =
|
|
4801
|
+
const ref = React19.useRef(value);
|
|
4192
4802
|
if (!isEqual(ref.current, value)) ref.current = value;
|
|
4193
4803
|
return ref.current;
|
|
4194
4804
|
};
|
|
4195
4805
|
var sameFetchKey = (a, b) => (a == null ? void 0 : a.serverUrl) === (b == null ? void 0 : b.serverUrl) && (a == null ? void 0 : a.apiKey) === (b == null ? void 0 : b.apiKey) && (a == null ? void 0 : a.appId) === (b == null ? void 0 : b.appId);
|
|
4196
4806
|
var useWireFeatures = (config) => {
|
|
4197
4807
|
const stableConfig = useStableValue(config, sameFetchKey);
|
|
4198
|
-
const [flags, setFlags] =
|
|
4808
|
+
const [flags, setFlags] = React19.useState(defaultWireFeatures);
|
|
4199
4809
|
const canFetch = !!(stableConfig == null ? void 0 : stableConfig.serverUrl) && !!(stableConfig == null ? void 0 : stableConfig.apiKey);
|
|
4200
|
-
|
|
4810
|
+
React19.useEffect(() => {
|
|
4201
4811
|
if (!canFetch) {
|
|
4202
4812
|
setFlags((prev) => featuresEqual(prev, defaultWireFeatures) ? prev : defaultWireFeatures);
|
|
4203
4813
|
return;
|
|
@@ -4216,7 +4826,7 @@ var useWireFeatures = (config) => {
|
|
|
4216
4826
|
var CONTEXT_SYMBOL = /* @__PURE__ */ Symbol.for("wireai.features.context");
|
|
4217
4827
|
var globalObj = global;
|
|
4218
4828
|
if (!globalObj[CONTEXT_SYMBOL]) {
|
|
4219
|
-
globalObj[CONTEXT_SYMBOL] =
|
|
4829
|
+
globalObj[CONTEXT_SYMBOL] = React19.createContext(null);
|
|
4220
4830
|
}
|
|
4221
4831
|
var WireFeaturesContext = globalObj[CONTEXT_SYMBOL];
|
|
4222
4832
|
var WireFeaturesProvider = ({
|
|
@@ -4226,11 +4836,11 @@ var WireFeaturesProvider = ({
|
|
|
4226
4836
|
}) => {
|
|
4227
4837
|
const fetched = useWireFeatures(flags ? void 0 : config);
|
|
4228
4838
|
const value = flags != null ? flags : fetched;
|
|
4229
|
-
const stable =
|
|
4839
|
+
const stable = React19.useMemo(() => value, [value]);
|
|
4230
4840
|
return /* @__PURE__ */ jsxRuntime.jsx(WireFeaturesContext.Provider, { value: stable, children });
|
|
4231
4841
|
};
|
|
4232
4842
|
WireFeaturesProvider.displayName = "WireFeaturesProvider";
|
|
4233
|
-
var useWireFeaturesContext = () =>
|
|
4843
|
+
var useWireFeaturesContext = () => React19.useContext(WireFeaturesContext);
|
|
4234
4844
|
var useResolvedFeatures = (options) => {
|
|
4235
4845
|
var _a2, _b, _c;
|
|
4236
4846
|
const ctx = useWireFeaturesContext();
|
|
@@ -4245,11 +4855,6 @@ var WIRE_ENV_VARS = [
|
|
|
4245
4855
|
"EXPO_PUBLIC_WIREAI_SERVER_URL",
|
|
4246
4856
|
"EXPO_PUBLIC_WIREAI_APP_ID"
|
|
4247
4857
|
];
|
|
4248
|
-
var warnInDev3 = (message) => {
|
|
4249
|
-
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
4250
|
-
console.warn(message);
|
|
4251
|
-
}
|
|
4252
|
-
};
|
|
4253
4858
|
var wireConfigFromEnv = (overrides) => {
|
|
4254
4859
|
var _a2, _b, _c, _d, _e;
|
|
4255
4860
|
const apiKey = (_a2 = overrides == null ? void 0 : overrides.apiKey) != null ? _a2 : process.env.EXPO_PUBLIC_WIREAI_API_KEY;
|
|
@@ -4260,7 +4865,7 @@ var wireConfigFromEnv = (overrides) => {
|
|
|
4260
4865
|
apiKey ? void 0 : "EXPO_PUBLIC_WIREAI_API_KEY",
|
|
4261
4866
|
serverUrl ? void 0 : "EXPO_PUBLIC_WIREAI_SERVER_URL"
|
|
4262
4867
|
].filter(Boolean);
|
|
4263
|
-
|
|
4868
|
+
warnInDev(
|
|
4264
4869
|
`[wireai] wireConfigFromEnv() returned null: ${missing.join(" and ")} ${missing.length > 1 ? "are" : "is"} missing. The kit is now FULLY DISABLED (no onboarding, no analytics, no gates) and nothing else will report an error. Set the var(s) in your env / EAS secrets.`
|
|
4265
4870
|
);
|
|
4266
4871
|
return null;
|
|
@@ -4315,6 +4920,11 @@ var toAnalyticsEvent = (event) => {
|
|
|
4315
4920
|
};
|
|
4316
4921
|
case "fallback":
|
|
4317
4922
|
return { name: WIRE_ONBOARDING_EVENTS.fallback, params: { reason: event.reason } };
|
|
4923
|
+
case "permission":
|
|
4924
|
+
return {
|
|
4925
|
+
name: permissionEventName(event.stage),
|
|
4926
|
+
params: permissionEventProps(event.permission, event.status)
|
|
4927
|
+
};
|
|
4318
4928
|
default: {
|
|
4319
4929
|
const _exhaustive = event;
|
|
4320
4930
|
return _exhaustive;
|
|
@@ -4368,15 +4978,17 @@ var resetActivationRevalidation = () => {
|
|
|
4368
4978
|
};
|
|
4369
4979
|
|
|
4370
4980
|
// src/activation/wireActivation.ts
|
|
4371
|
-
var clean =
|
|
4372
|
-
if (typeof value !== "string") return void 0;
|
|
4373
|
-
const trimmed = value.trim();
|
|
4374
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
4375
|
-
};
|
|
4981
|
+
var clean = cleanString;
|
|
4376
4982
|
var createWireActivation = (config) => {
|
|
4377
4983
|
var _a2, _b;
|
|
4378
4984
|
const target = { serverUrl: config.serverUrl, apiKey: config.apiKey };
|
|
4379
4985
|
const explicitDeviceKey = (_b = clean(config.deviceKey)) != null ? _b : clean((_a2 = config.userContext) == null ? void 0 : _a2.deviceKey);
|
|
4986
|
+
resolveIdentity({
|
|
4987
|
+
value: explicitDeviceKey,
|
|
4988
|
+
space: "device",
|
|
4989
|
+
source: "host",
|
|
4990
|
+
scope: config.appId
|
|
4991
|
+
});
|
|
4380
4992
|
const autoDeviceKeyOptions = {
|
|
4381
4993
|
appId: config.appId,
|
|
4382
4994
|
// An explicit key opts out of minting AND persisting (unchanged contract).
|
|
@@ -4422,15 +5034,15 @@ var createWireActivation = (config) => {
|
|
|
4422
5034
|
getRevalidationVersion: getActivationRevalidationVersion
|
|
4423
5035
|
};
|
|
4424
5036
|
};
|
|
4425
|
-
var useActivationRevalidation = () =>
|
|
5037
|
+
var useActivationRevalidation = () => React19.useSyncExternalStore(
|
|
4426
5038
|
subscribeActivationRevalidation,
|
|
4427
5039
|
getActivationRevalidationVersion,
|
|
4428
5040
|
getActivationRevalidationVersion
|
|
4429
5041
|
);
|
|
4430
5042
|
var useWireActivation = (config) => {
|
|
4431
5043
|
var _a2;
|
|
4432
|
-
const ref =
|
|
4433
|
-
const prevKeys =
|
|
5044
|
+
const ref = React19.useRef(void 0);
|
|
5045
|
+
const prevKeys = React19.useRef("");
|
|
4434
5046
|
const currentKeys = [
|
|
4435
5047
|
config.serverUrl,
|
|
4436
5048
|
config.apiKey,
|
|
@@ -4655,9 +5267,9 @@ var reportSessionStart = (opts) => {
|
|
|
4655
5267
|
};
|
|
4656
5268
|
var BACKGROUND_SESSION_MS = 30 * 60 * 1e3;
|
|
4657
5269
|
var useSessionStart = (config, options = {}) => {
|
|
4658
|
-
const latest =
|
|
5270
|
+
const latest = React19.useRef({ config, options });
|
|
4659
5271
|
latest.current = { config, options };
|
|
4660
|
-
|
|
5272
|
+
React19.useEffect(() => {
|
|
4661
5273
|
const resolveDeviceKey = (cfg, opts) => {
|
|
4662
5274
|
const host = typeof opts.deviceKey === "string" && opts.deviceKey.trim() ? opts.deviceKey : void 0;
|
|
4663
5275
|
if (host) return host;
|
|
@@ -4810,6 +5422,30 @@ var DEFAULTS = {
|
|
|
4810
5422
|
maxRetries: 6
|
|
4811
5423
|
};
|
|
4812
5424
|
var READ_TIMEOUT_MS4 = 1500;
|
|
5425
|
+
var QUEUE_KEY_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:eventQueueKeys");
|
|
5426
|
+
var queueKeyGlobal = globalThis;
|
|
5427
|
+
var claimedQueueKeys = () => {
|
|
5428
|
+
const existing = queueKeyGlobal[QUEUE_KEY_SLOT];
|
|
5429
|
+
if (existing) return existing;
|
|
5430
|
+
const created = /* @__PURE__ */ new Set();
|
|
5431
|
+
queueKeyGlobal[QUEUE_KEY_SLOT] = created;
|
|
5432
|
+
return created;
|
|
5433
|
+
};
|
|
5434
|
+
var claimQueueKey = (preferred, explicit) => {
|
|
5435
|
+
const claimed = claimedQueueKeys();
|
|
5436
|
+
if (explicit || !claimed.has(preferred)) {
|
|
5437
|
+
claimed.add(preferred);
|
|
5438
|
+
return preferred;
|
|
5439
|
+
}
|
|
5440
|
+
let ordinal = 2;
|
|
5441
|
+
while (claimed.has(`${preferred}#${ordinal}`)) ordinal++;
|
|
5442
|
+
const key = `${preferred}#${ordinal}`;
|
|
5443
|
+
claimed.add(key);
|
|
5444
|
+
warnInDev(
|
|
5445
|
+
`[wireai] a second event queue was created for the same appId, and "${preferred}" is already claimed by a live one. Two queues sharing one storage slot overwrite each other's backlog, delete each other's pending events when one drains to empty, and double-send on relaunch, so this queue was given "${key}" instead. Prefer ONE analytics instance per app; if you really need two, pass an explicit \`storageKey\` to each so the slots are yours to reason about.`
|
|
5446
|
+
);
|
|
5447
|
+
return key;
|
|
5448
|
+
};
|
|
4813
5449
|
var withTimeout4 = (p, ms) => {
|
|
4814
5450
|
let timer;
|
|
4815
5451
|
const timeout = new Promise((resolve) => {
|
|
@@ -4841,7 +5477,8 @@ var createEventQueue = (options) => {
|
|
|
4841
5477
|
var _a2, _b, _c, _d, _e, _f, _g;
|
|
4842
5478
|
const target = options.target;
|
|
4843
5479
|
const storage = options.storage;
|
|
4844
|
-
const
|
|
5480
|
+
const defaultKey = (_b = options.storageKey) != null ? _b : `wireai:evtq:${(_a2 = options.appId) != null ? _a2 : "default"}`;
|
|
5481
|
+
const key = storage ? claimQueueKey(defaultKey, options.storageKey !== void 0) : defaultKey;
|
|
4845
5482
|
const maxSize = (_c = options.maxSize) != null ? _c : DEFAULTS.maxSize;
|
|
4846
5483
|
const batchSize = (_d = options.batchSize) != null ? _d : DEFAULTS.batchSize;
|
|
4847
5484
|
const baseBackoffMs = (_e = options.baseBackoffMs) != null ? _e : DEFAULTS.baseBackoffMs;
|
|
@@ -5006,10 +5643,10 @@ var createEventQueue = (options) => {
|
|
|
5006
5643
|
|
|
5007
5644
|
// src/session-analytics/useLifecycleEvents.ts
|
|
5008
5645
|
var useLifecycleEvents = (config, options = {}) => {
|
|
5009
|
-
const latest =
|
|
5646
|
+
const latest = React19.useRef({ config, options });
|
|
5010
5647
|
latest.current = { config, options };
|
|
5011
|
-
const queueRef =
|
|
5012
|
-
|
|
5648
|
+
const queueRef = React19.useRef(void 0);
|
|
5649
|
+
React19.useEffect(() => {
|
|
5013
5650
|
const resolveSink = () => {
|
|
5014
5651
|
var _a2, _b;
|
|
5015
5652
|
const { config: cfg, options: opts } = latest.current;
|
|
@@ -5031,7 +5668,13 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
5031
5668
|
return (cfg == null ? void 0 : cfg.serverUrl) ? { serverUrl: cfg.serverUrl, apiKey: (_a2 = cfg.apiKey) != null ? _a2 : "" } : void 0;
|
|
5032
5669
|
};
|
|
5033
5670
|
const resolveDeviceKey = (cfg, opts) => {
|
|
5034
|
-
|
|
5671
|
+
var _a2;
|
|
5672
|
+
const host = (_a2 = resolveIdentity({
|
|
5673
|
+
value: opts.deviceKey,
|
|
5674
|
+
space: "device",
|
|
5675
|
+
source: "host",
|
|
5676
|
+
scope: cfg == null ? void 0 : cfg.appId
|
|
5677
|
+
})) == null ? void 0 : _a2.value;
|
|
5035
5678
|
if (host) return host;
|
|
5036
5679
|
if (!(cfg == null ? void 0 : cfg.storage)) return void 0;
|
|
5037
5680
|
return resolveAutoDeviceKey({ appId: cfg.appId, storage: cfg.storage });
|
|
@@ -5118,24 +5761,29 @@ exports.CenteredModal = CenteredModal;
|
|
|
5118
5761
|
exports.ChipSelectCard = ChipSelectCard;
|
|
5119
5762
|
exports.CompletionView = CompletionView;
|
|
5120
5763
|
exports.DEFAULT_FEATURES_TTL_MS = DEFAULT_FEATURES_TTL_MS;
|
|
5764
|
+
exports.DEFAULT_PERMISSION_COPY = DEFAULT_PERMISSION_COPY;
|
|
5765
|
+
exports.DEFAULT_PERMISSION_PLACEMENT = DEFAULT_PERMISSION_PLACEMENT;
|
|
5121
5766
|
exports.DEFAULT_SESSION_TTL_MS = DEFAULT_SESSION_TTL_MS;
|
|
5122
5767
|
exports.DemoOnboarding = DemoOnboarding;
|
|
5123
|
-
exports.DoneBlock = DoneBlock;
|
|
5124
5768
|
exports.EXTRA_KEY_PREFIX = EXTRA_KEY_PREFIX;
|
|
5125
5769
|
exports.ErrorBlock = ErrorBlock;
|
|
5126
5770
|
exports.FIRST_OPEN_EVENT = FIRST_OPEN_EVENT;
|
|
5771
|
+
exports.GENERIC_PERMISSION_COPY = GENERIC_PERMISSION_COPY;
|
|
5127
5772
|
exports.IconRegistryProvider = IconRegistryProvider;
|
|
5128
5773
|
exports.IllustrationProvider = IllustrationProvider;
|
|
5129
5774
|
exports.InterstitialCard = InterstitialCard;
|
|
5130
5775
|
exports.LoadingBlock = LoadingBlock;
|
|
5131
5776
|
exports.LoadingScreen = LoadingScreen;
|
|
5777
|
+
exports.NOTIFICATIONS_PERMISSION_COPY = NOTIFICATIONS_PERMISSION_COPY;
|
|
5132
5778
|
exports.NumberStepperCard = NumberStepperCard;
|
|
5133
5779
|
exports.OnboardingButton = Button;
|
|
5134
5780
|
exports.OnboardingFlow = OnboardingFlow;
|
|
5135
5781
|
exports.OnboardingScaffold = OnboardingScaffold;
|
|
5136
5782
|
exports.OnboardingThemeProvider = OnboardingThemeProvider;
|
|
5783
|
+
exports.PERMISSION_CARD_NAME = PERMISSION_CARD_NAME;
|
|
5137
5784
|
exports.PLAN_TIER_CONTEXT_KEY = PLAN_TIER_CONTEXT_KEY;
|
|
5138
|
-
exports.
|
|
5785
|
+
exports.PermissionCard = PermissionCard;
|
|
5786
|
+
exports.PermissionCardView = PermissionCardView;
|
|
5139
5787
|
exports.SESSION_STARTED_EVENT = SESSION_STARTED_EVENT;
|
|
5140
5788
|
exports.SelectionCard = SelectionCard;
|
|
5141
5789
|
exports.StatusCard = StatusCard;
|
|
@@ -5146,6 +5794,7 @@ exports.WIRE_ENV_VARS = WIRE_ENV_VARS;
|
|
|
5146
5794
|
exports.WIRE_ICON_GLYPHS = WIRE_ICON_GLYPHS;
|
|
5147
5795
|
exports.WIRE_ICON_NAMES = WIRE_ICON_NAMES;
|
|
5148
5796
|
exports.WIRE_ONBOARDING_EVENTS = WIRE_ONBOARDING_EVENTS;
|
|
5797
|
+
exports.WIRE_PERMISSION_EVENTS = WIRE_PERMISSION_EVENTS;
|
|
5149
5798
|
exports.WIRE_PURCHASE_EVENTS = WIRE_PURCHASE_EVENTS;
|
|
5150
5799
|
exports.WireFeaturesProvider = WireFeaturesProvider;
|
|
5151
5800
|
exports.WireIcon = WireIcon;
|
|
@@ -5157,6 +5806,7 @@ exports.attributionMetadata = attributionMetadata;
|
|
|
5157
5806
|
exports.bumpActivationRevalidation = bumpActivationRevalidation;
|
|
5158
5807
|
exports.clearPersistedSession = clearPersistedSession;
|
|
5159
5808
|
exports.clearPiiFromContext = clearPiiFromContext;
|
|
5809
|
+
exports.clearSettledPermissions = clearSettledPermissions;
|
|
5160
5810
|
exports.clearUserContext = clearUserContext;
|
|
5161
5811
|
exports.collectDeviceContext = collectDeviceContext;
|
|
5162
5812
|
exports.createRevenueCatBridge = createRevenueCatBridge;
|
|
@@ -5179,13 +5829,17 @@ exports.firstOpenStorageKey = firstOpenStorageKey;
|
|
|
5179
5829
|
exports.getActivationRevalidationVersion = getActivationRevalidationVersion;
|
|
5180
5830
|
exports.getCurrentSessionId = getCurrentSessionId;
|
|
5181
5831
|
exports.hashEmailFnv1a = hashEmailFnv1a;
|
|
5832
|
+
exports.hostIdentity = hostIdentity;
|
|
5182
5833
|
exports.hydrateAutoDeviceKey = hydrateAutoDeviceKey;
|
|
5834
|
+
exports.hydrateDeviceIdentity = hydrateDeviceIdentity;
|
|
5183
5835
|
exports.identifyOnboarding = identifyOnboarding;
|
|
5184
5836
|
exports.isFeaturesFresh = isFeaturesFresh;
|
|
5185
5837
|
exports.isOnboardingEnabled = isOnboardingEnabled;
|
|
5838
|
+
exports.isPermissionDue = isPermissionDue;
|
|
5186
5839
|
exports.isUserCancelled = isUserCancelled;
|
|
5187
5840
|
exports.isWireScalar = isWireScalar;
|
|
5188
5841
|
exports.loadPersistedSession = loadPersistedSession;
|
|
5842
|
+
exports.loadSettledPermissions = loadSettledPermissions;
|
|
5189
5843
|
exports.looksLikeEmail = looksLikeEmail;
|
|
5190
5844
|
exports.lookupIconGlyph = lookupIconGlyph;
|
|
5191
5845
|
exports.makeSessionId = makeSessionId;
|
|
@@ -5193,11 +5847,18 @@ exports.mergeTheme = mergeTheme;
|
|
|
5193
5847
|
exports.mintDeviceId = mintDeviceId;
|
|
5194
5848
|
exports.motionSpec = motionSpec_exports;
|
|
5195
5849
|
exports.namespaceExtra = namespaceExtra;
|
|
5850
|
+
exports.normalizeAfterCard = normalizeAfterCard;
|
|
5851
|
+
exports.normalizePermissionStatus = normalizePermissionStatus;
|
|
5196
5852
|
exports.onboardingComponents = onboardingComponents;
|
|
5197
5853
|
exports.parseWireFeatures = parseWireFeatures;
|
|
5198
5854
|
exports.peekPersistedSession = peekPersistedSession;
|
|
5855
|
+
exports.permissionEventName = permissionEventName;
|
|
5856
|
+
exports.permissionEventProps = permissionEventProps;
|
|
5857
|
+
exports.permissionScreenId = permissionScreenId;
|
|
5858
|
+
exports.permissionStorageKey = permissionStorageKey;
|
|
5199
5859
|
exports.readCachedFeatures = readCachedFeatures;
|
|
5200
5860
|
exports.readProgress = readProgress;
|
|
5861
|
+
exports.readSettledPermissions = readSettledPermissions;
|
|
5201
5862
|
exports.reportClientEvent = reportClientEvent;
|
|
5202
5863
|
exports.reportClientEventAwait = reportClientEventAwait;
|
|
5203
5864
|
exports.reportClientEvents = reportClientEvents;
|
|
@@ -5208,12 +5869,17 @@ exports.resetActivationRevalidation = resetActivationRevalidation;
|
|
|
5208
5869
|
exports.resetAutoDeviceKeys = resetAutoDeviceKeys;
|
|
5209
5870
|
exports.resetCurrentSessionId = resetCurrentSessionId;
|
|
5210
5871
|
exports.resetFirstOpenLatch = resetFirstOpenLatch;
|
|
5872
|
+
exports.resetIdentityProvenance = resetIdentityProvenance;
|
|
5211
5873
|
exports.resetSessionStartGuard = resetSessionStartGuard;
|
|
5212
5874
|
exports.resolveAutoDeviceKey = resolveAutoDeviceKey;
|
|
5875
|
+
exports.resolveIdentity = resolveIdentity;
|
|
5876
|
+
exports.resolvePermissionCopy = resolvePermissionCopy;
|
|
5213
5877
|
exports.resolvePlanTier = resolvePlanTier;
|
|
5214
5878
|
exports.resolveUserContext = resolveUserContext;
|
|
5215
5879
|
exports.sanitizeUserId = sanitizeUserId;
|
|
5216
5880
|
exports.savePersistedSession = savePersistedSession;
|
|
5881
|
+
exports.saveSettledPermissions = saveSettledPermissions;
|
|
5882
|
+
exports.selectDuePermissionScreen = selectDuePermissionScreen;
|
|
5217
5883
|
exports.sessionStorageKey = sessionStorageKey;
|
|
5218
5884
|
exports.setCurrentSessionId = setCurrentSessionId;
|
|
5219
5885
|
exports.subscribeActivationRevalidation = subscribeActivationRevalidation;
|