@wireai/activation 0.13.0 → 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 +116 -3
- package/README.md +82 -0
- package/dist/analytics/index.d.mts +2 -2
- package/dist/analytics/index.d.ts +2 -2
- package/dist/analytics/index.js +61 -2
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +61 -2
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/{currentSession-_GynvhzT.d.mts → currentSession-ClkLjcJ0.d.mts} +298 -13
- package/dist/{currentSession-D7zabMXK.d.ts → currentSession-DOVZEWJl.d.ts} +298 -13
- package/dist/index.d.mts +195 -4
- package/dist/index.d.ts +195 -4
- package/dist/index.js +909 -340
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +693 -145
- 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 +12 -1
- package/dist/reviews/index.js.map +1 -1
- package/dist/reviews/index.mjs +12 -1
- package/dist/reviews/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/OnboardingFlow.tsx +131 -0
- package/src/WireOnboarding.tsx +63 -2
- package/src/analytics/analyticsEvent.ts +16 -1
- package/src/analytics/eventQueue.ts +2 -0
- package/src/analytics/reportClientEvent.ts +68 -12
- package/src/cards/PermissionCard.tsx +438 -0
- package/src/cards/index.ts +7 -0
- package/src/illustrations/defaultIllustrations.tsx +44 -3
- package/src/index.ts +38 -0
- 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/types.ts +66 -4
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { StyleSheet, Easing, Animated, BackHandler, Modal, Pressable, Text, View, TouchableOpacity, KeyboardAvoidingView, ScrollView, useWindowDimensions, TextInput, Image, AccessibilityInfo, Platform, Dimensions, I18nManager, AppState, NativeModules, TurboModuleRegistry } from 'react-native';
|
|
2
|
-
import
|
|
2
|
+
import React19, { createContext, forwardRef, useRef, useState, useEffect, useCallback, useImperativeHandle, useMemo, useContext, useSyncExternalStore } from 'react';
|
|
3
3
|
import { useWireAIThread, useWireAIAction, ComponentRenderer, WireAIProvider } from 'wireai-rn';
|
|
4
4
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
5
5
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
@@ -484,7 +484,7 @@ var useIllustration = (name2) => {
|
|
|
484
484
|
if (!name2) return void 0;
|
|
485
485
|
return registry[name2];
|
|
486
486
|
};
|
|
487
|
-
var
|
|
487
|
+
var Disc = ({ children }) => {
|
|
488
488
|
const t = useOnboardingTheme();
|
|
489
489
|
return /* @__PURE__ */ jsx(
|
|
490
490
|
View,
|
|
@@ -493,11 +493,23 @@ var Glyph = ({ children }) => {
|
|
|
493
493
|
styles.disc,
|
|
494
494
|
{ backgroundColor: t.colors.primarySoft, borderRadius: t.radius.full }
|
|
495
495
|
],
|
|
496
|
-
children
|
|
496
|
+
children
|
|
497
497
|
}
|
|
498
498
|
);
|
|
499
499
|
};
|
|
500
|
+
var Glyph = ({ children }) => {
|
|
501
|
+
const t = useOnboardingTheme();
|
|
502
|
+
return /* @__PURE__ */ jsx(Disc, { children: /* @__PURE__ */ jsx(Text, { style: [styles.glyphText, { color: t.colors.primary }], allowFontScaling: false, children }) });
|
|
503
|
+
};
|
|
500
504
|
var MomentumGlyph = () => /* @__PURE__ */ jsx(Glyph, { children: "\u2197" });
|
|
505
|
+
var NotificationsGlyph = () => {
|
|
506
|
+
const t = useOnboardingTheme();
|
|
507
|
+
return /* @__PURE__ */ jsx(Disc, { children: /* @__PURE__ */ jsxs(View, { style: styles.bell, children: [
|
|
508
|
+
/* @__PURE__ */ jsx(View, { style: [styles.bellBody, { backgroundColor: t.colors.primary }] }),
|
|
509
|
+
/* @__PURE__ */ jsx(View, { style: [styles.bellRim, { backgroundColor: t.colors.primary }] }),
|
|
510
|
+
/* @__PURE__ */ jsx(View, { style: [styles.bellClapper, { backgroundColor: t.colors.primary }] })
|
|
511
|
+
] }) });
|
|
512
|
+
};
|
|
501
513
|
var BeforeAfterGlyph = () => {
|
|
502
514
|
const t = useOnboardingTheme();
|
|
503
515
|
return /* @__PURE__ */ jsxs(View, { style: styles.row, children: [
|
|
@@ -524,7 +536,8 @@ var BeforeAfterGlyph = () => {
|
|
|
524
536
|
};
|
|
525
537
|
var defaultIllustrations = {
|
|
526
538
|
momentum: /* @__PURE__ */ jsx(MomentumGlyph, {}),
|
|
527
|
-
"before-after": /* @__PURE__ */ jsx(BeforeAfterGlyph, {})
|
|
539
|
+
"before-after": /* @__PURE__ */ jsx(BeforeAfterGlyph, {}),
|
|
540
|
+
notifications: /* @__PURE__ */ jsx(NotificationsGlyph, {})
|
|
528
541
|
};
|
|
529
542
|
var styles = StyleSheet.create({
|
|
530
543
|
disc: {
|
|
@@ -552,7 +565,12 @@ var styles = StyleSheet.create({
|
|
|
552
565
|
arrow: {
|
|
553
566
|
fontSize: 28,
|
|
554
567
|
fontWeight: "700"
|
|
555
|
-
}
|
|
568
|
+
},
|
|
569
|
+
// The bell: a domed body over a wider rim, with the clapper hanging below it.
|
|
570
|
+
bell: { alignItems: "center", justifyContent: "center" },
|
|
571
|
+
bellBody: { width: 34, height: 30, borderTopLeftRadius: 17, borderTopRightRadius: 17 },
|
|
572
|
+
bellRim: { width: 44, height: 5, borderRadius: 3, marginTop: 2 },
|
|
573
|
+
bellClapper: { width: 9, height: 9, borderRadius: 5, marginTop: 3 }
|
|
556
574
|
});
|
|
557
575
|
var EMPTY_REGISTRY2 = {};
|
|
558
576
|
var IconContext = createContext(EMPTY_REGISTRY2);
|
|
@@ -802,7 +820,7 @@ var _AnimatedSparkle = ({ size = 30, color, variant = "spin" }) => {
|
|
|
802
820
|
}
|
|
803
821
|
) });
|
|
804
822
|
};
|
|
805
|
-
var AnimatedSparkle =
|
|
823
|
+
var AnimatedSparkle = React19.memo(_AnimatedSparkle);
|
|
806
824
|
var styles2 = StyleSheet.create({
|
|
807
825
|
box: { alignItems: "center", justifyContent: "center" },
|
|
808
826
|
glyph: {
|
|
@@ -905,7 +923,7 @@ var _LoadingBlock = ({
|
|
|
905
923
|
)
|
|
906
924
|
] });
|
|
907
925
|
};
|
|
908
|
-
var LoadingBlock =
|
|
926
|
+
var LoadingBlock = React19.memo(_LoadingBlock);
|
|
909
927
|
var styles3 = StyleSheet.create({
|
|
910
928
|
center: { flex: 1, alignItems: "center", justifyContent: "center" },
|
|
911
929
|
// Absolutely-positioned label column, pinned just under the centered stage: its top
|
|
@@ -950,7 +968,7 @@ var _LoadingScreen = ({ title, hint }) => {
|
|
|
950
968
|
}
|
|
951
969
|
);
|
|
952
970
|
};
|
|
953
|
-
var LoadingScreen =
|
|
971
|
+
var LoadingScreen = React19.memo(_LoadingScreen);
|
|
954
972
|
var styles4 = StyleSheet.create({
|
|
955
973
|
flex: { flex: 1 }
|
|
956
974
|
});
|
|
@@ -990,7 +1008,7 @@ var _StepProgress = ({ step, complete, approxScreens }) => {
|
|
|
990
1008
|
}
|
|
991
1009
|
) });
|
|
992
1010
|
};
|
|
993
|
-
var StepProgress =
|
|
1011
|
+
var StepProgress = React19.memo(_StepProgress);
|
|
994
1012
|
var styles5 = StyleSheet.create({
|
|
995
1013
|
track: { height: 6, width: "100%", overflow: "hidden" },
|
|
996
1014
|
fill: { height: 6 }
|
|
@@ -1006,31 +1024,31 @@ var _OnboardingScaffold = ({
|
|
|
1006
1024
|
children
|
|
1007
1025
|
}) => {
|
|
1008
1026
|
const t = useOnboardingTheme();
|
|
1009
|
-
const
|
|
1027
|
+
const styles18 = useMemo(() => makeStyles(t), [t]);
|
|
1010
1028
|
const hit = { top: 12, bottom: 12, left: 12, right: 12 };
|
|
1011
1029
|
return /* @__PURE__ */ jsxs(
|
|
1012
1030
|
SafeAreaView,
|
|
1013
1031
|
{
|
|
1014
1032
|
edges: ["top", "bottom"],
|
|
1015
|
-
style: [
|
|
1033
|
+
style: [styles18.flex, { backgroundColor: t.colors.background }],
|
|
1016
1034
|
children: [
|
|
1017
|
-
/* @__PURE__ */ jsxs(View, { style: [
|
|
1018
|
-
/* @__PURE__ */ jsx(View, { style:
|
|
1019
|
-
onSkip ? /* @__PURE__ */ jsx(TouchableOpacity, { onPress: onSkip, hitSlop: hit, style:
|
|
1035
|
+
/* @__PURE__ */ jsxs(View, { style: [styles18.header, { paddingHorizontal: t.spacing.md, paddingTop: t.spacing.md }], children: [
|
|
1036
|
+
/* @__PURE__ */ jsx(View, { style: styles18.progressWrap, children: /* @__PURE__ */ jsx(StepProgress, { step, complete, approxScreens }) }),
|
|
1037
|
+
onSkip ? /* @__PURE__ */ jsx(TouchableOpacity, { onPress: onSkip, hitSlop: hit, style: styles18.skip, children: /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted }], children: skipLabel }) }) : null
|
|
1020
1038
|
] }),
|
|
1021
1039
|
/* @__PURE__ */ jsx(
|
|
1022
1040
|
View,
|
|
1023
1041
|
{
|
|
1024
|
-
style: [
|
|
1042
|
+
style: [styles18.body, { paddingHorizontal: t.spacing.md, paddingTop: t.spacing.md }],
|
|
1025
1043
|
children
|
|
1026
1044
|
}
|
|
1027
1045
|
),
|
|
1028
|
-
onBack ? /* @__PURE__ */ jsx(View, { style: [
|
|
1046
|
+
onBack ? /* @__PURE__ */ jsx(View, { style: [styles18.footer, { paddingHorizontal: t.spacing.md, paddingBottom: t.spacing.sm }], children: /* @__PURE__ */ jsx(TouchableOpacity, { onPress: onBack, hitSlop: hit, children: /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted }], children: backLabel }) }) }) : null
|
|
1029
1047
|
]
|
|
1030
1048
|
}
|
|
1031
1049
|
);
|
|
1032
1050
|
};
|
|
1033
|
-
var OnboardingScaffold =
|
|
1051
|
+
var OnboardingScaffold = React19.memo(_OnboardingScaffold);
|
|
1034
1052
|
function makeStyles(t) {
|
|
1035
1053
|
return StyleSheet.create({
|
|
1036
1054
|
flex: { flex: 1 },
|
|
@@ -1131,7 +1149,7 @@ var _Button = ({
|
|
|
1131
1149
|
}
|
|
1132
1150
|
);
|
|
1133
1151
|
};
|
|
1134
|
-
var Button =
|
|
1152
|
+
var Button = React19.memo(_Button);
|
|
1135
1153
|
var styles6 = StyleSheet.create({
|
|
1136
1154
|
base: { alignItems: "center", justifyContent: "center" },
|
|
1137
1155
|
fill: { width: "100%" }
|
|
@@ -1149,7 +1167,7 @@ var _ErrorBlock = ({
|
|
|
1149
1167
|
onRetry ? /* @__PURE__ */ jsx(Button, { title: retryLabel, onPress: onRetry, variant: "outline" }) : null
|
|
1150
1168
|
] });
|
|
1151
1169
|
};
|
|
1152
|
-
var ErrorBlock =
|
|
1170
|
+
var ErrorBlock = React19.memo(_ErrorBlock);
|
|
1153
1171
|
var styles7 = StyleSheet.create({
|
|
1154
1172
|
center: { flex: 1, alignItems: "center", justifyContent: "center" }
|
|
1155
1173
|
});
|
|
@@ -1162,10 +1180,10 @@ var _CardLayout = ({
|
|
|
1162
1180
|
titleNode
|
|
1163
1181
|
}) => {
|
|
1164
1182
|
const t = useOnboardingTheme();
|
|
1165
|
-
const
|
|
1183
|
+
const styles18 = useMemo(() => makeStyles2(t), [t]);
|
|
1166
1184
|
const hasHeader = !!title || !!subtitle || !!titleNode;
|
|
1167
|
-
return /* @__PURE__ */ jsxs(KeyboardAvoidingView, { style:
|
|
1168
|
-
hasHeader ? /* @__PURE__ */ jsxs(View, { style:
|
|
1185
|
+
return /* @__PURE__ */ jsxs(KeyboardAvoidingView, { style: styles18.fill, behavior: "padding", children: [
|
|
1186
|
+
hasHeader ? /* @__PURE__ */ jsxs(View, { style: styles18.header, children: [
|
|
1169
1187
|
titleNode != null ? titleNode : title ? /* @__PURE__ */ jsx(Text, { style: [labelStyle(t.fonts), { color: t.colors.text }], children: title }) : null,
|
|
1170
1188
|
subtitle ? /* @__PURE__ */ jsx(
|
|
1171
1189
|
Text,
|
|
@@ -1181,10 +1199,10 @@ var _CardLayout = ({
|
|
|
1181
1199
|
/* @__PURE__ */ jsx(
|
|
1182
1200
|
ScrollView,
|
|
1183
1201
|
{
|
|
1184
|
-
style:
|
|
1202
|
+
style: styles18.fill,
|
|
1185
1203
|
contentContainerStyle: [
|
|
1186
|
-
|
|
1187
|
-
align === "center" &&
|
|
1204
|
+
styles18.scrollContent,
|
|
1205
|
+
align === "center" && styles18.center
|
|
1188
1206
|
],
|
|
1189
1207
|
keyboardShouldPersistTaps: "handled",
|
|
1190
1208
|
keyboardDismissMode: "on-drag",
|
|
@@ -1192,10 +1210,10 @@ var _CardLayout = ({
|
|
|
1192
1210
|
children
|
|
1193
1211
|
}
|
|
1194
1212
|
),
|
|
1195
|
-
footer ? /* @__PURE__ */ jsx(View, { style:
|
|
1213
|
+
footer ? /* @__PURE__ */ jsx(View, { style: styles18.footer, children: footer }) : null
|
|
1196
1214
|
] });
|
|
1197
1215
|
};
|
|
1198
|
-
var CardLayout =
|
|
1216
|
+
var CardLayout = React19.memo(_CardLayout);
|
|
1199
1217
|
function makeStyles2(t) {
|
|
1200
1218
|
return StyleSheet.create({
|
|
1201
1219
|
fill: { flex: 1 },
|
|
@@ -1320,7 +1338,7 @@ var _CompletionView = ({
|
|
|
1320
1338
|
}
|
|
1321
1339
|
);
|
|
1322
1340
|
};
|
|
1323
|
-
var CompletionView =
|
|
1341
|
+
var CompletionView = React19.memo(_CompletionView);
|
|
1324
1342
|
var styles8 = StyleSheet.create({
|
|
1325
1343
|
center: { width: "100%", alignItems: "center", justifyContent: "center" },
|
|
1326
1344
|
badgeStage: {
|
|
@@ -1450,7 +1468,7 @@ var transformFor = (variant, t, reduced, layer, travel) => {
|
|
|
1450
1468
|
}
|
|
1451
1469
|
];
|
|
1452
1470
|
};
|
|
1453
|
-
var CardHandoff =
|
|
1471
|
+
var CardHandoff = React19.memo(_CardHandoff);
|
|
1454
1472
|
var styles9 = StyleSheet.create({
|
|
1455
1473
|
fill: { flex: 1 }
|
|
1456
1474
|
});
|
|
@@ -1503,13 +1521,24 @@ var readProgress = (response) => {
|
|
|
1503
1521
|
|
|
1504
1522
|
// src/analytics/reportClientEvent.ts
|
|
1505
1523
|
var makeSessionId = () => `wire_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
1524
|
+
var toWireEvents = (events) => events.map((event) => {
|
|
1525
|
+
if (typeof event.ts !== "number") return event;
|
|
1526
|
+
const { ts, ...rest } = event;
|
|
1527
|
+
if (!Number.isFinite(ts)) return rest;
|
|
1528
|
+
try {
|
|
1529
|
+
return { ...rest, ts: new Date(ts).toISOString() };
|
|
1530
|
+
} catch {
|
|
1531
|
+
return rest;
|
|
1532
|
+
}
|
|
1533
|
+
});
|
|
1506
1534
|
var buildEventsRequest = (target, events) => {
|
|
1507
1535
|
if (!(target == null ? void 0 : target.serverUrl) || events.length === 0) return null;
|
|
1508
1536
|
try {
|
|
1509
1537
|
const url = `${target.serverUrl.replace(/\/$/, "")}/v1/events`;
|
|
1510
1538
|
const headers = { "Content-Type": "application/json" };
|
|
1511
1539
|
if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;
|
|
1512
|
-
|
|
1540
|
+
const body = JSON.stringify({ events: toWireEvents(events) });
|
|
1541
|
+
return { url, init: { method: "POST", headers, body } };
|
|
1513
1542
|
} catch {
|
|
1514
1543
|
return null;
|
|
1515
1544
|
}
|
|
@@ -1521,7 +1550,13 @@ var readEventsAck = async (res) => {
|
|
|
1521
1550
|
const body = await Promise.resolve(json.call(res));
|
|
1522
1551
|
const skipped = body == null ? void 0 : body.skipped;
|
|
1523
1552
|
if (typeof skipped !== "number" || !Number.isFinite(skipped)) return void 0;
|
|
1524
|
-
const reasons = Array.isArray(body == null ? void 0 : body.errors) ? body.errors.map((e) =>
|
|
1553
|
+
const reasons = Array.isArray(body == null ? void 0 : body.errors) ? body.errors.map((e) => {
|
|
1554
|
+
const entry = e;
|
|
1555
|
+
const reason = entry == null ? void 0 : entry.reason;
|
|
1556
|
+
if (typeof reason !== "string") return void 0;
|
|
1557
|
+
const field = entry == null ? void 0 : entry.field;
|
|
1558
|
+
return typeof field === "string" && field.length > 0 ? `${reason} (field: ${field})` : reason;
|
|
1559
|
+
}).filter((r) => typeof r === "string") : [];
|
|
1525
1560
|
return { written: typeof (body == null ? void 0 : body.written) === "number" ? body.written : void 0, skipped, reasons };
|
|
1526
1561
|
} catch {
|
|
1527
1562
|
return void 0;
|
|
@@ -1591,6 +1626,382 @@ var sendPreview = (target, { sessionId, userMessage }) => {
|
|
|
1591
1626
|
} catch {
|
|
1592
1627
|
}
|
|
1593
1628
|
};
|
|
1629
|
+
|
|
1630
|
+
// src/permissions/permissionEvents.ts
|
|
1631
|
+
var WIRE_PERMISSION_EVENTS = {
|
|
1632
|
+
/** The priming screen became visible. The denominator for every rate below. */
|
|
1633
|
+
screenShown: "wire_permission_screen_shown",
|
|
1634
|
+
/** The user tapped the primary, so the OS dialog is about to open. The rationale worked. */
|
|
1635
|
+
primerAccepted: "wire_permission_primer_accepted",
|
|
1636
|
+
/** The OS granted it. */
|
|
1637
|
+
granted: "wire_permission_granted",
|
|
1638
|
+
/** The OS refused it (a permanently blocked answer reports here too, with `status: "blocked"`). */
|
|
1639
|
+
denied: "wire_permission_denied",
|
|
1640
|
+
/** The user took the secondary. The one native prompt was NOT spent. */
|
|
1641
|
+
skipped: "wire_permission_skipped",
|
|
1642
|
+
/** A blocked user was redirected to the OS settings page. */
|
|
1643
|
+
settingsOpened: "wire_permission_settings_opened"
|
|
1644
|
+
};
|
|
1645
|
+
var permissionEventName = (stage) => {
|
|
1646
|
+
switch (stage) {
|
|
1647
|
+
case "shown":
|
|
1648
|
+
return WIRE_PERMISSION_EVENTS.screenShown;
|
|
1649
|
+
case "accepted":
|
|
1650
|
+
return WIRE_PERMISSION_EVENTS.primerAccepted;
|
|
1651
|
+
case "granted":
|
|
1652
|
+
return WIRE_PERMISSION_EVENTS.granted;
|
|
1653
|
+
case "denied":
|
|
1654
|
+
return WIRE_PERMISSION_EVENTS.denied;
|
|
1655
|
+
case "skipped":
|
|
1656
|
+
return WIRE_PERMISSION_EVENTS.skipped;
|
|
1657
|
+
case "settings":
|
|
1658
|
+
return WIRE_PERMISSION_EVENTS.settingsOpened;
|
|
1659
|
+
default: {
|
|
1660
|
+
const _exhaustive = stage;
|
|
1661
|
+
return _exhaustive;
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
};
|
|
1665
|
+
var permissionEventProps = (permission, status) => status ? { permission, status } : { permission };
|
|
1666
|
+
var normalizePermissionStatus = (value) => value === "granted" || value === "denied" || value === "blocked" ? value : "denied";
|
|
1667
|
+
|
|
1668
|
+
// src/haptics/haptics.ts
|
|
1669
|
+
var play = async (tone) => {
|
|
1670
|
+
try {
|
|
1671
|
+
const mod = await import('expo-haptics');
|
|
1672
|
+
if (!mod) return false;
|
|
1673
|
+
if (tone === "selection") ;
|
|
1674
|
+
if (tone === "light") ;
|
|
1675
|
+
await mod.notificationAsync(mod.NotificationFeedbackType.Success);
|
|
1676
|
+
return true;
|
|
1677
|
+
} catch {
|
|
1678
|
+
return false;
|
|
1679
|
+
}
|
|
1680
|
+
};
|
|
1681
|
+
var playHaptic = (tone) => {
|
|
1682
|
+
void play(tone);
|
|
1683
|
+
};
|
|
1684
|
+
|
|
1685
|
+
// src/utils/warnInDev.ts
|
|
1686
|
+
var warnInDev = (message) => {
|
|
1687
|
+
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
1688
|
+
console.warn(message);
|
|
1689
|
+
return true;
|
|
1690
|
+
}
|
|
1691
|
+
return false;
|
|
1692
|
+
};
|
|
1693
|
+
var easeWire4 = Easing.bezier(...WIRE_BEZIER);
|
|
1694
|
+
var RISE_PX = 10;
|
|
1695
|
+
var PERMISSION_CARD_NAME = "PermissionCard";
|
|
1696
|
+
var REQUEST_WATCHDOG_MS = 9e4;
|
|
1697
|
+
var schema = z.object({
|
|
1698
|
+
permission: z.string().describe("Which OS permission this screen primes, e.g. 'notifications'"),
|
|
1699
|
+
title: z.string().describe("Headline for the ask"),
|
|
1700
|
+
message: z.string().describe("Why the app needs it, in the user's terms"),
|
|
1701
|
+
primaryLabel: z.string().describe("Primary button, the only control that can open the OS dialog"),
|
|
1702
|
+
secondaryLabel: z.string().describe("Secondary button, advances without spending the OS prompt"),
|
|
1703
|
+
blockedTitle: z.string().optional().describe("Headline once the permission is permanently refused"),
|
|
1704
|
+
blockedMessage: z.string().optional().describe("Rationale for the settings route"),
|
|
1705
|
+
settingsLabel: z.string().optional().describe("Primary button label on the blocked route"),
|
|
1706
|
+
continueLabel: z.string().optional().describe("Primary button label when there is nothing to ask"),
|
|
1707
|
+
illustration: z.string().optional().describe("Name of an app-provided illustration (defaults to the permission name)")
|
|
1708
|
+
});
|
|
1709
|
+
var _PermissionCard = ({
|
|
1710
|
+
permission,
|
|
1711
|
+
title,
|
|
1712
|
+
message,
|
|
1713
|
+
primaryLabel,
|
|
1714
|
+
secondaryLabel,
|
|
1715
|
+
blockedTitle,
|
|
1716
|
+
blockedMessage,
|
|
1717
|
+
settingsLabel,
|
|
1718
|
+
continueLabel,
|
|
1719
|
+
illustration,
|
|
1720
|
+
request,
|
|
1721
|
+
getStatus,
|
|
1722
|
+
openSettings,
|
|
1723
|
+
onStage,
|
|
1724
|
+
onSettled
|
|
1725
|
+
}) => {
|
|
1726
|
+
const t = useOnboardingTheme();
|
|
1727
|
+
const reduced = useReducedMotion();
|
|
1728
|
+
const art = useIllustration(illustration != null ? illustration : permission);
|
|
1729
|
+
const [status, setStatus] = useState(void 0);
|
|
1730
|
+
const [busy, setBusy] = useState(false);
|
|
1731
|
+
const settledRef = useRef(false);
|
|
1732
|
+
const acceptedRef = useRef(false);
|
|
1733
|
+
const watchdogRef = useRef(null);
|
|
1734
|
+
const clearWatchdog = useCallback(() => {
|
|
1735
|
+
if (watchdogRef.current) {
|
|
1736
|
+
clearTimeout(watchdogRef.current);
|
|
1737
|
+
watchdogRef.current = null;
|
|
1738
|
+
}
|
|
1739
|
+
}, []);
|
|
1740
|
+
useEffect(() => clearWatchdog, [clearWatchdog]);
|
|
1741
|
+
const onStageRef = useRef(onStage);
|
|
1742
|
+
onStageRef.current = onStage;
|
|
1743
|
+
const onSettledRef = useRef(onSettled);
|
|
1744
|
+
onSettledRef.current = onSettled;
|
|
1745
|
+
const getStatusRef = useRef(getStatus);
|
|
1746
|
+
getStatusRef.current = getStatus;
|
|
1747
|
+
const shownRef = useRef(false);
|
|
1748
|
+
useEffect(() => {
|
|
1749
|
+
var _a2;
|
|
1750
|
+
if (shownRef.current) return;
|
|
1751
|
+
shownRef.current = true;
|
|
1752
|
+
(_a2 = onStageRef.current) == null ? void 0 : _a2.call(onStageRef, "shown");
|
|
1753
|
+
}, []);
|
|
1754
|
+
useEffect(() => {
|
|
1755
|
+
const probe = getStatusRef.current;
|
|
1756
|
+
if (!probe) return;
|
|
1757
|
+
let cancelled = false;
|
|
1758
|
+
try {
|
|
1759
|
+
void Promise.resolve(probe()).then((value) => {
|
|
1760
|
+
if (!cancelled) setStatus(normalizePermissionStatus(value));
|
|
1761
|
+
}).catch(() => {
|
|
1762
|
+
});
|
|
1763
|
+
} catch {
|
|
1764
|
+
}
|
|
1765
|
+
return () => {
|
|
1766
|
+
cancelled = true;
|
|
1767
|
+
};
|
|
1768
|
+
}, []);
|
|
1769
|
+
const requestRef = useRef(request);
|
|
1770
|
+
requestRef.current = request;
|
|
1771
|
+
useEffect(() => {
|
|
1772
|
+
if (requestRef.current) return;
|
|
1773
|
+
warnInDev(
|
|
1774
|
+
`[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).`
|
|
1775
|
+
);
|
|
1776
|
+
}, [permission]);
|
|
1777
|
+
const settle = useCallback((outcome) => {
|
|
1778
|
+
var _a2;
|
|
1779
|
+
if (settledRef.current) return;
|
|
1780
|
+
settledRef.current = true;
|
|
1781
|
+
clearWatchdog();
|
|
1782
|
+
(_a2 = onSettledRef.current) == null ? void 0 : _a2.call(onSettledRef, outcome);
|
|
1783
|
+
}, [clearWatchdog]);
|
|
1784
|
+
const primaryAction = status === "blocked" ? "settings" : status === "granted" || !request ? "continue" : "ask";
|
|
1785
|
+
const handlePrimary = useCallback(() => {
|
|
1786
|
+
var _a2, _b, _c, _d;
|
|
1787
|
+
if (settledRef.current || busy) return;
|
|
1788
|
+
if (primaryAction === "settings") {
|
|
1789
|
+
(_a2 = onStageRef.current) == null ? void 0 : _a2.call(onStageRef, "settings", "blocked");
|
|
1790
|
+
try {
|
|
1791
|
+
void Promise.resolve(openSettings == null ? void 0 : openSettings()).catch(() => {
|
|
1792
|
+
});
|
|
1793
|
+
} catch {
|
|
1794
|
+
}
|
|
1795
|
+
settle("blocked");
|
|
1796
|
+
return;
|
|
1797
|
+
}
|
|
1798
|
+
if (primaryAction === "continue") {
|
|
1799
|
+
if (status === "granted") {
|
|
1800
|
+
(_b = onStageRef.current) == null ? void 0 : _b.call(onStageRef, "granted", "granted");
|
|
1801
|
+
settle("granted");
|
|
1802
|
+
} else {
|
|
1803
|
+
(_c = onStageRef.current) == null ? void 0 : _c.call(onStageRef, "skipped");
|
|
1804
|
+
settle("skipped");
|
|
1805
|
+
}
|
|
1806
|
+
return;
|
|
1807
|
+
}
|
|
1808
|
+
if (!acceptedRef.current) {
|
|
1809
|
+
acceptedRef.current = true;
|
|
1810
|
+
(_d = onStageRef.current) == null ? void 0 : _d.call(onStageRef, "accepted");
|
|
1811
|
+
}
|
|
1812
|
+
setBusy(true);
|
|
1813
|
+
const finish = (value) => {
|
|
1814
|
+
var _a3, _b2;
|
|
1815
|
+
if (settledRef.current) return;
|
|
1816
|
+
clearWatchdog();
|
|
1817
|
+
const resolved = normalizePermissionStatus(value);
|
|
1818
|
+
setBusy(false);
|
|
1819
|
+
setStatus(resolved);
|
|
1820
|
+
if (resolved === "granted") {
|
|
1821
|
+
playHaptic("success");
|
|
1822
|
+
(_a3 = onStageRef.current) == null ? void 0 : _a3.call(onStageRef, "granted", "granted");
|
|
1823
|
+
} else {
|
|
1824
|
+
(_b2 = onStageRef.current) == null ? void 0 : _b2.call(onStageRef, "denied", resolved);
|
|
1825
|
+
}
|
|
1826
|
+
settle(resolved);
|
|
1827
|
+
};
|
|
1828
|
+
clearWatchdog();
|
|
1829
|
+
watchdogRef.current = setTimeout(() => {
|
|
1830
|
+
watchdogRef.current = null;
|
|
1831
|
+
if (settledRef.current) return;
|
|
1832
|
+
setBusy(false);
|
|
1833
|
+
warnInDev(
|
|
1834
|
+
`[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.`
|
|
1835
|
+
);
|
|
1836
|
+
}, REQUEST_WATCHDOG_MS);
|
|
1837
|
+
try {
|
|
1838
|
+
void Promise.resolve(request == null ? void 0 : request()).then(finish, () => finish("denied"));
|
|
1839
|
+
} catch {
|
|
1840
|
+
finish("denied");
|
|
1841
|
+
}
|
|
1842
|
+
}, [busy, primaryAction, status, request, openSettings, settle, clearWatchdog, permission]);
|
|
1843
|
+
const handleSecondary = useCallback(() => {
|
|
1844
|
+
var _a2;
|
|
1845
|
+
if (settledRef.current || busy) return;
|
|
1846
|
+
(_a2 = onStageRef.current) == null ? void 0 : _a2.call(onStageRef, "skipped");
|
|
1847
|
+
settle("skipped");
|
|
1848
|
+
}, [busy, settle]);
|
|
1849
|
+
const popT = useRef(new Animated.Value(reduced ? 1 : 0)).current;
|
|
1850
|
+
const titleT = useRef(new Animated.Value(reduced ? 1 : 0)).current;
|
|
1851
|
+
const bodyT = useRef(new Animated.Value(reduced ? 1 : 0)).current;
|
|
1852
|
+
useEffect(() => {
|
|
1853
|
+
if (reduced) {
|
|
1854
|
+
popT.setValue(1);
|
|
1855
|
+
titleT.setValue(1);
|
|
1856
|
+
bodyT.setValue(1);
|
|
1857
|
+
return;
|
|
1858
|
+
}
|
|
1859
|
+
const rise = (value, delay) => Animated.timing(value, {
|
|
1860
|
+
toValue: 1,
|
|
1861
|
+
duration: scaledMs(INTERSTITIAL_HEAD_MS),
|
|
1862
|
+
delay,
|
|
1863
|
+
easing: easeWire4,
|
|
1864
|
+
useNativeDriver: true
|
|
1865
|
+
});
|
|
1866
|
+
const anims = [
|
|
1867
|
+
Animated.spring(popT, {
|
|
1868
|
+
toValue: 1,
|
|
1869
|
+
friction: STATUS_POP_SPRING.friction,
|
|
1870
|
+
tension: STATUS_POP_SPRING.tension,
|
|
1871
|
+
useNativeDriver: true
|
|
1872
|
+
}),
|
|
1873
|
+
rise(titleT, scaledMs(INTERSTITIAL_HEAD_STAGGER_MS)),
|
|
1874
|
+
rise(bodyT, scaledMs(INTERSTITIAL_HEAD_STAGGER_MS * 2))
|
|
1875
|
+
];
|
|
1876
|
+
anims.forEach((a) => a.start());
|
|
1877
|
+
return () => anims.forEach((a) => a.stop());
|
|
1878
|
+
}, [reduced, popT, titleT, bodyT]);
|
|
1879
|
+
const popScale = popT.interpolate({
|
|
1880
|
+
inputRange: [0, 1],
|
|
1881
|
+
outputRange: [STATUS_POP_SCALE_FROM, 1]
|
|
1882
|
+
});
|
|
1883
|
+
const riseStyle = (value) => ({
|
|
1884
|
+
opacity: value,
|
|
1885
|
+
transform: [
|
|
1886
|
+
{ translateY: value.interpolate({ inputRange: [0, 1], outputRange: [RISE_PX, 0] }) }
|
|
1887
|
+
]
|
|
1888
|
+
});
|
|
1889
|
+
const blocked = status === "blocked";
|
|
1890
|
+
const shownTitle = blocked ? blockedTitle != null ? blockedTitle : title : title;
|
|
1891
|
+
const shownMessage = blocked ? blockedMessage != null ? blockedMessage : message : message;
|
|
1892
|
+
const primaryTitle = primaryAction === "settings" ? settingsLabel != null ? settingsLabel : primaryLabel : primaryAction === "continue" ? continueLabel != null ? continueLabel : primaryLabel : primaryLabel;
|
|
1893
|
+
const showSecondary = status !== "granted";
|
|
1894
|
+
return /* @__PURE__ */ jsx(
|
|
1895
|
+
CardLayout,
|
|
1896
|
+
{
|
|
1897
|
+
align: "center",
|
|
1898
|
+
footer: /* @__PURE__ */ jsxs(View, { style: [styles10.footer, { gap: t.spacing.sm }], children: [
|
|
1899
|
+
/* @__PURE__ */ jsx(Button, { title: primaryTitle, onPress: handlePrimary, variant: "primary", disabled: busy }),
|
|
1900
|
+
showSecondary ? /* @__PURE__ */ jsx(
|
|
1901
|
+
Button,
|
|
1902
|
+
{
|
|
1903
|
+
title: secondaryLabel,
|
|
1904
|
+
onPress: handleSecondary,
|
|
1905
|
+
variant: "outline",
|
|
1906
|
+
disabled: busy
|
|
1907
|
+
}
|
|
1908
|
+
) : null
|
|
1909
|
+
] }),
|
|
1910
|
+
children: /* @__PURE__ */ jsxs(View, { style: [styles10.center, { gap: t.spacing.md }], children: [
|
|
1911
|
+
art ? /* @__PURE__ */ jsx(
|
|
1912
|
+
Animated.View,
|
|
1913
|
+
{
|
|
1914
|
+
style: [styles10.art, { opacity: popT, transform: [{ scale: popScale }] }],
|
|
1915
|
+
children: art
|
|
1916
|
+
}
|
|
1917
|
+
) : null,
|
|
1918
|
+
/* @__PURE__ */ jsx(Animated.View, { style: riseStyle(titleT), children: /* @__PURE__ */ jsx(Text, { style: [headingStyle(t.fonts), { color: t.colors.text, textAlign: "center" }], children: shownTitle }) }),
|
|
1919
|
+
/* @__PURE__ */ jsx(Animated.View, { style: riseStyle(bodyT), children: /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted, textAlign: "center" }], children: shownMessage }) })
|
|
1920
|
+
] })
|
|
1921
|
+
}
|
|
1922
|
+
);
|
|
1923
|
+
};
|
|
1924
|
+
var PermissionCardView = React19.memo(_PermissionCard);
|
|
1925
|
+
var PermissionCard = {
|
|
1926
|
+
name: PERMISSION_CARD_NAME,
|
|
1927
|
+
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.",
|
|
1928
|
+
component: PermissionCardView,
|
|
1929
|
+
propsSchema: schema
|
|
1930
|
+
};
|
|
1931
|
+
var styles10 = StyleSheet.create({
|
|
1932
|
+
center: { width: "100%", alignItems: "center", justifyContent: "center" },
|
|
1933
|
+
art: { alignItems: "center", justifyContent: "center" },
|
|
1934
|
+
footer: { width: "100%", alignItems: "center" }
|
|
1935
|
+
});
|
|
1936
|
+
|
|
1937
|
+
// src/permissions/placement.ts
|
|
1938
|
+
var DEFAULT_PERMISSION_PLACEMENT = "beforeEnd";
|
|
1939
|
+
var normalizeAfterCard = (value) => Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
|
|
1940
|
+
var targetIndex = (placement) => {
|
|
1941
|
+
if (placement === "beforeEnd") return void 0;
|
|
1942
|
+
if (placement === "start") return 0;
|
|
1943
|
+
return normalizeAfterCard(placement.afterCard);
|
|
1944
|
+
};
|
|
1945
|
+
var isPermissionDue = (placement, position) => {
|
|
1946
|
+
if (position.cardIndex < 0) return false;
|
|
1947
|
+
const target = targetIndex(placement);
|
|
1948
|
+
if (target === void 0) return position.isTerminal;
|
|
1949
|
+
return position.cardIndex >= target || position.isTerminal;
|
|
1950
|
+
};
|
|
1951
|
+
var permissionScreenId = (screen, index) => {
|
|
1952
|
+
var _a2;
|
|
1953
|
+
return (_a2 = screen.id) != null ? _a2 : `${screen.permission}:${index}`;
|
|
1954
|
+
};
|
|
1955
|
+
var selectDuePermissionScreen = (screens, settled, position) => {
|
|
1956
|
+
var _a2;
|
|
1957
|
+
if (!screens || screens.length === 0) return void 0;
|
|
1958
|
+
for (let index = 0; index < screens.length; index++) {
|
|
1959
|
+
const screen = screens[index];
|
|
1960
|
+
if (!screen) continue;
|
|
1961
|
+
const id = permissionScreenId(screen, index);
|
|
1962
|
+
if (settled.includes(id)) continue;
|
|
1963
|
+
if (isPermissionDue((_a2 = screen.placement) != null ? _a2 : DEFAULT_PERMISSION_PLACEMENT, position)) {
|
|
1964
|
+
return { screen, id };
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
return void 0;
|
|
1968
|
+
};
|
|
1969
|
+
|
|
1970
|
+
// src/permissions/permissionCopy.ts
|
|
1971
|
+
var NOTIFICATIONS_PERMISSION_COPY = {
|
|
1972
|
+
title: "Want a nudge at the right moment?",
|
|
1973
|
+
message: "Turn notifications on and we will remind you when it actually helps. No daily noise, and you can turn them off any time.",
|
|
1974
|
+
primaryLabel: "Enable notifications",
|
|
1975
|
+
secondaryLabel: "Maybe later",
|
|
1976
|
+
blockedTitle: "Notifications are switched off",
|
|
1977
|
+
blockedMessage: "Notifications are turned off for this app, so we cannot ask from here. You can switch them back on in Settings.",
|
|
1978
|
+
settingsLabel: "Open settings",
|
|
1979
|
+
continueLabel: "Continue"
|
|
1980
|
+
};
|
|
1981
|
+
var GENERIC_PERMISSION_COPY = {
|
|
1982
|
+
title: "One quick permission",
|
|
1983
|
+
message: "We need your permission for this part to work. You can change it any time.",
|
|
1984
|
+
primaryLabel: "Allow",
|
|
1985
|
+
secondaryLabel: "Maybe later",
|
|
1986
|
+
blockedTitle: "Permission is switched off",
|
|
1987
|
+
blockedMessage: "This permission is turned off for this app, so we cannot ask from here. You can switch it back on in Settings.",
|
|
1988
|
+
settingsLabel: "Open settings",
|
|
1989
|
+
continueLabel: "Continue"
|
|
1990
|
+
};
|
|
1991
|
+
var DEFAULT_PERMISSION_COPY = {
|
|
1992
|
+
notifications: NOTIFICATIONS_PERMISSION_COPY
|
|
1993
|
+
};
|
|
1994
|
+
var resolvePermissionCopy = (permission, overrides) => {
|
|
1995
|
+
var _a2;
|
|
1996
|
+
const base = (_a2 = DEFAULT_PERMISSION_COPY[permission]) != null ? _a2 : GENERIC_PERMISSION_COPY;
|
|
1997
|
+
if (!overrides) return base;
|
|
1998
|
+
const merged = { ...base };
|
|
1999
|
+
for (const key of Object.keys(overrides)) {
|
|
2000
|
+
const value = overrides[key];
|
|
2001
|
+
if (typeof value === "string" && value.length > 0) merged[key] = value;
|
|
2002
|
+
}
|
|
2003
|
+
return merged;
|
|
2004
|
+
};
|
|
1594
2005
|
var SKIP_ONE_SENTINEL = "__wireai_skip__";
|
|
1595
2006
|
var ANSWER_CALLBACKS = ["onSubmit", "onSelect", "onConfirm", "onPress"];
|
|
1596
2007
|
var DEFAULT_COPY = {
|
|
@@ -1619,7 +2030,11 @@ var OnboardingFlow = ({
|
|
|
1619
2030
|
reportTarget,
|
|
1620
2031
|
sessionId,
|
|
1621
2032
|
resumed = false,
|
|
1622
|
-
clientContext
|
|
2033
|
+
clientContext,
|
|
2034
|
+
permissionScreens,
|
|
2035
|
+
settledPermissions,
|
|
2036
|
+
permissionsPending = false,
|
|
2037
|
+
onPermissionSettled
|
|
1623
2038
|
}) => {
|
|
1624
2039
|
var _a2, _b, _c, _d, _e, _f;
|
|
1625
2040
|
const { messages, error, isLoading, sendMessage, reset } = useWireAIThread();
|
|
@@ -1650,6 +2065,9 @@ var OnboardingFlow = ({
|
|
|
1650
2065
|
const [timedOut, setTimedOut] = useState(false);
|
|
1651
2066
|
const [validationError, setValidationError] = useState();
|
|
1652
2067
|
const [validating, setValidating] = useState(false);
|
|
2068
|
+
const [locallySettledPermissions, setLocallySettledPermissions] = useState([]);
|
|
2069
|
+
const onPermissionSettledRef = useRef(onPermissionSettled);
|
|
2070
|
+
onPermissionSettledRef.current = onPermissionSettled;
|
|
1653
2071
|
const lastCard = useMemo(
|
|
1654
2072
|
() => [...messages].reverse().find((m) => {
|
|
1655
2073
|
var _a3;
|
|
@@ -1760,6 +2178,36 @@ var OnboardingFlow = ({
|
|
|
1760
2178
|
if (isLoading) return;
|
|
1761
2179
|
sendMessage(SKIP_ONE_SENTINEL);
|
|
1762
2180
|
}, [isLoading, sendMessage]);
|
|
2181
|
+
const emitPermissionStage = useCallback(
|
|
2182
|
+
(permission, stage, status) => {
|
|
2183
|
+
var _a3, _b2, _c2, _d2;
|
|
2184
|
+
(_a3 = onEventRef.current) == null ? void 0 : _a3.call(onEventRef, { type: "permission", permission, stage, status });
|
|
2185
|
+
reportClientEvent(reportTargetRef.current, {
|
|
2186
|
+
event_type: "app_event",
|
|
2187
|
+
session_id: sessionIdRef.current,
|
|
2188
|
+
question_key: permissionEventName(stage),
|
|
2189
|
+
component: PERMISSION_CARD_NAME,
|
|
2190
|
+
screen_index: lastScreenIndexRef.current >= 0 ? lastScreenIndexRef.current : void 0,
|
|
2191
|
+
meta: JSON.stringify(permissionEventProps(permission, status)),
|
|
2192
|
+
device: (_b2 = clientContextRef.current) == null ? void 0 : _b2.device,
|
|
2193
|
+
user_context: (_c2 = clientContextRef.current) == null ? void 0 : _c2.userContext,
|
|
2194
|
+
user_id: (_d2 = clientContextRef.current) == null ? void 0 : _d2.userId
|
|
2195
|
+
});
|
|
2196
|
+
},
|
|
2197
|
+
[]
|
|
2198
|
+
);
|
|
2199
|
+
const settlePermission = useCallback(
|
|
2200
|
+
(id, screen, outcome) => {
|
|
2201
|
+
var _a3, _b2;
|
|
2202
|
+
setLocallySettledPermissions((prev) => prev.includes(id) ? prev : [...prev, id]);
|
|
2203
|
+
(_a3 = onPermissionSettledRef.current) == null ? void 0 : _a3.call(onPermissionSettledRef, id, outcome);
|
|
2204
|
+
try {
|
|
2205
|
+
(_b2 = screen.onResult) == null ? void 0 : _b2.call(screen, screen.permission, outcome);
|
|
2206
|
+
} catch {
|
|
2207
|
+
}
|
|
2208
|
+
},
|
|
2209
|
+
[]
|
|
2210
|
+
);
|
|
1763
2211
|
const previewOnSelect = useCallback((value) => {
|
|
1764
2212
|
const target = reportTargetRef.current;
|
|
1765
2213
|
if (!(target == null ? void 0 : target.serverUrl)) return;
|
|
@@ -1830,6 +2278,36 @@ var OnboardingFlow = ({
|
|
|
1830
2278
|
const step = (_a2 = progress.step) != null ? _a2 : renderedCount;
|
|
1831
2279
|
const total = (_b = progress.total) != null ? _b : approxScreens;
|
|
1832
2280
|
const isTerminal = response.action === "render" && response.component === "StatusCard";
|
|
2281
|
+
const duePermission = permissionsPending ? void 0 : selectDuePermissionScreen(
|
|
2282
|
+
permissionScreens,
|
|
2283
|
+
settledPermissions ? [...settledPermissions, ...locallySettledPermissions] : locallySettledPermissions,
|
|
2284
|
+
{ cardIndex: renderedCount - 1, isTerminal }
|
|
2285
|
+
);
|
|
2286
|
+
if (duePermission) {
|
|
2287
|
+
const { screen, id } = duePermission;
|
|
2288
|
+
const copy2 = resolvePermissionCopy(screen.permission, screen.copy);
|
|
2289
|
+
return /* @__PURE__ */ jsx(OnboardingScaffold, { step, approxScreens: total, children: /* @__PURE__ */ jsx(CardHandoff, { transitionKey: `permission:${id}`, variant: "spring", children: /* @__PURE__ */ jsx(
|
|
2290
|
+
PermissionCardView,
|
|
2291
|
+
{
|
|
2292
|
+
permission: screen.permission,
|
|
2293
|
+
title: copy2.title,
|
|
2294
|
+
message: copy2.message,
|
|
2295
|
+
primaryLabel: copy2.primaryLabel,
|
|
2296
|
+
secondaryLabel: copy2.secondaryLabel,
|
|
2297
|
+
blockedTitle: copy2.blockedTitle,
|
|
2298
|
+
blockedMessage: copy2.blockedMessage,
|
|
2299
|
+
settingsLabel: copy2.settingsLabel,
|
|
2300
|
+
continueLabel: copy2.continueLabel,
|
|
2301
|
+
illustration: screen.illustration,
|
|
2302
|
+
request: screen.request,
|
|
2303
|
+
getStatus: screen.getStatus,
|
|
2304
|
+
openSettings: screen.openSettings,
|
|
2305
|
+
onStage: (stage, status) => emitPermissionStage(screen.permission, stage, status),
|
|
2306
|
+
onSettled: (outcome) => settlePermission(id, screen, outcome)
|
|
2307
|
+
},
|
|
2308
|
+
id
|
|
2309
|
+
) }) });
|
|
2310
|
+
}
|
|
1833
2311
|
if (isTerminal) {
|
|
1834
2312
|
const props = (_c = response.props) != null ? _c : {};
|
|
1835
2313
|
return /* @__PURE__ */ jsx(OnboardingScaffold, { step, complete: true, approxScreens: total, children: /* @__PURE__ */ jsx(
|
|
@@ -1888,8 +2366,8 @@ var OnboardingFlow = ({
|
|
|
1888
2366
|
}
|
|
1889
2367
|
);
|
|
1890
2368
|
};
|
|
1891
|
-
var
|
|
1892
|
-
var
|
|
2369
|
+
var easeWire5 = Easing.bezier(...WIRE_BEZIER);
|
|
2370
|
+
var schema2 = z.object({
|
|
1893
2371
|
title: z.string().describe("Question or section header, e.g. 'Which season?'"),
|
|
1894
2372
|
chips: z.array(z.string()).min(1).describe("List of option labels shown as chips, e.g. ['Spring','Summer','Autumn','Winter']"),
|
|
1895
2373
|
multiSelect: z.boolean().optional().describe("Allow multiple chips selected at once, default true"),
|
|
@@ -1920,7 +2398,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
|
|
|
1920
2398
|
const anim = Animated.timing(fillT, {
|
|
1921
2399
|
toValue: isSelected ? 1 : 0,
|
|
1922
2400
|
duration: scaledMs(stateChangeDuration(reduced, CHIP_FILL_MS)),
|
|
1923
|
-
easing:
|
|
2401
|
+
easing: easeWire5,
|
|
1924
2402
|
useNativeDriver: true
|
|
1925
2403
|
});
|
|
1926
2404
|
anim.start();
|
|
@@ -1960,7 +2438,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
|
|
|
1960
2438
|
Animated.View,
|
|
1961
2439
|
{
|
|
1962
2440
|
style: [
|
|
1963
|
-
|
|
2441
|
+
styles11.chip,
|
|
1964
2442
|
{
|
|
1965
2443
|
borderRadius: t.radius.full,
|
|
1966
2444
|
paddingHorizontal: t.spacing.md,
|
|
@@ -1977,7 +2455,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
|
|
|
1977
2455
|
pointerEvents: "none",
|
|
1978
2456
|
style: [
|
|
1979
2457
|
StyleSheet.absoluteFill,
|
|
1980
|
-
|
|
2458
|
+
styles11.fillOverlay,
|
|
1981
2459
|
{
|
|
1982
2460
|
borderRadius: t.radius.full,
|
|
1983
2461
|
borderColor: t.colors.primary,
|
|
@@ -1991,7 +2469,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
|
|
|
1991
2469
|
Animated.Text,
|
|
1992
2470
|
{
|
|
1993
2471
|
style: [
|
|
1994
|
-
|
|
2472
|
+
styles11.check,
|
|
1995
2473
|
{ color: t.colors.onPrimary, opacity: checkT, transform: [{ scale: checkScale }] }
|
|
1996
2474
|
],
|
|
1997
2475
|
children: "\u2713"
|
|
@@ -2002,7 +2480,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
|
|
|
2002
2480
|
{
|
|
2003
2481
|
style: [
|
|
2004
2482
|
captionStyle(t.fonts),
|
|
2005
|
-
|
|
2483
|
+
styles11.chipLabel,
|
|
2006
2484
|
{ color: isSelected ? t.colors.onPrimary : t.colors.textMuted }
|
|
2007
2485
|
],
|
|
2008
2486
|
children: chip
|
|
@@ -2014,7 +2492,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
|
|
|
2014
2492
|
}
|
|
2015
2493
|
);
|
|
2016
2494
|
};
|
|
2017
|
-
var ChipItem =
|
|
2495
|
+
var ChipItem = React19.memo(_ChipItem);
|
|
2018
2496
|
var _ChipSelectCard = ({
|
|
2019
2497
|
title,
|
|
2020
2498
|
chips,
|
|
@@ -2064,7 +2542,7 @@ var _ChipSelectCard = ({
|
|
|
2064
2542
|
disabled: selected.length === 0 || submitted
|
|
2065
2543
|
}
|
|
2066
2544
|
),
|
|
2067
|
-
children: /* @__PURE__ */ jsx(View, { style: [
|
|
2545
|
+
children: /* @__PURE__ */ jsx(View, { style: [styles11.chipsRow, { gap: t.spacing.sm + t.spacing.xs }], children: chips.map((chip) => /* @__PURE__ */ jsx(
|
|
2068
2546
|
ChipItem,
|
|
2069
2547
|
{
|
|
2070
2548
|
chip,
|
|
@@ -2080,18 +2558,18 @@ var _ChipSelectCard = ({
|
|
|
2080
2558
|
var ChipSelectCard = {
|
|
2081
2559
|
name: "ChipSelectCard",
|
|
2082
2560
|
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.",
|
|
2083
|
-
component:
|
|
2084
|
-
propsSchema:
|
|
2561
|
+
component: React19.memo(_ChipSelectCard),
|
|
2562
|
+
propsSchema: schema2,
|
|
2085
2563
|
defaultProps: { multiSelect: true, submitLabel: "Continue" }
|
|
2086
2564
|
};
|
|
2087
|
-
var
|
|
2565
|
+
var styles11 = StyleSheet.create({
|
|
2088
2566
|
chipsRow: { flexDirection: "row", flexWrap: "wrap" },
|
|
2089
2567
|
chip: { borderWidth: 1.5, flexDirection: "row", alignItems: "center", gap: 6, overflow: "hidden" },
|
|
2090
2568
|
fillOverlay: { borderWidth: 1.5 },
|
|
2091
2569
|
chipLabel: { fontWeight: "600" },
|
|
2092
2570
|
check: { fontSize: 13, fontWeight: "800", lineHeight: 15 }
|
|
2093
2571
|
});
|
|
2094
|
-
var
|
|
2572
|
+
var schema3 = z.object({
|
|
2095
2573
|
label: z.string().describe("Input field label / the question being asked"),
|
|
2096
2574
|
hint: z.string().optional().describe(
|
|
2097
2575
|
"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."
|
|
@@ -2116,7 +2594,7 @@ var _TextInputCard = ({
|
|
|
2116
2594
|
lines = 6
|
|
2117
2595
|
}) => {
|
|
2118
2596
|
const t = useOnboardingTheme();
|
|
2119
|
-
const
|
|
2597
|
+
const styles18 = useMemo(() => makeStyles3(t), [t]);
|
|
2120
2598
|
const { height: screenHeight } = useWindowDimensions();
|
|
2121
2599
|
const [value, setValue] = useState("");
|
|
2122
2600
|
const [submitted, setSubmitted] = useState(false);
|
|
@@ -2143,7 +2621,7 @@ var _TextInputCard = ({
|
|
|
2143
2621
|
disabled: !value.trim() || locked
|
|
2144
2622
|
}
|
|
2145
2623
|
),
|
|
2146
|
-
children: /* @__PURE__ */ jsxs(View, { style:
|
|
2624
|
+
children: /* @__PURE__ */ jsxs(View, { style: styles18.row, children: [
|
|
2147
2625
|
/* @__PURE__ */ jsx(
|
|
2148
2626
|
TextInput,
|
|
2149
2627
|
{
|
|
@@ -2157,7 +2635,7 @@ var _TextInputCard = ({
|
|
|
2157
2635
|
textAlignVertical: multiline ? "top" : "center",
|
|
2158
2636
|
style: [
|
|
2159
2637
|
bodyStyle(t.fonts),
|
|
2160
|
-
|
|
2638
|
+
styles18.input,
|
|
2161
2639
|
{
|
|
2162
2640
|
color: t.colors.text,
|
|
2163
2641
|
backgroundColor: t.colors.surface,
|
|
@@ -2177,8 +2655,8 @@ var _TextInputCard = ({
|
|
|
2177
2655
|
var TextInputCard = {
|
|
2178
2656
|
name: "TextInputCard",
|
|
2179
2657
|
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.",
|
|
2180
|
-
component:
|
|
2181
|
-
propsSchema:
|
|
2658
|
+
component: React19.memo(_TextInputCard),
|
|
2659
|
+
propsSchema: schema3,
|
|
2182
2660
|
defaultProps: { submitLabel: "Submit", multiline: true, lines: 6 }
|
|
2183
2661
|
};
|
|
2184
2662
|
function makeStyles3(t) {
|
|
@@ -2333,7 +2811,7 @@ var _WireIcon = ({ name: name2, size = 20, color, requireModule }) => {
|
|
|
2333
2811
|
return /* @__PURE__ */ jsx(
|
|
2334
2812
|
View,
|
|
2335
2813
|
{
|
|
2336
|
-
style:
|
|
2814
|
+
style: styles12.decorative,
|
|
2337
2815
|
accessible: false,
|
|
2338
2816
|
importantForAccessibility: "no-hide-descendants",
|
|
2339
2817
|
children: hostIcon
|
|
@@ -2347,7 +2825,7 @@ var _WireIcon = ({ name: name2, size = 20, color, requireModule }) => {
|
|
|
2347
2825
|
return /* @__PURE__ */ jsx(
|
|
2348
2826
|
View,
|
|
2349
2827
|
{
|
|
2350
|
-
style:
|
|
2828
|
+
style: styles12.decorative,
|
|
2351
2829
|
accessible: false,
|
|
2352
2830
|
importantForAccessibility: "no-hide-descendants",
|
|
2353
2831
|
children: /* @__PURE__ */ jsx(
|
|
@@ -2362,9 +2840,9 @@ var _WireIcon = ({ name: name2, size = 20, color, requireModule }) => {
|
|
|
2362
2840
|
}
|
|
2363
2841
|
);
|
|
2364
2842
|
};
|
|
2365
|
-
var WireIcon =
|
|
2843
|
+
var WireIcon = React19.memo(_WireIcon);
|
|
2366
2844
|
WireIcon.displayName = "WireIcon";
|
|
2367
|
-
var
|
|
2845
|
+
var styles12 = StyleSheet.create({
|
|
2368
2846
|
// No size/margin of its own: the glyph decides its footprint, so an absent icon and a
|
|
2369
2847
|
// present one differ by exactly the glyph — never by a wrapper.
|
|
2370
2848
|
decorative: {}
|
|
@@ -2378,8 +2856,8 @@ var optionObjectSchema = z.object({
|
|
|
2378
2856
|
var coerceOptions = (arr) => Array.isArray(arr) ? arr.map((item) => typeof item === "string" ? { value: item, label: item } : item) : arr;
|
|
2379
2857
|
var optionsField = (description) => z.preprocess(coerceOptions, z.array(optionObjectSchema).min(1)).describe(description);
|
|
2380
2858
|
var normalizeOptions = (options) => options.map((opt) => typeof opt === "string" ? { value: opt, label: opt } : opt);
|
|
2381
|
-
var
|
|
2382
|
-
var
|
|
2859
|
+
var easeWire6 = Easing.bezier(...WIRE_BEZIER);
|
|
2860
|
+
var schema4 = z.object({
|
|
2383
2861
|
title: z.string().describe("Question or prompt for the user"),
|
|
2384
2862
|
options: optionsField("List of choices"),
|
|
2385
2863
|
submitLabel: z.string().optional().describe("Submit button label, default: Continue"),
|
|
@@ -2409,7 +2887,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
|
|
|
2409
2887
|
const anim = Animated.timing(selectT, {
|
|
2410
2888
|
toValue: isSelected ? 1 : 0,
|
|
2411
2889
|
duration: scaledMs(stateChangeDuration(reduced, SELECT_FILL_MS)),
|
|
2412
|
-
easing:
|
|
2890
|
+
easing: easeWire6,
|
|
2413
2891
|
useNativeDriver: true
|
|
2414
2892
|
});
|
|
2415
2893
|
anim.start();
|
|
@@ -2449,7 +2927,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
|
|
|
2449
2927
|
Animated.View,
|
|
2450
2928
|
{
|
|
2451
2929
|
style: [
|
|
2452
|
-
|
|
2930
|
+
styles13.option,
|
|
2453
2931
|
{
|
|
2454
2932
|
padding: t.spacing.md,
|
|
2455
2933
|
borderRadius: t.radius.md,
|
|
@@ -2466,7 +2944,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
|
|
|
2466
2944
|
pointerEvents: "none",
|
|
2467
2945
|
style: [
|
|
2468
2946
|
StyleSheet.absoluteFill,
|
|
2469
|
-
|
|
2947
|
+
styles13.selectedOverlay,
|
|
2470
2948
|
{
|
|
2471
2949
|
borderRadius: t.radius.md,
|
|
2472
2950
|
borderColor: t.colors.primary,
|
|
@@ -2480,7 +2958,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
|
|
|
2480
2958
|
View,
|
|
2481
2959
|
{
|
|
2482
2960
|
style: [
|
|
2483
|
-
|
|
2961
|
+
styles13.indicator,
|
|
2484
2962
|
{ borderRadius: t.radius.full },
|
|
2485
2963
|
isSelected ? { borderColor: t.colors.primary, backgroundColor: t.colors.primary } : { borderColor: t.colors.borderStrong, backgroundColor: "transparent" }
|
|
2486
2964
|
],
|
|
@@ -2488,7 +2966,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
|
|
|
2488
2966
|
Animated.Text,
|
|
2489
2967
|
{
|
|
2490
2968
|
style: [
|
|
2491
|
-
|
|
2969
|
+
styles13.check,
|
|
2492
2970
|
{ color: t.colors.onPrimary, opacity: checkT, transform: [{ scale: checkScale }] }
|
|
2493
2971
|
],
|
|
2494
2972
|
children: "\u2713"
|
|
@@ -2504,7 +2982,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
|
|
|
2504
2982
|
}
|
|
2505
2983
|
);
|
|
2506
2984
|
};
|
|
2507
|
-
var OptionRow =
|
|
2985
|
+
var OptionRow = React19.memo(_OptionRow);
|
|
2508
2986
|
var _SelectionCard = ({
|
|
2509
2987
|
title,
|
|
2510
2988
|
options,
|
|
@@ -2514,7 +2992,7 @@ var _SelectionCard = ({
|
|
|
2514
2992
|
onSelectionChange
|
|
2515
2993
|
}) => {
|
|
2516
2994
|
const t = useOnboardingTheme();
|
|
2517
|
-
const opts =
|
|
2995
|
+
const opts = React19.useMemo(() => normalizeOptions(options), [options]);
|
|
2518
2996
|
const [selected, setSelected] = useState([]);
|
|
2519
2997
|
const [submitted, setSubmitted] = useState(false);
|
|
2520
2998
|
useEffect(() => {
|
|
@@ -2565,18 +3043,18 @@ var _SelectionCard = ({
|
|
|
2565
3043
|
var SelectionCard = {
|
|
2566
3044
|
name: "SelectionCard",
|
|
2567
3045
|
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).",
|
|
2568
|
-
component:
|
|
2569
|
-
propsSchema:
|
|
3046
|
+
component: React19.memo(_SelectionCard),
|
|
3047
|
+
propsSchema: schema4,
|
|
2570
3048
|
defaultProps: { submitLabel: "Continue", multiSelect: false }
|
|
2571
3049
|
};
|
|
2572
|
-
var
|
|
3050
|
+
var styles13 = StyleSheet.create({
|
|
2573
3051
|
option: { borderWidth: 1.5, flexDirection: "row", alignItems: "center", overflow: "hidden" },
|
|
2574
3052
|
selectedOverlay: { borderWidth: 1.5 },
|
|
2575
3053
|
indicator: { width: 24, height: 24, borderWidth: 2, alignItems: "center", justifyContent: "center" },
|
|
2576
3054
|
check: { fontSize: 13, fontWeight: "800", lineHeight: 15 }
|
|
2577
3055
|
});
|
|
2578
|
-
var
|
|
2579
|
-
var
|
|
3056
|
+
var easeWire7 = Easing.bezier(...WIRE_BEZIER);
|
|
3057
|
+
var schema5 = z.object({
|
|
2580
3058
|
title: z.string().describe("Question or prompt for the user, e.g. 'What brings you here?'"),
|
|
2581
3059
|
options: optionsField(
|
|
2582
3060
|
"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."
|
|
@@ -2608,7 +3086,7 @@ var _OptionCard = ({ opt, isSelected, multiSelect, onToggle }) => {
|
|
|
2608
3086
|
const anim = Animated.timing(selectT, {
|
|
2609
3087
|
toValue: isSelected ? 1 : 0,
|
|
2610
3088
|
duration: scaledMs(stateChangeDuration(reduced, SELECT_FILL_MS)),
|
|
2611
|
-
easing:
|
|
3089
|
+
easing: easeWire7,
|
|
2612
3090
|
useNativeDriver: true
|
|
2613
3091
|
});
|
|
2614
3092
|
anim.start();
|
|
@@ -2623,12 +3101,12 @@ var _OptionCard = ({ opt, isSelected, multiSelect, onToggle }) => {
|
|
|
2623
3101
|
accessibilityRole: multiSelect ? "checkbox" : "radio",
|
|
2624
3102
|
accessibilityState: multiSelect ? { checked: isSelected } : { selected: isSelected },
|
|
2625
3103
|
accessibilityLabel: opt.label,
|
|
2626
|
-
style:
|
|
3104
|
+
style: styles14.cell,
|
|
2627
3105
|
children: /* @__PURE__ */ jsxs(
|
|
2628
3106
|
Animated.View,
|
|
2629
3107
|
{
|
|
2630
3108
|
style: [
|
|
2631
|
-
|
|
3109
|
+
styles14.card,
|
|
2632
3110
|
{
|
|
2633
3111
|
padding: t.spacing.md,
|
|
2634
3112
|
borderRadius: t.radius.lg,
|
|
@@ -2645,7 +3123,7 @@ var _OptionCard = ({ opt, isSelected, multiSelect, onToggle }) => {
|
|
|
2645
3123
|
pointerEvents: "none",
|
|
2646
3124
|
style: [
|
|
2647
3125
|
StyleSheet.absoluteFill,
|
|
2648
|
-
|
|
3126
|
+
styles14.selectedOverlay,
|
|
2649
3127
|
{
|
|
2650
3128
|
borderRadius: t.radius.lg,
|
|
2651
3129
|
borderColor: t.colors.primary,
|
|
@@ -2663,7 +3141,7 @@ var _OptionCard = ({ opt, isSelected, multiSelect, onToggle }) => {
|
|
|
2663
3141
|
}
|
|
2664
3142
|
);
|
|
2665
3143
|
};
|
|
2666
|
-
var OptionCard =
|
|
3144
|
+
var OptionCard = React19.memo(_OptionCard);
|
|
2667
3145
|
var _CardGridSelectCard = ({
|
|
2668
3146
|
title,
|
|
2669
3147
|
options,
|
|
@@ -2673,7 +3151,7 @@ var _CardGridSelectCard = ({
|
|
|
2673
3151
|
onSelectionChange
|
|
2674
3152
|
}) => {
|
|
2675
3153
|
const t = useOnboardingTheme();
|
|
2676
|
-
const opts =
|
|
3154
|
+
const opts = React19.useMemo(() => normalizeOptions(options), [options]);
|
|
2677
3155
|
const [selected, setSelected] = useState([]);
|
|
2678
3156
|
const [submitted, setSubmitted] = useState(false);
|
|
2679
3157
|
useEffect(() => {
|
|
@@ -2710,7 +3188,7 @@ var _CardGridSelectCard = ({
|
|
|
2710
3188
|
disabled: selected.length === 0 || submitted
|
|
2711
3189
|
}
|
|
2712
3190
|
),
|
|
2713
|
-
children: /* @__PURE__ */ jsx(View, { style: [
|
|
3191
|
+
children: /* @__PURE__ */ jsx(View, { style: [styles14.grid, { gap: t.spacing.sm }], children: opts.map((opt) => /* @__PURE__ */ jsx(
|
|
2714
3192
|
OptionCard,
|
|
2715
3193
|
{
|
|
2716
3194
|
opt,
|
|
@@ -2726,19 +3204,19 @@ var _CardGridSelectCard = ({
|
|
|
2726
3204
|
var CardGridSelectCard = {
|
|
2727
3205
|
name: "CardGridSelectCard",
|
|
2728
3206
|
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.",
|
|
2729
|
-
component:
|
|
2730
|
-
propsSchema:
|
|
3207
|
+
component: React19.memo(_CardGridSelectCard),
|
|
3208
|
+
propsSchema: schema5,
|
|
2731
3209
|
defaultProps: { submitLabel: "Continue", multiSelect: true }
|
|
2732
3210
|
};
|
|
2733
|
-
var
|
|
3211
|
+
var styles14 = StyleSheet.create({
|
|
2734
3212
|
grid: { flexDirection: "row", flexWrap: "wrap" },
|
|
2735
3213
|
// Two per row: ~47% basis leaves room for the gap, flexGrow fills the remainder exactly.
|
|
2736
3214
|
cell: { flexBasis: "47%", flexGrow: 1 },
|
|
2737
3215
|
card: { borderWidth: 1.5, alignItems: "flex-start", overflow: "hidden" },
|
|
2738
3216
|
selectedOverlay: { borderWidth: 1.5 }
|
|
2739
3217
|
});
|
|
2740
|
-
var
|
|
2741
|
-
var
|
|
3218
|
+
var easeWire8 = Easing.bezier(...WIRE_BEZIER);
|
|
3219
|
+
var schema6 = z.object({
|
|
2742
3220
|
status: z.enum(["success", "warning", "error", "info"]).describe("Status type"),
|
|
2743
3221
|
title: z.string().describe("Status title"),
|
|
2744
3222
|
message: z.string().optional().describe("Supporting message"),
|
|
@@ -2788,7 +3266,7 @@ var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSu
|
|
|
2788
3266
|
toValue: 1,
|
|
2789
3267
|
duration: scaledMs(STATUS_COPY_MS),
|
|
2790
3268
|
delay: scaledMs(STATUS_COPY_STAGGER_MS),
|
|
2791
|
-
easing:
|
|
3269
|
+
easing: easeWire8,
|
|
2792
3270
|
useNativeDriver: true
|
|
2793
3271
|
})
|
|
2794
3272
|
];
|
|
@@ -2810,8 +3288,8 @@ var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSu
|
|
|
2810
3288
|
{
|
|
2811
3289
|
align: "center",
|
|
2812
3290
|
footer: ctaLabel ? /* @__PURE__ */ jsx(Button, { title: ctaLabel, onPress: handleContinue, variant: "primary", disabled: submitted }) : void 0,
|
|
2813
|
-
children: /* @__PURE__ */ jsxs(View, { style: [
|
|
2814
|
-
/* @__PURE__ */ jsxs(View, { style: [
|
|
3291
|
+
children: /* @__PURE__ */ jsxs(View, { style: [styles15.center, { gap: t.spacing.sm }], children: [
|
|
3292
|
+
/* @__PURE__ */ jsxs(View, { style: [styles15.titleRow, { gap: t.spacing.sm }], children: [
|
|
2815
3293
|
/* @__PURE__ */ jsx(
|
|
2816
3294
|
Animated.Text,
|
|
2817
3295
|
{
|
|
@@ -2855,14 +3333,14 @@ var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSu
|
|
|
2855
3333
|
var StatusCard = {
|
|
2856
3334
|
name: "StatusCard",
|
|
2857
3335
|
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.",
|
|
2858
|
-
component:
|
|
2859
|
-
propsSchema:
|
|
3336
|
+
component: React19.memo(_StatusCard),
|
|
3337
|
+
propsSchema: schema6
|
|
2860
3338
|
};
|
|
2861
|
-
var
|
|
3339
|
+
var styles15 = StyleSheet.create({
|
|
2862
3340
|
center: { width: "100%", alignItems: "center", justifyContent: "center" },
|
|
2863
3341
|
titleRow: { flexDirection: "row", alignItems: "center", justifyContent: "center" }
|
|
2864
3342
|
});
|
|
2865
|
-
var
|
|
3343
|
+
var schema7 = z.object({
|
|
2866
3344
|
label: z.string().describe("What is being counted, e.g. 'Number of days' or 'Travellers'"),
|
|
2867
3345
|
min: z.number().optional().describe("Minimum value, default 1"),
|
|
2868
3346
|
max: z.number().optional().describe("Maximum value, default 30"),
|
|
@@ -2883,7 +3361,7 @@ var _NumberStepperCard = ({
|
|
|
2883
3361
|
onSubmit
|
|
2884
3362
|
}) => {
|
|
2885
3363
|
const t = useOnboardingTheme();
|
|
2886
|
-
const
|
|
3364
|
+
const styles18 = useMemo(() => makeStyles4(t), [t]);
|
|
2887
3365
|
const safeMin = safeNum(min, 1);
|
|
2888
3366
|
const safeMaxRaw = safeNum(max, 30);
|
|
2889
3367
|
const safeMax = safeMaxRaw < safeMin ? safeMin : safeMaxRaw;
|
|
@@ -2907,7 +3385,7 @@ var _NumberStepperCard = ({
|
|
|
2907
3385
|
const atMin = value <= safeMin;
|
|
2908
3386
|
const atMax = value >= safeMax;
|
|
2909
3387
|
const stepBtnStyle = (disabled) => [
|
|
2910
|
-
|
|
3388
|
+
styles18.stepBtn,
|
|
2911
3389
|
{ borderRadius: t.radius.full, backgroundColor: t.colors.background, borderColor: disabled ? t.colors.border : t.colors.primary }
|
|
2912
3390
|
];
|
|
2913
3391
|
return /* @__PURE__ */ jsx(
|
|
@@ -2916,10 +3394,10 @@ var _NumberStepperCard = ({
|
|
|
2916
3394
|
title: label,
|
|
2917
3395
|
align: "center",
|
|
2918
3396
|
footer: /* @__PURE__ */ jsx(Button, { title: submitLabel, onPress: handleSubmit, variant: "primary", disabled: submitted }),
|
|
2919
|
-
children: /* @__PURE__ */ jsxs(View, { style:
|
|
2920
|
-
/* @__PURE__ */ jsxs(View, { style: [
|
|
3397
|
+
children: /* @__PURE__ */ jsxs(View, { style: styles18.wrap, children: [
|
|
3398
|
+
/* @__PURE__ */ jsxs(View, { style: [styles18.stepper, { gap: t.spacing.lg }], children: [
|
|
2921
3399
|
/* @__PURE__ */ jsx(Pressable, { onPress: atMin || submitted ? void 0 : handleDecrement, disabled: atMin || submitted, style: stepBtnStyle(atMin), children: /* @__PURE__ */ jsx(Text, { style: [headingStyle(t.fonts), { color: atMin ? t.colors.textMuted : t.colors.primary }], children: "\u2212" }) }),
|
|
2922
|
-
/* @__PURE__ */ jsxs(View, { style:
|
|
3400
|
+
/* @__PURE__ */ jsxs(View, { style: styles18.valueBox, children: [
|
|
2923
3401
|
/* @__PURE__ */ jsx(Text, { style: [headingStyle(t.fonts), { color: t.colors.primary }], children: String(value) }),
|
|
2924
3402
|
unit ? /* @__PURE__ */ jsx(Text, { style: [captionStyle(t.fonts), { color: t.colors.textMuted }], children: unit }) : null
|
|
2925
3403
|
] }),
|
|
@@ -2933,8 +3411,8 @@ var _NumberStepperCard = ({
|
|
|
2933
3411
|
var NumberStepperCard = {
|
|
2934
3412
|
name: "NumberStepperCard",
|
|
2935
3413
|
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.",
|
|
2936
|
-
component:
|
|
2937
|
-
propsSchema:
|
|
3414
|
+
component: React19.memo(_NumberStepperCard),
|
|
3415
|
+
propsSchema: schema7,
|
|
2938
3416
|
defaultProps: { min: 1, max: 30, defaultValue: 1, step: 1, submitLabel: "Confirm" }
|
|
2939
3417
|
};
|
|
2940
3418
|
function makeStyles4(t) {
|
|
@@ -2945,9 +3423,9 @@ function makeStyles4(t) {
|
|
|
2945
3423
|
valueBox: { alignItems: "center", minWidth: 80 }
|
|
2946
3424
|
});
|
|
2947
3425
|
}
|
|
2948
|
-
var
|
|
2949
|
-
var
|
|
2950
|
-
var
|
|
3426
|
+
var easeWire9 = Easing.bezier(...WIRE_BEZIER);
|
|
3427
|
+
var RISE_PX2 = 10;
|
|
3428
|
+
var schema8 = z.object({
|
|
2951
3429
|
title: z.string().describe("Short, warm headline for the value/momentum moment"),
|
|
2952
3430
|
body: z.string().optional().describe("One or two supporting lines that reflect the user's answers back and build momentum"),
|
|
2953
3431
|
items: z.array(z.string()).max(4).optional().describe(
|
|
@@ -2984,7 +3462,7 @@ var _WorkItem = ({
|
|
|
2984
3462
|
toValue: 1,
|
|
2985
3463
|
duration: scaledMs(INTERSTITIAL_ITEM_MS),
|
|
2986
3464
|
delay: scaledMs(interstitialItemDelay(index)),
|
|
2987
|
-
easing:
|
|
3465
|
+
easing: easeWire9,
|
|
2988
3466
|
useNativeDriver: true
|
|
2989
3467
|
}),
|
|
2990
3468
|
Animated.spring(checkT, {
|
|
@@ -2998,7 +3476,7 @@ var _WorkItem = ({
|
|
|
2998
3476
|
anims.forEach((a) => a.start());
|
|
2999
3477
|
return () => anims.forEach((a) => a.stop());
|
|
3000
3478
|
}, [reduced, index, rowT, checkT]);
|
|
3001
|
-
const rise = rowT.interpolate({ inputRange: [0, 1], outputRange: [
|
|
3479
|
+
const rise = rowT.interpolate({ inputRange: [0, 1], outputRange: [RISE_PX2, 0] });
|
|
3002
3480
|
const checkScale = checkT.interpolate({
|
|
3003
3481
|
inputRange: [0, 1],
|
|
3004
3482
|
outputRange: [INTERSTITIAL_CHECK_SCALE_FROM, 1]
|
|
@@ -3006,20 +3484,20 @@ var _WorkItem = ({
|
|
|
3006
3484
|
return /* @__PURE__ */ jsxs(
|
|
3007
3485
|
Animated.View,
|
|
3008
3486
|
{
|
|
3009
|
-
style: [
|
|
3487
|
+
style: [styles16.itemRow, { gap: t.spacing.sm + t.spacing.xs, opacity: rowT, transform: [{ translateY: rise }] }],
|
|
3010
3488
|
children: [
|
|
3011
3489
|
/* @__PURE__ */ jsx(
|
|
3012
3490
|
View,
|
|
3013
3491
|
{
|
|
3014
3492
|
style: [
|
|
3015
|
-
|
|
3493
|
+
styles16.itemBadge,
|
|
3016
3494
|
{ backgroundColor: t.colors.primarySoft, borderRadius: t.radius.full }
|
|
3017
3495
|
],
|
|
3018
3496
|
children: /* @__PURE__ */ jsx(
|
|
3019
3497
|
Animated.Text,
|
|
3020
3498
|
{
|
|
3021
3499
|
style: [
|
|
3022
|
-
|
|
3500
|
+
styles16.itemCheck,
|
|
3023
3501
|
{ color: t.colors.primary, opacity: checkT, transform: [{ scale: checkScale }] }
|
|
3024
3502
|
],
|
|
3025
3503
|
children: "\u2713"
|
|
@@ -3027,12 +3505,12 @@ var _WorkItem = ({
|
|
|
3027
3505
|
)
|
|
3028
3506
|
}
|
|
3029
3507
|
),
|
|
3030
|
-
/* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts),
|
|
3508
|
+
/* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), styles16.itemText, { color: t.colors.text }], children: text })
|
|
3031
3509
|
]
|
|
3032
3510
|
}
|
|
3033
3511
|
);
|
|
3034
3512
|
};
|
|
3035
|
-
var WorkItem =
|
|
3513
|
+
var WorkItem = React19.memo(_WorkItem);
|
|
3036
3514
|
var _InterstitialCard = ({
|
|
3037
3515
|
title,
|
|
3038
3516
|
body,
|
|
@@ -3069,7 +3547,7 @@ var _InterstitialCard = ({
|
|
|
3069
3547
|
toValue: 1,
|
|
3070
3548
|
duration: scaledMs(INTERSTITIAL_HEAD_MS),
|
|
3071
3549
|
delay,
|
|
3072
|
-
easing:
|
|
3550
|
+
easing: easeWire9,
|
|
3073
3551
|
useNativeDriver: true
|
|
3074
3552
|
});
|
|
3075
3553
|
const anims = [
|
|
@@ -3082,7 +3560,7 @@ var _InterstitialCard = ({
|
|
|
3082
3560
|
}, [reduced, artT, titleT, bodyT]);
|
|
3083
3561
|
const riseStyle = (value) => ({
|
|
3084
3562
|
opacity: value,
|
|
3085
|
-
transform: [{ translateY: value.interpolate({ inputRange: [0, 1], outputRange: [
|
|
3563
|
+
transform: [{ translateY: value.interpolate({ inputRange: [0, 1], outputRange: [RISE_PX2, 0] }) }]
|
|
3086
3564
|
});
|
|
3087
3565
|
const handleContinue = useCallback(() => {
|
|
3088
3566
|
if (submitted || !canAdvance) return;
|
|
@@ -3105,18 +3583,18 @@ var _InterstitialCard = ({
|
|
|
3105
3583
|
disabled: submitted || !canAdvance
|
|
3106
3584
|
}
|
|
3107
3585
|
),
|
|
3108
|
-
children: /* @__PURE__ */ jsxs(View, { style: [
|
|
3109
|
-
/* @__PURE__ */ jsx(Animated.View, { style: [
|
|
3586
|
+
children: /* @__PURE__ */ jsxs(View, { style: [styles16.center, { gap: t.spacing.md }], children: [
|
|
3587
|
+
/* @__PURE__ */ jsx(Animated.View, { style: [styles16.fullWidth, riseStyle(artT)], children: showImage ? /* @__PURE__ */ jsx(
|
|
3110
3588
|
Image,
|
|
3111
3589
|
{
|
|
3112
3590
|
source: { uri: imageUrl },
|
|
3113
|
-
style:
|
|
3591
|
+
style: styles16.image,
|
|
3114
3592
|
resizeMode: "contain",
|
|
3115
3593
|
onError: () => setImgFailed(true)
|
|
3116
3594
|
}
|
|
3117
|
-
) : art ? /* @__PURE__ */ jsx(View, { style:
|
|
3595
|
+
) : art ? /* @__PURE__ */ jsx(View, { style: styles16.art, children: art }) : null }),
|
|
3118
3596
|
/* @__PURE__ */ jsx(Animated.View, { style: riseStyle(titleT), children: /* @__PURE__ */ jsx(Text, { style: [headingStyle(t.fonts), { color: t.colors.text, textAlign: "center" }], children: title }) }),
|
|
3119
|
-
(items == null ? void 0 : items.length) ? /* @__PURE__ */ jsx(View, { style: [
|
|
3597
|
+
(items == null ? void 0 : items.length) ? /* @__PURE__ */ jsx(View, { style: [styles16.items, { gap: t.spacing.md, marginTop: t.spacing.sm }], children: items.map((item, i) => /* @__PURE__ */ jsx(WorkItem, { text: item, index: i, reduced }, `${item}-${i}`)) }) : body ? /* @__PURE__ */ jsx(Animated.View, { style: riseStyle(bodyT), children: /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted, textAlign: "center" }], children: body }) }) : null
|
|
3120
3598
|
] })
|
|
3121
3599
|
}
|
|
3122
3600
|
);
|
|
@@ -3124,11 +3602,11 @@ var _InterstitialCard = ({
|
|
|
3124
3602
|
var InterstitialCard = {
|
|
3125
3603
|
name: "InterstitialCard",
|
|
3126
3604
|
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.",
|
|
3127
|
-
component:
|
|
3128
|
-
propsSchema:
|
|
3605
|
+
component: React19.memo(_InterstitialCard),
|
|
3606
|
+
propsSchema: schema8,
|
|
3129
3607
|
defaultProps: { ctaLabel: "Continue", illustrationFallback: "momentum" }
|
|
3130
3608
|
};
|
|
3131
|
-
var
|
|
3609
|
+
var styles16 = StyleSheet.create({
|
|
3132
3610
|
center: { width: "100%", alignItems: "center", justifyContent: "center" },
|
|
3133
3611
|
fullWidth: { width: "100%", alignItems: "center" },
|
|
3134
3612
|
art: { alignItems: "center", justifyContent: "center" },
|
|
@@ -3448,15 +3926,6 @@ var resetAutoDeviceKeys = () => {
|
|
|
3448
3926
|
(_a2 = registry.pending) == null ? void 0 : _a2.clear();
|
|
3449
3927
|
};
|
|
3450
3928
|
|
|
3451
|
-
// src/utils/warnInDev.ts
|
|
3452
|
-
var warnInDev = (message) => {
|
|
3453
|
-
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
3454
|
-
console.warn(message);
|
|
3455
|
-
return true;
|
|
3456
|
-
}
|
|
3457
|
-
return false;
|
|
3458
|
-
};
|
|
3459
|
-
|
|
3460
3929
|
// src/analytics/currentSession.ts
|
|
3461
3930
|
var CURRENT_SESSION_ID_SLOT = /* @__PURE__ */ Symbol.for(
|
|
3462
3931
|
"@wireai/activation:currentSessionId"
|
|
@@ -3661,6 +4130,43 @@ var resolveUserContext = (ctx = {}, opts = {}) => {
|
|
|
3661
4130
|
if (Object.keys(bucket).length > 0) result.userContext = bucket;
|
|
3662
4131
|
return result;
|
|
3663
4132
|
};
|
|
4133
|
+
|
|
4134
|
+
// src/permissions/permissionMemory.ts
|
|
4135
|
+
var permissionStorageKey = (appId) => `wireai:permissions:${appId}`;
|
|
4136
|
+
var readSettledPermissions = (raw, sessionId) => {
|
|
4137
|
+
if (!raw || !sessionId) return [];
|
|
4138
|
+
try {
|
|
4139
|
+
const parsed = JSON.parse(raw);
|
|
4140
|
+
if (typeof parsed !== "object" || parsed === null) return [];
|
|
4141
|
+
const record = parsed;
|
|
4142
|
+
if (record.sessionId !== sessionId || !Array.isArray(record.ids)) return [];
|
|
4143
|
+
return record.ids.filter((id) => typeof id === "string" && id.length > 0);
|
|
4144
|
+
} catch {
|
|
4145
|
+
return [];
|
|
4146
|
+
}
|
|
4147
|
+
};
|
|
4148
|
+
var loadSettledPermissions = async (storage, key, sessionId) => {
|
|
4149
|
+
try {
|
|
4150
|
+
return readSettledPermissions(await withTimeout(storage.getItem(key), READ_TIMEOUT_MS), sessionId);
|
|
4151
|
+
} catch {
|
|
4152
|
+
return [];
|
|
4153
|
+
}
|
|
4154
|
+
};
|
|
4155
|
+
var saveSettledPermissions = (storage, key, sessionId, ids) => {
|
|
4156
|
+
try {
|
|
4157
|
+
const record = { sessionId, ids: [...ids] };
|
|
4158
|
+
void storage.setItem(key, JSON.stringify(record)).catch(() => {
|
|
4159
|
+
});
|
|
4160
|
+
} catch {
|
|
4161
|
+
}
|
|
4162
|
+
};
|
|
4163
|
+
var clearSettledPermissions = (storage, key) => {
|
|
4164
|
+
try {
|
|
4165
|
+
void storage.removeItem(key).catch(() => {
|
|
4166
|
+
});
|
|
4167
|
+
} catch {
|
|
4168
|
+
}
|
|
4169
|
+
};
|
|
3664
4170
|
var AUTO_JOIN_HYDRATION_TIMEOUT_MS = 1500;
|
|
3665
4171
|
var WireOnboarding = ({
|
|
3666
4172
|
config,
|
|
@@ -3685,9 +4191,10 @@ var WireOnboarding = ({
|
|
|
3685
4191
|
retainSessionOnComplete,
|
|
3686
4192
|
userContext,
|
|
3687
4193
|
userId,
|
|
3688
|
-
autoJoinKey = true
|
|
4194
|
+
autoJoinKey = true,
|
|
4195
|
+
permissionScreens
|
|
3689
4196
|
}) => {
|
|
3690
|
-
var _a2, _b, _c, _d, _e;
|
|
4197
|
+
var _a2, _b, _c, _d, _e, _f;
|
|
3691
4198
|
const boundUserId = useMemo(() => sanitizeUserId(userId), [userId]);
|
|
3692
4199
|
const device = useMemo(() => {
|
|
3693
4200
|
const collected = collectDeviceContext();
|
|
@@ -3775,6 +4282,7 @@ var WireOnboarding = ({
|
|
|
3775
4282
|
() => storage ? null : { id: makeSessionId(), resumed: false }
|
|
3776
4283
|
);
|
|
3777
4284
|
const storageKey = persistKey != null ? persistKey : sessionStorageKey(config.appId);
|
|
4285
|
+
const permissionsKey = permissionStorageKey(config.appId);
|
|
3778
4286
|
useEffect(() => {
|
|
3779
4287
|
if (!storage || session) return;
|
|
3780
4288
|
let cancelled = false;
|
|
@@ -3787,12 +4295,43 @@ var WireOnboarding = ({
|
|
|
3787
4295
|
}, [storage, storageKey, sessionTtlMs, session]);
|
|
3788
4296
|
const handleComplete = useCallback(
|
|
3789
4297
|
(result) => {
|
|
3790
|
-
if (storage && shouldClearOnComplete(retainSessionOnComplete))
|
|
4298
|
+
if (storage && shouldClearOnComplete(retainSessionOnComplete)) {
|
|
4299
|
+
clearPersistedSession(storage, storageKey);
|
|
4300
|
+
clearSettledPermissions(storage, permissionsKey);
|
|
4301
|
+
}
|
|
3791
4302
|
onComplete(result);
|
|
3792
4303
|
},
|
|
3793
|
-
[storage, storageKey, retainSessionOnComplete, onComplete]
|
|
4304
|
+
[storage, storageKey, permissionsKey, retainSessionOnComplete, onComplete]
|
|
3794
4305
|
);
|
|
3795
4306
|
const sessionId = (_c = session == null ? void 0 : session.id) != null ? _c : "";
|
|
4307
|
+
const wantsPermissionMemory = Boolean(storage) && ((_d = permissionScreens == null ? void 0 : permissionScreens.length) != null ? _d : 0) > 0;
|
|
4308
|
+
const [settledPermissions, setSettledPermissions] = useState(void 0);
|
|
4309
|
+
useEffect(() => {
|
|
4310
|
+
if (!wantsPermissionMemory || !storage || !sessionId || settledPermissions !== void 0) return;
|
|
4311
|
+
let cancelled = false;
|
|
4312
|
+
void loadSettledPermissions(storage, permissionsKey, sessionId).then((ids) => {
|
|
4313
|
+
if (!cancelled) setSettledPermissions(ids);
|
|
4314
|
+
});
|
|
4315
|
+
return () => {
|
|
4316
|
+
cancelled = true;
|
|
4317
|
+
};
|
|
4318
|
+
}, [wantsPermissionMemory, storage, permissionsKey, sessionId, settledPermissions]);
|
|
4319
|
+
const settledPermissionsRef = useRef(void 0);
|
|
4320
|
+
settledPermissionsRef.current = settledPermissions;
|
|
4321
|
+
const handlePermissionSettled = useCallback(
|
|
4322
|
+
(id) => {
|
|
4323
|
+
var _a3;
|
|
4324
|
+
const current = (_a3 = settledPermissionsRef.current) != null ? _a3 : [];
|
|
4325
|
+
if (current.includes(id)) return;
|
|
4326
|
+
const next = [...current, id];
|
|
4327
|
+
settledPermissionsRef.current = next;
|
|
4328
|
+
setSettledPermissions(next);
|
|
4329
|
+
if (storage && wantsPermissionMemory && sessionId) {
|
|
4330
|
+
saveSettledPermissions(storage, permissionsKey, sessionId, next);
|
|
4331
|
+
}
|
|
4332
|
+
},
|
|
4333
|
+
[storage, wantsPermissionMemory, permissionsKey, sessionId]
|
|
4334
|
+
);
|
|
3796
4335
|
const startupUserIdRef = useRef(boundUserId);
|
|
3797
4336
|
const metadataKey = config.metadata ? JSON.stringify(config.metadata) : "";
|
|
3798
4337
|
const metadataStable = useMemo(() => metadataKey ? JSON.parse(metadataKey) : void 0, [metadataKey]);
|
|
@@ -3853,8 +4392,8 @@ var WireOnboarding = ({
|
|
|
3853
4392
|
return /* @__PURE__ */ jsx(OnboardingThemeProvider, { theme, children: /* @__PURE__ */ jsx(
|
|
3854
4393
|
LoadingScreen,
|
|
3855
4394
|
{
|
|
3856
|
-
title: (
|
|
3857
|
-
hint: (
|
|
4395
|
+
title: (_e = copy == null ? void 0 : copy.restoringTitle) != null ? _e : DEFAULT_COPY.restoringTitle,
|
|
4396
|
+
hint: (_f = copy == null ? void 0 : copy.restoringHint) != null ? _f : DEFAULT_COPY.restoringHint
|
|
3858
4397
|
}
|
|
3859
4398
|
) });
|
|
3860
4399
|
}
|
|
@@ -3875,7 +4414,11 @@ var WireOnboarding = ({
|
|
|
3875
4414
|
reportTarget,
|
|
3876
4415
|
sessionId,
|
|
3877
4416
|
resumed: session.resumed,
|
|
3878
|
-
clientContext
|
|
4417
|
+
clientContext,
|
|
4418
|
+
permissionScreens,
|
|
4419
|
+
settledPermissions,
|
|
4420
|
+
permissionsPending: wantsPermissionMemory && settledPermissions === void 0,
|
|
4421
|
+
onPermissionSettled: handlePermissionSettled
|
|
3879
4422
|
}
|
|
3880
4423
|
) }) }) }) });
|
|
3881
4424
|
};
|
|
@@ -3892,7 +4435,7 @@ function DemoOnboarding({
|
|
|
3892
4435
|
const [open, setOpen] = useState(false);
|
|
3893
4436
|
const [runId, setRunId] = useState(0);
|
|
3894
4437
|
const t = useMemo(() => mergeTheme(theme != null ? theme : {}), [theme]);
|
|
3895
|
-
const
|
|
4438
|
+
const styles18 = useMemo(() => makeStyles5(t), [t]);
|
|
3896
4439
|
const openDemo = useCallback(() => {
|
|
3897
4440
|
setRunId((n) => n + 1);
|
|
3898
4441
|
setOpen(true);
|
|
@@ -3906,7 +4449,7 @@ function DemoOnboarding({
|
|
|
3906
4449
|
[onComplete]
|
|
3907
4450
|
);
|
|
3908
4451
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3909
|
-
renderTrigger ? renderTrigger(openDemo) : /* @__PURE__ */ jsx(Pressable, { onPress: openDemo, style:
|
|
4452
|
+
renderTrigger ? renderTrigger(openDemo) : /* @__PURE__ */ jsx(Pressable, { onPress: openDemo, style: styles18.trigger, accessibilityRole: "button", children: /* @__PURE__ */ jsx(Text, { style: styles18.triggerText, children: label }) }),
|
|
3910
4453
|
/* @__PURE__ */ jsx(
|
|
3911
4454
|
Modal,
|
|
3912
4455
|
{
|
|
@@ -3914,7 +4457,7 @@ function DemoOnboarding({
|
|
|
3914
4457
|
animationType: "slide",
|
|
3915
4458
|
presentationStyle: "fullScreen",
|
|
3916
4459
|
onRequestClose: close,
|
|
3917
|
-
children: /* @__PURE__ */ jsx(View, { style:
|
|
4460
|
+
children: /* @__PURE__ */ jsx(View, { style: styles18.modal, children: config ? /* @__PURE__ */ jsx(
|
|
3918
4461
|
WireOnboarding,
|
|
3919
4462
|
{
|
|
3920
4463
|
config,
|
|
@@ -3927,10 +4470,10 @@ function DemoOnboarding({
|
|
|
3927
4470
|
onError: close
|
|
3928
4471
|
},
|
|
3929
4472
|
runId
|
|
3930
|
-
) : /* @__PURE__ */ jsxs(View, { style:
|
|
3931
|
-
/* @__PURE__ */ jsx(Text, { style:
|
|
3932
|
-
/* @__PURE__ */ jsx(Text, { style:
|
|
3933
|
-
/* @__PURE__ */ jsx(Pressable, { onPress: close, style:
|
|
4473
|
+
) : /* @__PURE__ */ jsxs(View, { style: styles18.notice, children: [
|
|
4474
|
+
/* @__PURE__ */ jsx(Text, { style: styles18.noticeTitle, children: "Wire AI not configured" }),
|
|
4475
|
+
/* @__PURE__ */ jsx(Text, { style: styles18.noticeBody, children: "Set the Wire AI key + server URL (e.g. via wireConfigFromEnv) to demo the AI onboarding here." }),
|
|
4476
|
+
/* @__PURE__ */ jsx(Pressable, { onPress: close, style: styles18.trigger, accessibilityRole: "button", children: /* @__PURE__ */ jsx(Text, { style: styles18.triggerText, children: "Close" }) })
|
|
3934
4477
|
] }) })
|
|
3935
4478
|
}
|
|
3936
4479
|
)
|
|
@@ -4080,25 +4623,25 @@ var _CenteredModal = forwardRef(
|
|
|
4080
4623
|
/* @__PURE__ */ jsx(
|
|
4081
4624
|
Animated.View,
|
|
4082
4625
|
{
|
|
4083
|
-
style: [
|
|
4626
|
+
style: [styles17.backdrop, { opacity: backdropOpacity }],
|
|
4084
4627
|
pointerEvents: "none"
|
|
4085
4628
|
}
|
|
4086
4629
|
),
|
|
4087
4630
|
/* @__PURE__ */ jsx(
|
|
4088
4631
|
Pressable,
|
|
4089
4632
|
{
|
|
4090
|
-
style:
|
|
4633
|
+
style: styles17.fill,
|
|
4091
4634
|
onPress: onBackdrop,
|
|
4092
4635
|
accessibilityRole: "button",
|
|
4093
4636
|
accessibilityLabel: "Dismiss",
|
|
4094
|
-
children: /* @__PURE__ */ jsx(SafeAreaView, { style:
|
|
4095
|
-
}, style:
|
|
4637
|
+
children: /* @__PURE__ */ jsx(SafeAreaView, { style: styles17.center, pointerEvents: "box-none", children: /* @__PURE__ */ jsx(Pressable, { onPress: () => {
|
|
4638
|
+
}, style: styles17.cardWrap, children: /* @__PURE__ */ jsx(
|
|
4096
4639
|
Animated.View,
|
|
4097
4640
|
{
|
|
4098
4641
|
accessibilityViewIsModal: true,
|
|
4099
4642
|
accessibilityRole: "alert",
|
|
4100
4643
|
style: [
|
|
4101
|
-
|
|
4644
|
+
styles17.card,
|
|
4102
4645
|
{
|
|
4103
4646
|
backgroundColor: theme.colors.surface,
|
|
4104
4647
|
borderRadius: theme.radius.lg,
|
|
@@ -4119,8 +4662,8 @@ var _CenteredModal = forwardRef(
|
|
|
4119
4662
|
}
|
|
4120
4663
|
);
|
|
4121
4664
|
_CenteredModal.displayName = "CenteredModal";
|
|
4122
|
-
var CenteredModal =
|
|
4123
|
-
var
|
|
4665
|
+
var CenteredModal = React19.memo(_CenteredModal);
|
|
4666
|
+
var styles17 = StyleSheet.create({
|
|
4124
4667
|
fill: { flex: 1 },
|
|
4125
4668
|
backdrop: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, backgroundColor: "#000" },
|
|
4126
4669
|
center: { flex: 1, alignItems: "center", justifyContent: "center", padding: 24 },
|
|
@@ -4371,6 +4914,11 @@ var toAnalyticsEvent = (event) => {
|
|
|
4371
4914
|
};
|
|
4372
4915
|
case "fallback":
|
|
4373
4916
|
return { name: WIRE_ONBOARDING_EVENTS.fallback, params: { reason: event.reason } };
|
|
4917
|
+
case "permission":
|
|
4918
|
+
return {
|
|
4919
|
+
name: permissionEventName(event.stage),
|
|
4920
|
+
params: permissionEventProps(event.permission, event.status)
|
|
4921
|
+
};
|
|
4374
4922
|
default: {
|
|
4375
4923
|
const _exhaustive = event;
|
|
4376
4924
|
return _exhaustive;
|
|
@@ -5198,6 +5746,6 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
5198
5746
|
}, []);
|
|
5199
5747
|
};
|
|
5200
5748
|
|
|
5201
|
-
export { AUTO_DEVICE_ID_PREFIX, AnimatedSparkle, BACKGROUND_SESSION_MS, CardGridSelectCard, CardHandoff, CenteredModal, ChipSelectCard, CompletionView, DEFAULT_FEATURES_TTL_MS, DEFAULT_SESSION_TTL_MS, DemoOnboarding, EXTRA_KEY_PREFIX, ErrorBlock, FIRST_OPEN_EVENT, IconRegistryProvider, IllustrationProvider, InterstitialCard, LoadingBlock, LoadingScreen, NumberStepperCard, Button as OnboardingButton, OnboardingFlow, OnboardingScaffold, OnboardingThemeProvider, PLAN_TIER_CONTEXT_KEY, SESSION_STARTED_EVENT, SelectionCard, StatusCard, StepProgress, TextInputCard, USER_ID_MAX_LENGTH, WIRE_ENV_VARS, WIRE_ICON_GLYPHS, WIRE_ICON_NAMES, WIRE_ONBOARDING_EVENTS, WIRE_PURCHASE_EVENTS, WireFeaturesProvider, WireIcon, WireOnboarding, activationJoinContext, activeEntitlement, analyticsUserIdStorageKey, attributionMetadata, bumpActivationRevalidation, clearPersistedSession, clearPiiFromContext, clearUserContext, collectDeviceContext, createRevenueCatBridge, createWireActivation, defaultIllustrations, defaultOnboardingTheme, defaultWireFeatures, deriveAnswers, describeEntitlement, describeFailure, describePackage, detectAppVersion, detectNativeModel, deviceIdStorageKey, ensureCurrentSessionId, featuresCacheKey, featuresEqual, fetchWireFeatures, firstOpenStorageKey, getActivationRevalidationVersion, getCurrentSessionId, hashEmailFnv1a, hostIdentity, hydrateAutoDeviceKey, hydrateDeviceIdentity, identifyOnboarding, isFeaturesFresh, isOnboardingEnabled, isUserCancelled, isWireScalar, loadPersistedSession, looksLikeEmail, lookupIconGlyph, makeSessionId, mergeTheme, mintDeviceId, motionSpec_exports as motionSpec, namespaceExtra, onboardingComponents, parseWireFeatures, peekPersistedSession, readCachedFeatures, readProgress, reportClientEvent, reportClientEventAwait, reportClientEvents, reportClientEventsAwait, reportFirstOpen, reportSessionStart, resetActivationRevalidation, resetAutoDeviceKeys, resetCurrentSessionId, resetFirstOpenLatch, resetIdentityProvenance, resetSessionStartGuard, resolveAutoDeviceKey, resolveIdentity, resolvePlanTier, resolveUserContext, sanitizeUserId, savePersistedSession, sessionStorageKey, setCurrentSessionId, subscribeActivationRevalidation, themeFromBrand, toAnalyticsEvent, useActivationRevalidation, useHostIcon, useIllustration, useLifecycleEvents, useOnboardingTheme, useReducedMotion, useResolvedFeatures, useSessionStart, useWireActivation, useWireFeatures, useWireFeaturesContext, wireConfigFromEnv, wireLifecycleEvents, writeCachedFeatures };
|
|
5749
|
+
export { AUTO_DEVICE_ID_PREFIX, AnimatedSparkle, BACKGROUND_SESSION_MS, CardGridSelectCard, CardHandoff, CenteredModal, ChipSelectCard, CompletionView, DEFAULT_FEATURES_TTL_MS, DEFAULT_PERMISSION_COPY, DEFAULT_PERMISSION_PLACEMENT, DEFAULT_SESSION_TTL_MS, DemoOnboarding, EXTRA_KEY_PREFIX, ErrorBlock, FIRST_OPEN_EVENT, GENERIC_PERMISSION_COPY, IconRegistryProvider, IllustrationProvider, InterstitialCard, LoadingBlock, LoadingScreen, NOTIFICATIONS_PERMISSION_COPY, NumberStepperCard, Button as OnboardingButton, OnboardingFlow, OnboardingScaffold, OnboardingThemeProvider, PERMISSION_CARD_NAME, PLAN_TIER_CONTEXT_KEY, PermissionCard, PermissionCardView, SESSION_STARTED_EVENT, SelectionCard, StatusCard, StepProgress, TextInputCard, USER_ID_MAX_LENGTH, WIRE_ENV_VARS, WIRE_ICON_GLYPHS, WIRE_ICON_NAMES, WIRE_ONBOARDING_EVENTS, WIRE_PERMISSION_EVENTS, WIRE_PURCHASE_EVENTS, WireFeaturesProvider, WireIcon, WireOnboarding, activationJoinContext, activeEntitlement, analyticsUserIdStorageKey, attributionMetadata, bumpActivationRevalidation, clearPersistedSession, clearPiiFromContext, clearSettledPermissions, clearUserContext, collectDeviceContext, createRevenueCatBridge, createWireActivation, defaultIllustrations, defaultOnboardingTheme, defaultWireFeatures, deriveAnswers, describeEntitlement, describeFailure, describePackage, detectAppVersion, detectNativeModel, deviceIdStorageKey, ensureCurrentSessionId, featuresCacheKey, featuresEqual, fetchWireFeatures, firstOpenStorageKey, getActivationRevalidationVersion, getCurrentSessionId, hashEmailFnv1a, hostIdentity, hydrateAutoDeviceKey, hydrateDeviceIdentity, identifyOnboarding, isFeaturesFresh, isOnboardingEnabled, isPermissionDue, isUserCancelled, isWireScalar, loadPersistedSession, loadSettledPermissions, looksLikeEmail, lookupIconGlyph, makeSessionId, mergeTheme, mintDeviceId, motionSpec_exports as motionSpec, namespaceExtra, normalizeAfterCard, normalizePermissionStatus, onboardingComponents, parseWireFeatures, peekPersistedSession, permissionEventName, permissionEventProps, permissionScreenId, permissionStorageKey, readCachedFeatures, readProgress, readSettledPermissions, reportClientEvent, reportClientEventAwait, reportClientEvents, reportClientEventsAwait, reportFirstOpen, reportSessionStart, resetActivationRevalidation, resetAutoDeviceKeys, resetCurrentSessionId, resetFirstOpenLatch, resetIdentityProvenance, resetSessionStartGuard, resolveAutoDeviceKey, resolveIdentity, resolvePermissionCopy, resolvePlanTier, resolveUserContext, sanitizeUserId, savePersistedSession, saveSettledPermissions, selectDuePermissionScreen, sessionStorageKey, setCurrentSessionId, subscribeActivationRevalidation, themeFromBrand, toAnalyticsEvent, useActivationRevalidation, useHostIcon, useIllustration, useLifecycleEvents, useOnboardingTheme, useReducedMotion, useResolvedFeatures, useSessionStart, useWireActivation, useWireFeatures, useWireFeaturesContext, wireConfigFromEnv, wireLifecycleEvents, writeCachedFeatures };
|
|
5202
5750
|
//# sourceMappingURL=index.mjs.map
|
|
5203
5751
|
//# sourceMappingURL=index.mjs.map
|