@wireai/activation 0.8.0 → 0.9.1
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 +29 -5
- package/CHANGELOG.md +202 -6
- package/README.md +105 -10
- package/dist/analytics/index.d.mts +6 -6
- package/dist/analytics/index.d.ts +6 -6
- package/dist/analytics/index.js +447 -49
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +447 -49
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/coachmarks/index.d.mts +2 -2
- package/dist/coachmarks/index.d.ts +2 -2
- package/dist/coachmarks/index.js.map +1 -1
- package/dist/coachmarks/index.mjs.map +1 -1
- package/dist/{currentSession-f7LWcdWG.d.ts → currentSession-61dcm3V-.d.ts} +10 -2
- package/dist/{currentSession-d9CrBxwe.d.mts → currentSession-Bs2JfTJ8.d.mts} +10 -2
- package/dist/{decision-BzbiKwk3.d.mts → decision-Bl_M2y3r.d.mts} +1 -1
- package/dist/{decision-plDEOCkt.d.ts → decision-Cau5KmP6.d.ts} +1 -1
- package/dist/index.d.mts +558 -15
- package/dist/index.d.ts +558 -15
- package/dist/index.js +1144 -375
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +969 -207
- package/dist/index.mjs.map +1 -1
- package/dist/questionnaire/index.d.mts +69 -23
- package/dist/questionnaire/index.d.ts +69 -23
- package/dist/questionnaire/index.js +643 -172
- package/dist/questionnaire/index.js.map +1 -1
- package/dist/questionnaire/index.mjs +608 -137
- package/dist/questionnaire/index.mjs.map +1 -1
- package/dist/reviews/index.d.mts +24 -12
- package/dist/reviews/index.d.ts +24 -12
- package/dist/reviews/index.js +507 -173
- package/dist/reviews/index.js.map +1 -1
- package/dist/reviews/index.mjs +462 -129
- package/dist/reviews/index.mjs.map +1 -1
- package/dist/showcase/index.d.mts +2 -2
- package/dist/showcase/index.d.ts +2 -2
- package/dist/showcase/index.js.map +1 -1
- package/dist/showcase/index.mjs.map +1 -1
- package/dist/transport-B31G0Cib.d.ts +128 -0
- package/dist/transport-Bzb-bcB2.d.mts +128 -0
- package/dist/{types-GL_hQ0TN.d.mts → types-BcmagF6K.d.mts} +1 -1
- package/dist/{types-GL_hQ0TN.d.ts → types-BcmagF6K.d.ts} +1 -1
- package/dist/{types-BhpXJGlg.d.ts → types-Buj9Lw9t.d.ts} +15 -3
- package/dist/{types-CMuOexw0.d.mts → types-CKFhyrMu.d.mts} +1 -1
- package/dist/{types-CMuOexw0.d.ts → types-CKFhyrMu.d.ts} +1 -1
- package/dist/{types-A6pTxIZV.d.mts → types-CNUqMK0D.d.mts} +15 -3
- package/metro/index.d.ts +18 -1
- package/metro/index.js +80 -0
- package/package.json +9 -1
- package/src/WireOnboarding.tsx +4 -0
- package/src/analytics/currentSession.ts +1 -1
- package/src/cards/CardGridSelectCard.tsx +253 -0
- package/src/cards/ChipSelectCard.tsx +1 -1
- package/src/cards/SelectionCard.tsx +15 -19
- package/src/cards/index.ts +13 -1
- package/src/cards/optionSchema.ts +65 -0
- package/src/coachmarks/types.ts +1 -1
- package/src/components/CardHandoff.tsx +39 -7
- package/src/components/DoneCheck.tsx +94 -0
- package/src/components/ModalPaneHost.tsx +141 -0
- package/src/components/StepDots.tsx +112 -0
- package/src/device/appVersion.ts +29 -8
- package/src/device/deviceModel.ts +21 -8
- package/src/features/cache.ts +1 -1
- package/src/haptics/expo-haptics.d.ts +31 -0
- package/src/haptics/haptics.ts +58 -0
- package/src/icons/IconRegistry.tsx +53 -0
- package/src/icons/WireIcon.tsx +85 -0
- package/src/icons/expoIcons.ts +165 -0
- package/src/icons/index.ts +18 -0
- package/src/icons/vocabulary.ts +154 -0
- package/src/index.ts +20 -0
- package/src/questionnaire/QuestionnaireGate.tsx +315 -164
- package/src/questionnaire/decision.ts +4 -2
- package/src/questionnaire/index.ts +3 -2
- package/src/questionnaire/steps.ts +261 -0
- package/src/questionnaire/types.ts +33 -12
- package/src/reviews/ReviewGate.tsx +250 -152
- package/src/reviews/index.ts +7 -3
- package/src/reviews/transport.ts +119 -3
- package/src/reviews/types.ts +13 -0
- package/src/session/persistedSession.ts +1 -1
- package/src/session-analytics/lifecycle.ts +1 -1
- package/src/session-analytics/useSessionStart.ts +2 -2
- package/src/showcase/FeatureShowcase.tsx +1 -1
- package/src/showcase/showcaseColors.ts +1 -1
- package/src/theme/mergeThemeOver.ts +27 -0
- package/src/types.ts +8 -0
- package/dist/transport-BeO_Brcu.d.mts +0 -40
- package/dist/transport-DLpd1v5_.d.ts +0 -40
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var React8 = require('react');
|
|
4
4
|
var reactNative = require('react-native');
|
|
5
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
6
|
var reactNativeSafeAreaContext = require('react-native-safe-area-context');
|
|
7
7
|
|
|
8
8
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
9
|
|
|
10
|
-
var
|
|
10
|
+
var React8__default = /*#__PURE__*/_interopDefault(React8);
|
|
11
11
|
|
|
12
12
|
// src/questionnaire/QuestionnaireGate.tsx
|
|
13
13
|
|
|
@@ -60,9 +60,9 @@ var defaultOnboardingTheme = {
|
|
|
60
60
|
var mergeTheme = (override) => {
|
|
61
61
|
return defaultOnboardingTheme;
|
|
62
62
|
};
|
|
63
|
-
var OnboardingThemeContext =
|
|
63
|
+
var OnboardingThemeContext = React8.createContext(null);
|
|
64
64
|
var useOnboardingTheme = () => {
|
|
65
|
-
const ctx =
|
|
65
|
+
const ctx = React8.useContext(OnboardingThemeContext);
|
|
66
66
|
return ctx != null ? ctx : mergeTheme();
|
|
67
67
|
};
|
|
68
68
|
|
|
@@ -94,16 +94,28 @@ var buttonLabelStyle = (f) => {
|
|
|
94
94
|
};
|
|
95
95
|
|
|
96
96
|
// src/motion/motionSpec.ts
|
|
97
|
+
var WIRE_BEZIER = [0.16, 1, 0.3, 1];
|
|
97
98
|
var REDUCED_FADE_MS = 120;
|
|
98
99
|
var BUTTON_PRESS_SCALE = 0.975;
|
|
99
100
|
var REDUCED_PRESS_OPACITY = 0.85;
|
|
100
101
|
var BUTTON_PRESS_SPRING = { friction: 8, tension: 240 };
|
|
102
|
+
var SELECT_FILL_MS = 180;
|
|
103
|
+
var CARD_ENTER_MS = 220;
|
|
104
|
+
var CARD_EXIT_MS = 180;
|
|
105
|
+
var CARD_EXIT_LEAD_MS = 80;
|
|
106
|
+
var CARD_SLIDE_PX = 26;
|
|
107
|
+
var CARD_ENTER_SCALE_FROM = 0.96;
|
|
108
|
+
var CARD_SPRING = { damping: 14, stiffness: 180, mass: 1 };
|
|
109
|
+
var LOADER_HANDOFF_FADE_MS = 120;
|
|
110
|
+
var STATUS_POP_SPRING = { friction: 6, tension: 200 };
|
|
111
|
+
var STATUS_POP_SCALE_FROM = 0.4;
|
|
101
112
|
var durationScale = 1;
|
|
102
113
|
var scaledMs = (ms) => Math.round(ms * durationScale);
|
|
114
|
+
var stateChangeDuration = (reduced, durationMs) => reduced ? 0 : durationMs;
|
|
103
115
|
var lastKnown = false;
|
|
104
116
|
var useReducedMotion = () => {
|
|
105
|
-
const [reduced, setReduced] =
|
|
106
|
-
|
|
117
|
+
const [reduced, setReduced] = React8.useState(lastKnown);
|
|
118
|
+
React8.useEffect(() => {
|
|
107
119
|
let mounted = true;
|
|
108
120
|
reactNative.AccessibilityInfo.isReduceMotionEnabled().then((value) => {
|
|
109
121
|
lastKnown = value;
|
|
@@ -131,8 +143,8 @@ var _Button = ({
|
|
|
131
143
|
const t = useOnboardingTheme();
|
|
132
144
|
const reduced = useReducedMotion();
|
|
133
145
|
const isPrimary = variant === "primary";
|
|
134
|
-
const pressT =
|
|
135
|
-
const pressTo =
|
|
146
|
+
const pressT = React8.useRef(new reactNative.Animated.Value(0)).current;
|
|
147
|
+
const pressTo = React8.useCallback(
|
|
136
148
|
(to) => {
|
|
137
149
|
if (reduced) {
|
|
138
150
|
reactNative.Animated.timing(pressT, {
|
|
@@ -151,16 +163,16 @@ var _Button = ({
|
|
|
151
163
|
},
|
|
152
164
|
[pressT, reduced]
|
|
153
165
|
);
|
|
154
|
-
const lastPress =
|
|
155
|
-
const handlePress =
|
|
166
|
+
const lastPress = React8.useRef(0);
|
|
167
|
+
const handlePress = React8.useCallback(() => {
|
|
156
168
|
if (!onPress) return;
|
|
157
169
|
const now = Date.now();
|
|
158
170
|
if (now - lastPress.current < 500) return;
|
|
159
171
|
lastPress.current = now;
|
|
160
172
|
onPress();
|
|
161
173
|
}, [onPress]);
|
|
162
|
-
const handlePressIn =
|
|
163
|
-
const handlePressOut =
|
|
174
|
+
const handlePressIn = React8.useCallback(() => pressTo(1), [pressTo]);
|
|
175
|
+
const handlePressOut = React8.useCallback(() => pressTo(0), [pressTo]);
|
|
164
176
|
const scale = pressT.interpolate({
|
|
165
177
|
inputRange: [0, 1],
|
|
166
178
|
outputRange: [1, reduced ? 1 : BUTTON_PRESS_SCALE]
|
|
@@ -211,7 +223,7 @@ var _Button = ({
|
|
|
211
223
|
}
|
|
212
224
|
);
|
|
213
225
|
};
|
|
214
|
-
var Button =
|
|
226
|
+
var Button = React8__default.default.memo(_Button);
|
|
215
227
|
var styles = reactNative.StyleSheet.create({
|
|
216
228
|
base: { alignItems: "center", justifyContent: "center" },
|
|
217
229
|
fill: { width: "100%" }
|
|
@@ -228,13 +240,13 @@ var CENTERED_MODAL_ANIM = {
|
|
|
228
240
|
/** Peak dim of the backdrop at progress 1. */
|
|
229
241
|
backdropOpacity: 0.55
|
|
230
242
|
};
|
|
231
|
-
var _CenteredModal =
|
|
243
|
+
var _CenteredModal = React8.forwardRef(
|
|
232
244
|
({ theme, dismissible = true, onDismiss, children }, ref) => {
|
|
233
|
-
const progress =
|
|
234
|
-
const [open, setOpen] =
|
|
235
|
-
const closingRef =
|
|
245
|
+
const progress = React8.useRef(new reactNative.Animated.Value(0)).current;
|
|
246
|
+
const [open, setOpen] = React8.useState(true);
|
|
247
|
+
const closingRef = React8.useRef(false);
|
|
236
248
|
const reduced = useReducedMotion();
|
|
237
|
-
|
|
249
|
+
React8.useEffect(() => {
|
|
238
250
|
const anim = reduced ? reactNative.Animated.timing(progress, {
|
|
239
251
|
toValue: 1,
|
|
240
252
|
duration: REDUCED_FADE_MS,
|
|
@@ -248,7 +260,7 @@ var _CenteredModal = React4.forwardRef(
|
|
|
248
260
|
anim.start();
|
|
249
261
|
return () => anim.stop();
|
|
250
262
|
}, [progress, reduced]);
|
|
251
|
-
const close =
|
|
263
|
+
const close = React8.useCallback(() => {
|
|
252
264
|
if (closingRef.current) return;
|
|
253
265
|
closingRef.current = true;
|
|
254
266
|
reactNative.Animated.timing(progress, {
|
|
@@ -261,15 +273,15 @@ var _CenteredModal = React4.forwardRef(
|
|
|
261
273
|
onDismiss();
|
|
262
274
|
});
|
|
263
275
|
}, [progress, onDismiss, reduced]);
|
|
264
|
-
|
|
265
|
-
|
|
276
|
+
React8.useImperativeHandle(ref, () => ({ close }), [close]);
|
|
277
|
+
React8.useEffect(() => {
|
|
266
278
|
const sub = reactNative.BackHandler.addEventListener("hardwareBackPress", () => {
|
|
267
279
|
if (dismissible) close();
|
|
268
280
|
return true;
|
|
269
281
|
});
|
|
270
282
|
return () => sub.remove();
|
|
271
283
|
}, [dismissible, close]);
|
|
272
|
-
const onBackdrop =
|
|
284
|
+
const onBackdrop = React8.useCallback(() => {
|
|
273
285
|
if (dismissible) close();
|
|
274
286
|
}, [dismissible, close]);
|
|
275
287
|
const backdropOpacity = progress.interpolate({
|
|
@@ -331,7 +343,7 @@ var _CenteredModal = React4.forwardRef(
|
|
|
331
343
|
}
|
|
332
344
|
);
|
|
333
345
|
_CenteredModal.displayName = "CenteredModal";
|
|
334
|
-
var CenteredModal =
|
|
346
|
+
var CenteredModal = React8__default.default.memo(_CenteredModal);
|
|
335
347
|
var styles2 = reactNative.StyleSheet.create({
|
|
336
348
|
fill: { flex: 1 },
|
|
337
349
|
backdrop: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, backgroundColor: "#000" },
|
|
@@ -347,6 +359,319 @@ var styles2 = reactNative.StyleSheet.create({
|
|
|
347
359
|
elevation: 12
|
|
348
360
|
}
|
|
349
361
|
});
|
|
362
|
+
var _DoneCheck = ({ theme, size = 48 }) => {
|
|
363
|
+
const reduced = useReducedMotion();
|
|
364
|
+
const pop = React8.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
|
|
365
|
+
React8.useEffect(() => {
|
|
366
|
+
if (reduced) {
|
|
367
|
+
pop.setValue(1);
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
const anim = reactNative.Animated.spring(pop, {
|
|
371
|
+
toValue: 1,
|
|
372
|
+
friction: STATUS_POP_SPRING.friction,
|
|
373
|
+
tension: STATUS_POP_SPRING.tension,
|
|
374
|
+
useNativeDriver: true
|
|
375
|
+
});
|
|
376
|
+
anim.start();
|
|
377
|
+
return () => anim.stop();
|
|
378
|
+
}, [pop, reduced]);
|
|
379
|
+
const scale = pop.interpolate({
|
|
380
|
+
inputRange: [0, 1],
|
|
381
|
+
outputRange: [STATUS_POP_SCALE_FROM, 1]
|
|
382
|
+
});
|
|
383
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
384
|
+
reactNative.Animated.View,
|
|
385
|
+
{
|
|
386
|
+
accessibilityElementsHidden: true,
|
|
387
|
+
importantForAccessibility: "no-hide-descendants",
|
|
388
|
+
style: [
|
|
389
|
+
styles3.ring,
|
|
390
|
+
{
|
|
391
|
+
width: size,
|
|
392
|
+
height: size,
|
|
393
|
+
borderRadius: theme.radius.full,
|
|
394
|
+
backgroundColor: theme.colors.success,
|
|
395
|
+
opacity: pop,
|
|
396
|
+
transform: [{ scale }]
|
|
397
|
+
}
|
|
398
|
+
],
|
|
399
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
400
|
+
reactNative.Animated.Text,
|
|
401
|
+
{
|
|
402
|
+
style: [
|
|
403
|
+
styles3.glyph,
|
|
404
|
+
{ color: theme.colors.onPrimary, fontSize: Math.round(size * 0.5), fontFamily: theme.fonts.bold }
|
|
405
|
+
],
|
|
406
|
+
children: "\u2713"
|
|
407
|
+
}
|
|
408
|
+
)
|
|
409
|
+
}
|
|
410
|
+
);
|
|
411
|
+
};
|
|
412
|
+
var DoneCheck = React8__default.default.memo(_DoneCheck);
|
|
413
|
+
DoneCheck.displayName = "DoneCheck";
|
|
414
|
+
var styles3 = reactNative.StyleSheet.create({
|
|
415
|
+
ring: { alignItems: "center", justifyContent: "center", alignSelf: "center" },
|
|
416
|
+
glyph: { includeFontPadding: false, textAlign: "center" }
|
|
417
|
+
});
|
|
418
|
+
var easeWire = reactNative.Easing.bezier(...WIRE_BEZIER);
|
|
419
|
+
var _CardHandoff = ({
|
|
420
|
+
transitionKey,
|
|
421
|
+
variant = "slide",
|
|
422
|
+
direction = "forward",
|
|
423
|
+
layout = "fill",
|
|
424
|
+
children
|
|
425
|
+
}) => {
|
|
426
|
+
const reduced = useReducedMotion();
|
|
427
|
+
const [leaving, setLeaving] = React8.useState(null);
|
|
428
|
+
const enterT = React8.useRef(new reactNative.Animated.Value(0)).current;
|
|
429
|
+
const exitT = React8.useRef(new reactNative.Animated.Value(0)).current;
|
|
430
|
+
const prev = React8.useRef({ key: transitionKey, node: children, variant });
|
|
431
|
+
const running = React8.useRef({});
|
|
432
|
+
const mounted = React8.useRef(false);
|
|
433
|
+
React8.useEffect(() => {
|
|
434
|
+
var _a, _b;
|
|
435
|
+
const last = prev.current;
|
|
436
|
+
prev.current = { key: transitionKey, node: children, variant };
|
|
437
|
+
if (!mounted.current) {
|
|
438
|
+
mounted.current = true;
|
|
439
|
+
running.current.enter = startEnter(enterT, variant, reduced, 0);
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
if (last.key === transitionKey) return;
|
|
443
|
+
(_a = running.current.enter) == null ? void 0 : _a.stop();
|
|
444
|
+
(_b = running.current.exit) == null ? void 0 : _b.stop();
|
|
445
|
+
setLeaving(last);
|
|
446
|
+
enterT.setValue(0);
|
|
447
|
+
exitT.setValue(0);
|
|
448
|
+
const exitDuration = reduced ? REDUCED_FADE_MS : last.variant === "fade" ? LOADER_HANDOFF_FADE_MS : CARD_EXIT_MS;
|
|
449
|
+
const exit = reactNative.Animated.timing(exitT, {
|
|
450
|
+
toValue: 1,
|
|
451
|
+
duration: scaledMs(exitDuration),
|
|
452
|
+
easing: easeWire,
|
|
453
|
+
useNativeDriver: true
|
|
454
|
+
});
|
|
455
|
+
running.current.exit = exit;
|
|
456
|
+
exit.start();
|
|
457
|
+
const enter = startEnter(
|
|
458
|
+
enterT,
|
|
459
|
+
variant,
|
|
460
|
+
reduced,
|
|
461
|
+
reduced ? 0 : CARD_EXIT_LEAD_MS,
|
|
462
|
+
() => setLeaving(null)
|
|
463
|
+
);
|
|
464
|
+
running.current.enter = enter;
|
|
465
|
+
});
|
|
466
|
+
React8.useEffect(
|
|
467
|
+
() => () => {
|
|
468
|
+
var _a, _b;
|
|
469
|
+
(_a = running.current.enter) == null ? void 0 : _a.stop();
|
|
470
|
+
(_b = running.current.exit) == null ? void 0 : _b.stop();
|
|
471
|
+
},
|
|
472
|
+
[]
|
|
473
|
+
);
|
|
474
|
+
const enterStyle = {
|
|
475
|
+
opacity: enterT,
|
|
476
|
+
transform: transformFor(variant, enterT, reduced, "enter", direction)
|
|
477
|
+
};
|
|
478
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
479
|
+
leaving ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
480
|
+
reactNative.Animated.View,
|
|
481
|
+
{
|
|
482
|
+
pointerEvents: "none",
|
|
483
|
+
style: [
|
|
484
|
+
reactNative.StyleSheet.absoluteFill,
|
|
485
|
+
{
|
|
486
|
+
opacity: exitT.interpolate({ inputRange: [0, 1], outputRange: [1, 0] }),
|
|
487
|
+
// The CURRENT direction drives BOTH layers: one transition, one geography.
|
|
488
|
+
transform: transformFor(leaving.variant, exitT, reduced, "exit", direction)
|
|
489
|
+
}
|
|
490
|
+
],
|
|
491
|
+
children: leaving.node
|
|
492
|
+
}
|
|
493
|
+
) : null,
|
|
494
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Animated.View, { style: [layout === "fill" ? styles4.fill : null, enterStyle], children }, variant)
|
|
495
|
+
] });
|
|
496
|
+
};
|
|
497
|
+
var startEnter = (value, variant, reduced, delay, onDone) => {
|
|
498
|
+
value.setValue(0);
|
|
499
|
+
const anim = !reduced && variant === "spring" ? reactNative.Animated.spring(value, {
|
|
500
|
+
toValue: 1,
|
|
501
|
+
delay: scaledMs(delay),
|
|
502
|
+
damping: CARD_SPRING.damping,
|
|
503
|
+
stiffness: CARD_SPRING.stiffness,
|
|
504
|
+
mass: CARD_SPRING.mass,
|
|
505
|
+
useNativeDriver: true
|
|
506
|
+
}) : reactNative.Animated.timing(value, {
|
|
507
|
+
toValue: 1,
|
|
508
|
+
delay: scaledMs(delay),
|
|
509
|
+
duration: scaledMs(
|
|
510
|
+
reduced ? REDUCED_FADE_MS : variant === "fade" ? LOADER_HANDOFF_FADE_MS : CARD_ENTER_MS
|
|
511
|
+
),
|
|
512
|
+
easing: easeWire,
|
|
513
|
+
useNativeDriver: true
|
|
514
|
+
});
|
|
515
|
+
anim.start(({ finished }) => {
|
|
516
|
+
if (finished) onDone == null ? void 0 : onDone();
|
|
517
|
+
});
|
|
518
|
+
return anim;
|
|
519
|
+
};
|
|
520
|
+
var transformFor = (variant, t, reduced, layer, travel) => {
|
|
521
|
+
if (reduced || variant === "fade") return [];
|
|
522
|
+
if (variant === "spring" && layer === "enter") {
|
|
523
|
+
return [{ scale: t.interpolate({ inputRange: [0, 1], outputRange: [CARD_ENTER_SCALE_FROM, 1] }) }];
|
|
524
|
+
}
|
|
525
|
+
const slide = travel === "forward" ? CARD_SLIDE_PX : -CARD_SLIDE_PX;
|
|
526
|
+
return [
|
|
527
|
+
{
|
|
528
|
+
translateX: t.interpolate({
|
|
529
|
+
inputRange: [0, 1],
|
|
530
|
+
outputRange: layer === "enter" ? [slide, 0] : [0, -slide]
|
|
531
|
+
})
|
|
532
|
+
}
|
|
533
|
+
];
|
|
534
|
+
};
|
|
535
|
+
var CardHandoff = React8__default.default.memo(_CardHandoff);
|
|
536
|
+
var styles4 = reactNative.StyleSheet.create({
|
|
537
|
+
fill: { flex: 1 }
|
|
538
|
+
});
|
|
539
|
+
var easeWire2 = reactNative.Easing.bezier(...WIRE_BEZIER);
|
|
540
|
+
var _ModalPaneHost = ({
|
|
541
|
+
paneKey,
|
|
542
|
+
direction = "forward",
|
|
543
|
+
header,
|
|
544
|
+
children
|
|
545
|
+
}) => {
|
|
546
|
+
const reduced = useReducedMotion();
|
|
547
|
+
const height = React8.useRef(new reactNative.Animated.Value(0)).current;
|
|
548
|
+
const measured = React8.useRef(null);
|
|
549
|
+
const [sized, setSized] = React8.useState(false);
|
|
550
|
+
const paneChanged = React8.useRef(false);
|
|
551
|
+
const running = React8.useRef(null);
|
|
552
|
+
React8.useEffect(() => {
|
|
553
|
+
paneChanged.current = true;
|
|
554
|
+
}, [paneKey]);
|
|
555
|
+
React8.useEffect(() => () => {
|
|
556
|
+
var _a;
|
|
557
|
+
return (_a = running.current) == null ? void 0 : _a.stop();
|
|
558
|
+
}, []);
|
|
559
|
+
const onLayout = React8.useCallback(
|
|
560
|
+
(event) => {
|
|
561
|
+
var _a;
|
|
562
|
+
const next = Math.round(event.nativeEvent.layout.height);
|
|
563
|
+
if (!next) return;
|
|
564
|
+
const previous = measured.current;
|
|
565
|
+
if (previous !== null && Math.abs(previous - next) < 1) return;
|
|
566
|
+
const first = previous === null;
|
|
567
|
+
const ease = !first && !reduced && paneChanged.current;
|
|
568
|
+
paneChanged.current = false;
|
|
569
|
+
measured.current = next;
|
|
570
|
+
(_a = running.current) == null ? void 0 : _a.stop();
|
|
571
|
+
if (!ease) {
|
|
572
|
+
height.setValue(next);
|
|
573
|
+
if (first) setSized(true);
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
const anim = reactNative.Animated.timing(height, {
|
|
577
|
+
toValue: next,
|
|
578
|
+
duration: scaledMs(CARD_ENTER_MS),
|
|
579
|
+
easing: easeWire2,
|
|
580
|
+
// height is layout, not transform — see the header note.
|
|
581
|
+
useNativeDriver: false
|
|
582
|
+
});
|
|
583
|
+
running.current = anim;
|
|
584
|
+
anim.start();
|
|
585
|
+
},
|
|
586
|
+
[height, reduced]
|
|
587
|
+
);
|
|
588
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Animated.View, { style: sized ? { height, overflow: "hidden" } : void 0, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { onLayout, style: styles5.pane, children: [
|
|
589
|
+
header,
|
|
590
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardHandoff, { transitionKey: paneKey, direction, layout: "auto", variant: "slide", children })
|
|
591
|
+
] }) });
|
|
592
|
+
};
|
|
593
|
+
var ModalPaneHost = React8__default.default.memo(_ModalPaneHost);
|
|
594
|
+
ModalPaneHost.displayName = "ModalPaneHost";
|
|
595
|
+
var styles5 = reactNative.StyleSheet.create({
|
|
596
|
+
pane: { width: "100%" }
|
|
597
|
+
});
|
|
598
|
+
var easeWire3 = reactNative.Easing.bezier(...WIRE_BEZIER);
|
|
599
|
+
var Dot = ({ theme, filled, size }) => {
|
|
600
|
+
const reduced = useReducedMotion();
|
|
601
|
+
const fill = React8.useRef(new reactNative.Animated.Value(filled ? 1 : 0)).current;
|
|
602
|
+
React8.useEffect(() => {
|
|
603
|
+
const anim = reactNative.Animated.timing(fill, {
|
|
604
|
+
toValue: filled ? 1 : 0,
|
|
605
|
+
duration: scaledMs(stateChangeDuration(reduced, SELECT_FILL_MS)),
|
|
606
|
+
easing: easeWire3,
|
|
607
|
+
useNativeDriver: true
|
|
608
|
+
});
|
|
609
|
+
anim.start();
|
|
610
|
+
return () => anim.stop();
|
|
611
|
+
}, [fill, filled, reduced]);
|
|
612
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
613
|
+
reactNative.View,
|
|
614
|
+
{
|
|
615
|
+
style: {
|
|
616
|
+
width: size,
|
|
617
|
+
height: size,
|
|
618
|
+
borderRadius: theme.radius.full,
|
|
619
|
+
backgroundColor: theme.colors.border
|
|
620
|
+
},
|
|
621
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
622
|
+
reactNative.Animated.View,
|
|
623
|
+
{
|
|
624
|
+
style: [
|
|
625
|
+
reactNative.StyleSheet.absoluteFill,
|
|
626
|
+
{
|
|
627
|
+
borderRadius: theme.radius.full,
|
|
628
|
+
backgroundColor: theme.colors.primary,
|
|
629
|
+
opacity: fill
|
|
630
|
+
}
|
|
631
|
+
]
|
|
632
|
+
}
|
|
633
|
+
)
|
|
634
|
+
}
|
|
635
|
+
);
|
|
636
|
+
};
|
|
637
|
+
var _StepDots = ({ theme, count, index }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
638
|
+
reactNative.View,
|
|
639
|
+
{
|
|
640
|
+
style: [styles6.row, { gap: theme.spacing.sm }],
|
|
641
|
+
accessibilityRole: "progressbar",
|
|
642
|
+
accessibilityLabel: `Step ${index + 1} of ${count}`,
|
|
643
|
+
accessibilityValue: { min: 1, max: count, now: index + 1 },
|
|
644
|
+
children: Array.from({ length: count }, (_, i) => /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", children: /* @__PURE__ */ jsxRuntime.jsx(Dot, { theme, filled: i <= index, size: theme.spacing.sm }) }, i))
|
|
645
|
+
}
|
|
646
|
+
);
|
|
647
|
+
var StepDots = React8__default.default.memo(_StepDots);
|
|
648
|
+
StepDots.displayName = "StepDots";
|
|
649
|
+
var styles6 = reactNative.StyleSheet.create({
|
|
650
|
+
row: { flexDirection: "row", alignItems: "center", justifyContent: "center" }
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
// src/haptics/haptics.ts
|
|
654
|
+
var play = async (tone) => {
|
|
655
|
+
try {
|
|
656
|
+
const mod = await import('expo-haptics');
|
|
657
|
+
if (!mod) return false;
|
|
658
|
+
if (tone === "selection") {
|
|
659
|
+
await mod.selectionAsync();
|
|
660
|
+
return true;
|
|
661
|
+
}
|
|
662
|
+
if (tone === "light") {
|
|
663
|
+
await mod.impactAsync(mod.ImpactFeedbackStyle.Light);
|
|
664
|
+
return true;
|
|
665
|
+
}
|
|
666
|
+
await mod.notificationAsync(mod.NotificationFeedbackType.Success);
|
|
667
|
+
return true;
|
|
668
|
+
} catch {
|
|
669
|
+
return false;
|
|
670
|
+
}
|
|
671
|
+
};
|
|
672
|
+
var playHaptic = (tone) => {
|
|
673
|
+
void play(tone);
|
|
674
|
+
};
|
|
350
675
|
|
|
351
676
|
// src/reviews/feedbackForm.ts
|
|
352
677
|
var FEEDBACK_MIN_LINES = 4;
|
|
@@ -361,6 +686,18 @@ var feedbackInputHeights = (fonts, verticalPadding = 0) => {
|
|
|
361
686
|
};
|
|
362
687
|
};
|
|
363
688
|
|
|
689
|
+
// src/theme/mergeThemeOver.ts
|
|
690
|
+
var mergeThemeOver = (base, override) => {
|
|
691
|
+
if (!override) return base;
|
|
692
|
+
return {
|
|
693
|
+
colors: { ...base.colors, ...override.colors },
|
|
694
|
+
fonts: { ...base.fonts, ...override.fonts },
|
|
695
|
+
radius: { ...base.radius, ...override.radius },
|
|
696
|
+
spacing: { ...base.spacing, ...override.spacing },
|
|
697
|
+
button: { ...base.button, ...override.button }
|
|
698
|
+
};
|
|
699
|
+
};
|
|
700
|
+
|
|
364
701
|
// src/reviews/decision.ts
|
|
365
702
|
var decideReview = (local, decision) => decision != null ? decision : local;
|
|
366
703
|
var resolveRules = (config) => {
|
|
@@ -408,6 +745,92 @@ var buildQuestionnaireSubmission = (input) => {
|
|
|
408
745
|
};
|
|
409
746
|
};
|
|
410
747
|
|
|
748
|
+
// src/questionnaire/steps.ts
|
|
749
|
+
var REQUIRED_STEPS = {
|
|
750
|
+
opinion: false,
|
|
751
|
+
improve: false,
|
|
752
|
+
suggestions: true
|
|
753
|
+
};
|
|
754
|
+
var DEFAULT_STEP_LABELS = {
|
|
755
|
+
opinion: "What do you think about the app?",
|
|
756
|
+
improve: "How can we improve?",
|
|
757
|
+
suggestions: "Any suggestions?"
|
|
758
|
+
};
|
|
759
|
+
var STEP_PLACEHOLDERS = {
|
|
760
|
+
opinion: "e.g. it feels fast and clear",
|
|
761
|
+
improve: "e.g. fewer steps to start",
|
|
762
|
+
suggestions: "e.g. a dark theme"
|
|
763
|
+
};
|
|
764
|
+
var buildQuestionnaireSteps = (questionnaire) => {
|
|
765
|
+
var _a, _b, _c;
|
|
766
|
+
return [
|
|
767
|
+
{
|
|
768
|
+
key: "opinion",
|
|
769
|
+
label: (_a = questionnaire.opinion_label) != null ? _a : DEFAULT_STEP_LABELS.opinion,
|
|
770
|
+
placeholder: STEP_PLACEHOLDERS.opinion,
|
|
771
|
+
required: REQUIRED_STEPS.opinion
|
|
772
|
+
},
|
|
773
|
+
{
|
|
774
|
+
key: "improve",
|
|
775
|
+
label: (_b = questionnaire.improve_label) != null ? _b : DEFAULT_STEP_LABELS.improve,
|
|
776
|
+
placeholder: STEP_PLACEHOLDERS.improve,
|
|
777
|
+
required: REQUIRED_STEPS.improve
|
|
778
|
+
},
|
|
779
|
+
{
|
|
780
|
+
key: "suggestions",
|
|
781
|
+
label: (_c = questionnaire.suggestions_label) != null ? _c : DEFAULT_STEP_LABELS.suggestions,
|
|
782
|
+
placeholder: STEP_PLACEHOLDERS.suggestions,
|
|
783
|
+
required: REQUIRED_STEPS.suggestions
|
|
784
|
+
}
|
|
785
|
+
];
|
|
786
|
+
};
|
|
787
|
+
var initialStepState = () => ({
|
|
788
|
+
index: 0,
|
|
789
|
+
drafts: {},
|
|
790
|
+
direction: "forward",
|
|
791
|
+
done: false
|
|
792
|
+
});
|
|
793
|
+
var currentStep = (steps, state) => steps[state.index];
|
|
794
|
+
var isLastStep = (steps, index) => index >= steps.length - 1;
|
|
795
|
+
var canGoBack = (state) => state.index > 0;
|
|
796
|
+
var canSkipStep = (steps, state) => {
|
|
797
|
+
var _a, _b;
|
|
798
|
+
return !((_b = (_a = steps[state.index]) == null ? void 0 : _a.required) != null ? _b : false);
|
|
799
|
+
};
|
|
800
|
+
var canCommitStep = (steps, state, value) => {
|
|
801
|
+
const step = steps[state.index];
|
|
802
|
+
if (!step) return false;
|
|
803
|
+
return !step.required || value.trim().length > 0;
|
|
804
|
+
};
|
|
805
|
+
var stashDraft = (steps, state, value) => {
|
|
806
|
+
const step = steps[state.index];
|
|
807
|
+
if (!step) return state;
|
|
808
|
+
return { ...state, drafts: { ...state.drafts, [step.key]: value } };
|
|
809
|
+
};
|
|
810
|
+
var commitStep = (steps, state, value) => {
|
|
811
|
+
const step = steps[state.index];
|
|
812
|
+
if (!step) return state;
|
|
813
|
+
if (!canCommitStep(steps, state, value)) return state;
|
|
814
|
+
const { drafts } = stashDraft(steps, state, value);
|
|
815
|
+
const last = isLastStep(steps, state.index);
|
|
816
|
+
return {
|
|
817
|
+
index: last ? state.index : state.index + 1,
|
|
818
|
+
drafts,
|
|
819
|
+
direction: "forward",
|
|
820
|
+
done: last
|
|
821
|
+
};
|
|
822
|
+
};
|
|
823
|
+
var skipStep = (steps, state) => commitStep(steps, state, "");
|
|
824
|
+
var goBack = (state) => state.index <= 0 ? state : { ...state, index: state.index - 1, direction: "backward" };
|
|
825
|
+
var toAnswers = (drafts) => {
|
|
826
|
+
var _a, _b, _c;
|
|
827
|
+
return {
|
|
828
|
+
opinion: (_a = drafts.opinion) != null ? _a : "",
|
|
829
|
+
improve: (_b = drafts.improve) != null ? _b : "",
|
|
830
|
+
suggestions: (_c = drafts.suggestions) != null ? _c : ""
|
|
831
|
+
};
|
|
832
|
+
};
|
|
833
|
+
|
|
411
834
|
// src/questionnaire/transport.ts
|
|
412
835
|
var submitQuestionnaireResponse = (target, id, submission) => {
|
|
413
836
|
if (!(target == null ? void 0 : target.serverUrl) || !id) return;
|
|
@@ -444,16 +867,6 @@ var fetchQuestionnaireDecision = async (target, options = {}) => {
|
|
|
444
867
|
return null;
|
|
445
868
|
}
|
|
446
869
|
};
|
|
447
|
-
var mergeThemeOver = (base, override) => {
|
|
448
|
-
if (!override) return base;
|
|
449
|
-
return {
|
|
450
|
-
colors: { ...base.colors, ...override.colors },
|
|
451
|
-
fonts: { ...base.fonts, ...override.fonts },
|
|
452
|
-
radius: { ...base.radius, ...override.radius },
|
|
453
|
-
spacing: { ...base.spacing, ...override.spacing },
|
|
454
|
-
button: { ...base.button, ...override.button }
|
|
455
|
-
};
|
|
456
|
-
};
|
|
457
870
|
var _QuestionnaireGate = ({
|
|
458
871
|
questionnaire,
|
|
459
872
|
target,
|
|
@@ -465,150 +878,208 @@ var _QuestionnaireGate = ({
|
|
|
465
878
|
theme: themeOverride,
|
|
466
879
|
dismissible = true
|
|
467
880
|
}) => {
|
|
468
|
-
var _a, _b, _c
|
|
881
|
+
var _a, _b, _c;
|
|
469
882
|
const active = useOnboardingTheme();
|
|
470
|
-
const t =
|
|
471
|
-
const modalRef =
|
|
883
|
+
const t = React8.useMemo(() => mergeThemeOver(active, themeOverride), [active, themeOverride]);
|
|
884
|
+
const modalRef = React8.useRef(null);
|
|
472
885
|
const id = questionnaire.id;
|
|
473
|
-
const
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
886
|
+
const steps = React8.useMemo(
|
|
887
|
+
() => buildQuestionnaireSteps({
|
|
888
|
+
opinion_label: questionnaire.opinion_label,
|
|
889
|
+
improve_label: questionnaire.improve_label,
|
|
890
|
+
suggestions_label: questionnaire.suggestions_label
|
|
891
|
+
}),
|
|
892
|
+
[questionnaire.opinion_label, questionnaire.improve_label, questionnaire.suggestions_label]
|
|
893
|
+
);
|
|
894
|
+
const [phase, setPhase] = React8.useState("form");
|
|
895
|
+
const [state, setState] = React8.useState(initialStepState);
|
|
896
|
+
const stateRef = React8.useRef(state);
|
|
897
|
+
stateRef.current = state;
|
|
898
|
+
const draftRef = React8.useRef("");
|
|
899
|
+
const [answered, setAnswered] = React8.useState(false);
|
|
900
|
+
const answeredRef = React8.useRef(false);
|
|
901
|
+
const seedDraft = React8.useCallback((value) => {
|
|
902
|
+
draftRef.current = value;
|
|
903
|
+
const has = value.trim().length > 0;
|
|
904
|
+
answeredRef.current = has;
|
|
905
|
+
setAnswered(has);
|
|
906
|
+
}, []);
|
|
907
|
+
const handleChangeText = React8.useCallback((text) => {
|
|
908
|
+
draftRef.current = text;
|
|
909
|
+
const has = text.trim().length > 0;
|
|
910
|
+
if (has === answeredRef.current) return;
|
|
911
|
+
answeredRef.current = has;
|
|
912
|
+
setAnswered(has);
|
|
913
|
+
}, []);
|
|
914
|
+
const shownRef = React8.useRef(false);
|
|
915
|
+
const onShownRef = React8.useRef(onShown);
|
|
916
|
+
const onEventRef = React8.useRef(onEvent);
|
|
480
917
|
onShownRef.current = onShown;
|
|
481
918
|
onEventRef.current = onEvent;
|
|
482
|
-
|
|
919
|
+
React8__default.default.useEffect(() => {
|
|
483
920
|
var _a2, _b2;
|
|
484
921
|
if (shownRef.current) return;
|
|
485
922
|
shownRef.current = true;
|
|
486
923
|
(_a2 = onShownRef.current) == null ? void 0 : _a2.call(onShownRef);
|
|
487
924
|
(_b2 = onEventRef.current) == null ? void 0 : _b2.call(onEventRef, { name: "questionnaire_prompt_shown", id });
|
|
488
925
|
}, []);
|
|
489
|
-
const resolveTimer =
|
|
490
|
-
|
|
926
|
+
const resolveTimer = React8.useRef(null);
|
|
927
|
+
React8__default.default.useEffect(() => {
|
|
491
928
|
return () => {
|
|
492
929
|
if (resolveTimer.current) clearTimeout(resolveTimer.current);
|
|
493
930
|
};
|
|
494
931
|
}, []);
|
|
495
|
-
const
|
|
496
|
-
|
|
497
|
-
}, [onResolved]);
|
|
498
|
-
const canSend = opinion.trim().length > 0;
|
|
499
|
-
const send = React4.useCallback(() => {
|
|
500
|
-
if (!canSend) return;
|
|
501
|
-
const submission = buildQuestionnaireSubmission({
|
|
502
|
-
answers: { opinion, improve, suggestions },
|
|
503
|
-
sessionId,
|
|
504
|
-
meta
|
|
505
|
-
});
|
|
506
|
-
submitQuestionnaireResponse(target, id, submission);
|
|
507
|
-
onEvent == null ? void 0 : onEvent({ name: "questionnaire_submitted", id });
|
|
508
|
-
setPhase("thanks");
|
|
509
|
-
if (resolveTimer.current) clearTimeout(resolveTimer.current);
|
|
510
|
-
resolveTimer.current = setTimeout(() => {
|
|
511
|
-
var _a2;
|
|
512
|
-
(_a2 = modalRef.current) == null ? void 0 : _a2.close();
|
|
513
|
-
}, 1500);
|
|
514
|
-
}, [canSend, opinion, improve, suggestions, sessionId, meta, target, id, onEvent]);
|
|
515
|
-
const handleDismissTap = React4.useCallback(() => {
|
|
932
|
+
const submittedRef = React8.useRef(false);
|
|
933
|
+
const handleDismissed = React8.useCallback(() => {
|
|
516
934
|
var _a2;
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
935
|
+
if (!submittedRef.current) {
|
|
936
|
+
(_a2 = onEventRef.current) == null ? void 0 : _a2.call(onEventRef, { name: "questionnaire_dismissed", id });
|
|
937
|
+
}
|
|
938
|
+
onResolved == null ? void 0 : onResolved();
|
|
939
|
+
}, [id, onResolved]);
|
|
940
|
+
const finish = React8.useCallback(
|
|
941
|
+
(drafts) => {
|
|
942
|
+
const submission = buildQuestionnaireSubmission({
|
|
943
|
+
answers: toAnswers(drafts),
|
|
944
|
+
sessionId,
|
|
945
|
+
meta
|
|
946
|
+
});
|
|
947
|
+
submitQuestionnaireResponse(target, id, submission);
|
|
948
|
+
submittedRef.current = true;
|
|
949
|
+
onEvent == null ? void 0 : onEvent({ name: "questionnaire_submitted", id });
|
|
950
|
+
playHaptic("success");
|
|
951
|
+
setPhase("thanks");
|
|
952
|
+
if (resolveTimer.current) clearTimeout(resolveTimer.current);
|
|
953
|
+
resolveTimer.current = setTimeout(() => {
|
|
954
|
+
var _a2;
|
|
955
|
+
(_a2 = modalRef.current) == null ? void 0 : _a2.close();
|
|
956
|
+
}, 1500);
|
|
957
|
+
},
|
|
958
|
+
[sessionId, meta, target, id, onEvent]
|
|
959
|
+
);
|
|
960
|
+
const applyTransition = React8.useCallback(
|
|
961
|
+
(next) => {
|
|
962
|
+
var _a2, _b2, _c2;
|
|
963
|
+
if (next === stateRef.current) return;
|
|
964
|
+
setState(next);
|
|
965
|
+
if (next.done) {
|
|
966
|
+
finish(next.drafts);
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
seedDraft((_c2 = next.drafts[(_b2 = (_a2 = steps[next.index]) == null ? void 0 : _a2.key) != null ? _b2 : "opinion"]) != null ? _c2 : "");
|
|
970
|
+
playHaptic("light");
|
|
971
|
+
},
|
|
972
|
+
[finish, seedDraft, steps]
|
|
973
|
+
);
|
|
974
|
+
const handleNext = React8.useCallback(() => {
|
|
975
|
+
applyTransition(commitStep(steps, stateRef.current, draftRef.current));
|
|
976
|
+
}, [applyTransition, steps]);
|
|
977
|
+
const handleSkip = React8.useCallback(() => {
|
|
978
|
+
applyTransition(skipStep(steps, stateRef.current));
|
|
979
|
+
}, [applyTransition, steps]);
|
|
980
|
+
const handleBack = React8.useCallback(() => {
|
|
981
|
+
var _a2, _b2, _c2;
|
|
982
|
+
const next = goBack(stashDraft(steps, stateRef.current, draftRef.current));
|
|
983
|
+
setState(next);
|
|
984
|
+
seedDraft((_c2 = next.drafts[(_b2 = (_a2 = steps[next.index]) == null ? void 0 : _a2.key) != null ? _b2 : "opinion"]) != null ? _c2 : "");
|
|
985
|
+
playHaptic("light");
|
|
986
|
+
}, [seedDraft, steps]);
|
|
987
|
+
const step = currentStep(steps, state);
|
|
988
|
+
const last = isLastStep(steps, state.index);
|
|
989
|
+
const canSkip = canSkipStep(steps, state);
|
|
990
|
+
const sendDisabled = ((_a = step == null ? void 0 : step.required) != null ? _a : false) && !answered;
|
|
991
|
+
const answerBox = feedbackInputHeights(t.fonts, t.spacing.md);
|
|
992
|
+
const linkStyle = [captionStyle(t.fonts), styles7.link, { color: t.colors.textMuted }];
|
|
993
|
+
const header = phase === "form" ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles7.header, { marginBottom: t.spacing.md }], children: [
|
|
994
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles7.headerSide, children: canGoBack(state) && /* @__PURE__ */ jsxRuntime.jsx(
|
|
995
|
+
reactNative.Pressable,
|
|
996
|
+
{
|
|
997
|
+
onPress: handleBack,
|
|
998
|
+
hitSlop: 12,
|
|
999
|
+
accessibilityRole: "button",
|
|
1000
|
+
accessibilityLabel: "Back to the previous question",
|
|
1001
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [headingStyle(t.fonts), styles7.backGlyph, { color: t.colors.textMuted }], children: "\u2039" })
|
|
1002
|
+
}
|
|
1003
|
+
) }),
|
|
1004
|
+
/* @__PURE__ */ jsxRuntime.jsx(StepDots, { theme: t, count: steps.length, index: state.index }),
|
|
1005
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles7.headerSide })
|
|
1006
|
+
] }) : null;
|
|
1007
|
+
const formPane = step ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { gap: t.spacing.md }, children: [
|
|
1008
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [headingStyle(t.fonts), { color: t.colors.text }], children: step.label }),
|
|
1009
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1010
|
+
reactNative.TextInput,
|
|
1011
|
+
{
|
|
1012
|
+
defaultValue: (_b = state.drafts[step.key]) != null ? _b : "",
|
|
1013
|
+
onChangeText: handleChangeText,
|
|
1014
|
+
placeholder: step.placeholder,
|
|
1015
|
+
placeholderTextColor: t.colors.textMuted,
|
|
1016
|
+
accessibilityLabel: step.label,
|
|
1017
|
+
multiline: true,
|
|
1018
|
+
textAlignVertical: "top",
|
|
1019
|
+
style: [
|
|
1020
|
+
bodyStyle(t.fonts),
|
|
1021
|
+
styles7.input,
|
|
1022
|
+
{
|
|
1023
|
+
color: t.colors.text,
|
|
1024
|
+
backgroundColor: t.colors.background,
|
|
1025
|
+
borderRadius: t.radius.md,
|
|
1026
|
+
borderColor: t.colors.border,
|
|
1027
|
+
padding: t.spacing.md,
|
|
1028
|
+
minHeight: answerBox.minHeight,
|
|
1029
|
+
maxHeight: answerBox.maxHeight
|
|
1030
|
+
}
|
|
1031
|
+
]
|
|
1032
|
+
},
|
|
1033
|
+
step.key
|
|
1034
|
+
),
|
|
1035
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1036
|
+
Button,
|
|
1037
|
+
{
|
|
1038
|
+
title: last ? "Send" : "Next",
|
|
1039
|
+
onPress: handleNext,
|
|
1040
|
+
variant: "primary",
|
|
1041
|
+
fullWidth: true,
|
|
1042
|
+
disabled: sendDisabled
|
|
1043
|
+
}
|
|
1044
|
+
),
|
|
1045
|
+
canSkip && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1046
|
+
reactNative.Pressable,
|
|
1047
|
+
{
|
|
1048
|
+
onPress: handleSkip,
|
|
1049
|
+
hitSlop: 8,
|
|
1050
|
+
accessibilityRole: "button",
|
|
1051
|
+
accessibilityLabel: "Skip this question",
|
|
1052
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: linkStyle, children: "Skip step" })
|
|
1053
|
+
}
|
|
1054
|
+
)
|
|
1055
|
+
] }) : null;
|
|
1056
|
+
const thanksPane = /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { gap: t.spacing.md }, children: [
|
|
1057
|
+
/* @__PURE__ */ jsxRuntime.jsx(DoneCheck, { theme: t }),
|
|
1058
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [headingStyle(t.fonts), styles7.centerText, { color: t.colors.text }], children: "Thank you" }),
|
|
1059
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [bodyStyle(t.fonts), styles7.centerText, { color: t.colors.textMuted }], children: "This helps us make it better." })
|
|
604
1060
|
] });
|
|
605
|
-
return /* @__PURE__ */ jsxRuntime.jsx(CenteredModal, { ref: modalRef, theme: t, dismissible, onDismiss:
|
|
1061
|
+
return /* @__PURE__ */ jsxRuntime.jsx(CenteredModal, { ref: modalRef, theme: t, dismissible, onDismiss: handleDismissed, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1062
|
+
ModalPaneHost,
|
|
1063
|
+
{
|
|
1064
|
+
paneKey: phase === "thanks" ? "thanks" : (_c = step == null ? void 0 : step.key) != null ? _c : "done",
|
|
1065
|
+
direction: state.direction,
|
|
1066
|
+
header,
|
|
1067
|
+
children: phase === "thanks" ? thanksPane : formPane
|
|
1068
|
+
}
|
|
1069
|
+
) });
|
|
606
1070
|
};
|
|
607
|
-
var QuestionnaireGate =
|
|
1071
|
+
var QuestionnaireGate = React8__default.default.memo(_QuestionnaireGate);
|
|
608
1072
|
QuestionnaireGate.displayName = "QuestionnaireGate";
|
|
609
|
-
var
|
|
1073
|
+
var styles7 = reactNative.StyleSheet.create({
|
|
610
1074
|
input: { borderWidth: 1 },
|
|
611
|
-
link: { textAlign: "center", textDecorationLine: "underline" }
|
|
1075
|
+
link: { textAlign: "center", textDecorationLine: "underline" },
|
|
1076
|
+
centerText: { textAlign: "center" },
|
|
1077
|
+
header: { flexDirection: "row", alignItems: "center" },
|
|
1078
|
+
// Equal flex on both sides keeps the dots optically centred whether Back is drawn or not.
|
|
1079
|
+
// `flex-start` matters: without it the back Pressable stretches across the whole left
|
|
1080
|
+
// third of the card, so a tap anywhere near the dots would navigate back.
|
|
1081
|
+
headerSide: { flex: 1, alignItems: "flex-start" },
|
|
1082
|
+
backGlyph: { includeFontPadding: false }
|
|
612
1083
|
});
|
|
613
1084
|
|
|
614
1085
|
// src/coachmarks/runtime.ts
|
|
@@ -750,16 +1221,16 @@ var fetchWireFeatures = async (config) => {
|
|
|
750
1221
|
|
|
751
1222
|
// src/features/useWireFeatures.ts
|
|
752
1223
|
var useStableValue = (value, isEqual) => {
|
|
753
|
-
const ref =
|
|
1224
|
+
const ref = React8.useRef(value);
|
|
754
1225
|
if (!isEqual(ref.current, value)) ref.current = value;
|
|
755
1226
|
return ref.current;
|
|
756
1227
|
};
|
|
757
1228
|
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);
|
|
758
1229
|
var useWireFeatures = (config) => {
|
|
759
1230
|
const stableConfig = useStableValue(config, sameFetchKey);
|
|
760
|
-
const [flags, setFlags] =
|
|
1231
|
+
const [flags, setFlags] = React8.useState(defaultWireFeatures);
|
|
761
1232
|
const canFetch = !!(stableConfig == null ? void 0 : stableConfig.serverUrl) && !!(stableConfig == null ? void 0 : stableConfig.apiKey);
|
|
762
|
-
|
|
1233
|
+
React8.useEffect(() => {
|
|
763
1234
|
if (!canFetch) {
|
|
764
1235
|
setFlags((prev) => featuresEqual(prev, defaultWireFeatures) ? prev : defaultWireFeatures);
|
|
765
1236
|
return;
|
|
@@ -778,10 +1249,10 @@ var useWireFeatures = (config) => {
|
|
|
778
1249
|
var CONTEXT_SYMBOL = /* @__PURE__ */ Symbol.for("wireai.features.context");
|
|
779
1250
|
var globalObj = global;
|
|
780
1251
|
if (!globalObj[CONTEXT_SYMBOL]) {
|
|
781
|
-
globalObj[CONTEXT_SYMBOL] =
|
|
1252
|
+
globalObj[CONTEXT_SYMBOL] = React8.createContext(null);
|
|
782
1253
|
}
|
|
783
1254
|
var WireFeaturesContext = globalObj[CONTEXT_SYMBOL];
|
|
784
|
-
var useWireFeaturesContext = () =>
|
|
1255
|
+
var useWireFeaturesContext = () => React8.useContext(WireFeaturesContext);
|
|
785
1256
|
var useResolvedFeatures = (options) => {
|
|
786
1257
|
var _a, _b, _c;
|
|
787
1258
|
const ctx = useWireFeaturesContext();
|
|
@@ -838,7 +1309,7 @@ var questionnaireSessionsKey = (id) => `wire_questionnaire_${id}_sessions`;
|
|
|
838
1309
|
|
|
839
1310
|
// src/questionnaire/useQuestionnaireGate.ts
|
|
840
1311
|
var useStableValue2 = (value, isEqual) => {
|
|
841
|
-
const ref =
|
|
1312
|
+
const ref = React8.useRef(value);
|
|
842
1313
|
if (!isEqual(ref.current, value)) ref.current = value;
|
|
843
1314
|
return ref.current;
|
|
844
1315
|
};
|
|
@@ -861,7 +1332,7 @@ var useQuestionnaireGate = ({
|
|
|
861
1332
|
);
|
|
862
1333
|
const lastKey = questionnaireLastShownKey(config.id);
|
|
863
1334
|
const sessionsKey = questionnaireSessionsKey(config.id);
|
|
864
|
-
const sessions =
|
|
1335
|
+
const sessions = React8.useState(() => {
|
|
865
1336
|
const store = resolveStorage(storage);
|
|
866
1337
|
const next = readInt(store, sessionsKey) + 1;
|
|
867
1338
|
writeInt(store, sessionsKey, next);
|
|
@@ -870,8 +1341,8 @@ var useQuestionnaireGate = ({
|
|
|
870
1341
|
const hasServerDecision = decision != null;
|
|
871
1342
|
const timeoutMs = config.timeoutFallbackMs;
|
|
872
1343
|
const waits = !hasServerDecision && timeoutMs != null && timeoutMs > 0;
|
|
873
|
-
const [elapsed, setElapsed] =
|
|
874
|
-
|
|
1344
|
+
const [elapsed, setElapsed] = React8.useState(!waits);
|
|
1345
|
+
React8.useEffect(() => {
|
|
875
1346
|
if (!waits) {
|
|
876
1347
|
setElapsed(true);
|
|
877
1348
|
return;
|
|
@@ -879,7 +1350,7 @@ var useQuestionnaireGate = ({
|
|
|
879
1350
|
const timer = setTimeout(() => setElapsed(true), timeoutMs);
|
|
880
1351
|
return () => clearTimeout(timer);
|
|
881
1352
|
}, [waits, timeoutMs]);
|
|
882
|
-
const { visible, verdict } =
|
|
1353
|
+
const { visible, verdict } = React8.useMemo(() => {
|
|
883
1354
|
if (!featureEnabled)
|
|
884
1355
|
return {
|
|
885
1356
|
visible: false,
|
|
@@ -916,16 +1387,16 @@ var useQuestionnaireGate = ({
|
|
|
916
1387
|
storage,
|
|
917
1388
|
isTesting
|
|
918
1389
|
]);
|
|
919
|
-
const shownRef =
|
|
920
|
-
const markShown =
|
|
1390
|
+
const shownRef = React8.useRef(false);
|
|
1391
|
+
const markShown = React8.useCallback(() => {
|
|
921
1392
|
if (shownRef.current) return;
|
|
922
1393
|
shownRef.current = true;
|
|
923
1394
|
writeInt(resolveStorage(storage), lastKey, Date.now());
|
|
924
1395
|
}, [storage, lastKey]);
|
|
925
|
-
const markResolved =
|
|
1396
|
+
const markResolved = React8.useCallback(() => {
|
|
926
1397
|
markSeenGate(seenKey, storage, isTesting);
|
|
927
1398
|
}, [seenKey, storage, isTesting]);
|
|
928
|
-
return
|
|
1399
|
+
return React8.useMemo(
|
|
929
1400
|
() => ({ visible, decision: verdict, markShown, markResolved }),
|
|
930
1401
|
[visible, verdict, markShown, markResolved]
|
|
931
1402
|
);
|