@wireai/activation 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +29 -5
- package/CHANGELOG.md +198 -6
- package/README.md +105 -10
- package/dist/analytics/index.d.mts +12 -8
- package/dist/analytics/index.d.ts +12 -8
- package/dist/analytics/index.js +525 -55
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +525 -55
- 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-CxnP7gAa.d.ts → currentSession-61dcm3V-.d.ts} +19 -3
- package/dist/{currentSession-BJBB7i4-.d.mts → currentSession-Bs2JfTJ8.d.mts} +19 -3
- 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 +620 -13
- package/dist/index.d.ts +620 -13
- package/dist/index.js +1194 -369
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1015 -201
- 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-DzU-TqZz.d.ts +86 -0
- package/dist/transport-f5VVB5hH.d.mts +86 -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/analyticsFacade.ts +55 -10
- 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/context/deviceId.ts +43 -0
- package/src/device/appVersion.ts +29 -8
- package/src/device/deviceContext.ts +14 -1
- package/src/device/deviceModel.ts +106 -0
- 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 +22 -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 +77 -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
package/dist/reviews/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var React7 = 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 React7__default = /*#__PURE__*/_interopDefault(React7);
|
|
11
11
|
|
|
12
12
|
// src/reviews/ReviewGate.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 = React7.createContext(null);
|
|
64
64
|
var useOnboardingTheme = () => {
|
|
65
|
-
const ctx =
|
|
65
|
+
const ctx = React7.useContext(OnboardingThemeContext);
|
|
66
66
|
return ctx != null ? ctx : mergeTheme();
|
|
67
67
|
};
|
|
68
68
|
|
|
@@ -94,16 +94,26 @@ 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 CARD_ENTER_MS = 220;
|
|
103
|
+
var CARD_EXIT_MS = 180;
|
|
104
|
+
var CARD_EXIT_LEAD_MS = 80;
|
|
105
|
+
var CARD_SLIDE_PX = 26;
|
|
106
|
+
var CARD_ENTER_SCALE_FROM = 0.96;
|
|
107
|
+
var CARD_SPRING = { damping: 14, stiffness: 180, mass: 1 };
|
|
108
|
+
var LOADER_HANDOFF_FADE_MS = 120;
|
|
109
|
+
var STATUS_POP_SPRING = { friction: 6, tension: 200 };
|
|
110
|
+
var STATUS_POP_SCALE_FROM = 0.4;
|
|
101
111
|
var durationScale = 1;
|
|
102
112
|
var scaledMs = (ms) => Math.round(ms * durationScale);
|
|
103
113
|
var lastKnown = false;
|
|
104
114
|
var useReducedMotion = () => {
|
|
105
|
-
const [reduced, setReduced] =
|
|
106
|
-
|
|
115
|
+
const [reduced, setReduced] = React7.useState(lastKnown);
|
|
116
|
+
React7.useEffect(() => {
|
|
107
117
|
let mounted = true;
|
|
108
118
|
reactNative.AccessibilityInfo.isReduceMotionEnabled().then((value) => {
|
|
109
119
|
lastKnown = value;
|
|
@@ -131,8 +141,8 @@ var _Button = ({
|
|
|
131
141
|
const t = useOnboardingTheme();
|
|
132
142
|
const reduced = useReducedMotion();
|
|
133
143
|
const isPrimary = variant === "primary";
|
|
134
|
-
const pressT =
|
|
135
|
-
const pressTo =
|
|
144
|
+
const pressT = React7.useRef(new reactNative.Animated.Value(0)).current;
|
|
145
|
+
const pressTo = React7.useCallback(
|
|
136
146
|
(to) => {
|
|
137
147
|
if (reduced) {
|
|
138
148
|
reactNative.Animated.timing(pressT, {
|
|
@@ -151,16 +161,16 @@ var _Button = ({
|
|
|
151
161
|
},
|
|
152
162
|
[pressT, reduced]
|
|
153
163
|
);
|
|
154
|
-
const lastPress =
|
|
155
|
-
const handlePress =
|
|
164
|
+
const lastPress = React7.useRef(0);
|
|
165
|
+
const handlePress = React7.useCallback(() => {
|
|
156
166
|
if (!onPress) return;
|
|
157
167
|
const now = Date.now();
|
|
158
168
|
if (now - lastPress.current < 500) return;
|
|
159
169
|
lastPress.current = now;
|
|
160
170
|
onPress();
|
|
161
171
|
}, [onPress]);
|
|
162
|
-
const handlePressIn =
|
|
163
|
-
const handlePressOut =
|
|
172
|
+
const handlePressIn = React7.useCallback(() => pressTo(1), [pressTo]);
|
|
173
|
+
const handlePressOut = React7.useCallback(() => pressTo(0), [pressTo]);
|
|
164
174
|
const scale = pressT.interpolate({
|
|
165
175
|
inputRange: [0, 1],
|
|
166
176
|
outputRange: [1, reduced ? 1 : BUTTON_PRESS_SCALE]
|
|
@@ -211,11 +221,282 @@ var _Button = ({
|
|
|
211
221
|
}
|
|
212
222
|
);
|
|
213
223
|
};
|
|
214
|
-
var Button =
|
|
224
|
+
var Button = React7__default.default.memo(_Button);
|
|
215
225
|
var styles = reactNative.StyleSheet.create({
|
|
216
226
|
base: { alignItems: "center", justifyContent: "center" },
|
|
217
227
|
fill: { width: "100%" }
|
|
218
228
|
});
|
|
229
|
+
var _DoneCheck = ({ theme, size = 48 }) => {
|
|
230
|
+
const reduced = useReducedMotion();
|
|
231
|
+
const pop = React7.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
|
|
232
|
+
React7.useEffect(() => {
|
|
233
|
+
if (reduced) {
|
|
234
|
+
pop.setValue(1);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const anim = reactNative.Animated.spring(pop, {
|
|
238
|
+
toValue: 1,
|
|
239
|
+
friction: STATUS_POP_SPRING.friction,
|
|
240
|
+
tension: STATUS_POP_SPRING.tension,
|
|
241
|
+
useNativeDriver: true
|
|
242
|
+
});
|
|
243
|
+
anim.start();
|
|
244
|
+
return () => anim.stop();
|
|
245
|
+
}, [pop, reduced]);
|
|
246
|
+
const scale = pop.interpolate({
|
|
247
|
+
inputRange: [0, 1],
|
|
248
|
+
outputRange: [STATUS_POP_SCALE_FROM, 1]
|
|
249
|
+
});
|
|
250
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
251
|
+
reactNative.Animated.View,
|
|
252
|
+
{
|
|
253
|
+
accessibilityElementsHidden: true,
|
|
254
|
+
importantForAccessibility: "no-hide-descendants",
|
|
255
|
+
style: [
|
|
256
|
+
styles2.ring,
|
|
257
|
+
{
|
|
258
|
+
width: size,
|
|
259
|
+
height: size,
|
|
260
|
+
borderRadius: theme.radius.full,
|
|
261
|
+
backgroundColor: theme.colors.success,
|
|
262
|
+
opacity: pop,
|
|
263
|
+
transform: [{ scale }]
|
|
264
|
+
}
|
|
265
|
+
],
|
|
266
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
267
|
+
reactNative.Animated.Text,
|
|
268
|
+
{
|
|
269
|
+
style: [
|
|
270
|
+
styles2.glyph,
|
|
271
|
+
{ color: theme.colors.onPrimary, fontSize: Math.round(size * 0.5), fontFamily: theme.fonts.bold }
|
|
272
|
+
],
|
|
273
|
+
children: "\u2713"
|
|
274
|
+
}
|
|
275
|
+
)
|
|
276
|
+
}
|
|
277
|
+
);
|
|
278
|
+
};
|
|
279
|
+
var DoneCheck = React7__default.default.memo(_DoneCheck);
|
|
280
|
+
DoneCheck.displayName = "DoneCheck";
|
|
281
|
+
var styles2 = reactNative.StyleSheet.create({
|
|
282
|
+
ring: { alignItems: "center", justifyContent: "center", alignSelf: "center" },
|
|
283
|
+
glyph: { includeFontPadding: false, textAlign: "center" }
|
|
284
|
+
});
|
|
285
|
+
var easeWire = reactNative.Easing.bezier(...WIRE_BEZIER);
|
|
286
|
+
var _CardHandoff = ({
|
|
287
|
+
transitionKey,
|
|
288
|
+
variant = "slide",
|
|
289
|
+
direction = "forward",
|
|
290
|
+
layout = "fill",
|
|
291
|
+
children
|
|
292
|
+
}) => {
|
|
293
|
+
const reduced = useReducedMotion();
|
|
294
|
+
const [leaving, setLeaving] = React7.useState(null);
|
|
295
|
+
const enterT = React7.useRef(new reactNative.Animated.Value(0)).current;
|
|
296
|
+
const exitT = React7.useRef(new reactNative.Animated.Value(0)).current;
|
|
297
|
+
const prev = React7.useRef({ key: transitionKey, node: children, variant });
|
|
298
|
+
const running = React7.useRef({});
|
|
299
|
+
const mounted = React7.useRef(false);
|
|
300
|
+
React7.useEffect(() => {
|
|
301
|
+
var _a, _b;
|
|
302
|
+
const last = prev.current;
|
|
303
|
+
prev.current = { key: transitionKey, node: children, variant };
|
|
304
|
+
if (!mounted.current) {
|
|
305
|
+
mounted.current = true;
|
|
306
|
+
running.current.enter = startEnter(enterT, variant, reduced, 0);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (last.key === transitionKey) return;
|
|
310
|
+
(_a = running.current.enter) == null ? void 0 : _a.stop();
|
|
311
|
+
(_b = running.current.exit) == null ? void 0 : _b.stop();
|
|
312
|
+
setLeaving(last);
|
|
313
|
+
enterT.setValue(0);
|
|
314
|
+
exitT.setValue(0);
|
|
315
|
+
const exitDuration = reduced ? REDUCED_FADE_MS : last.variant === "fade" ? LOADER_HANDOFF_FADE_MS : CARD_EXIT_MS;
|
|
316
|
+
const exit = reactNative.Animated.timing(exitT, {
|
|
317
|
+
toValue: 1,
|
|
318
|
+
duration: scaledMs(exitDuration),
|
|
319
|
+
easing: easeWire,
|
|
320
|
+
useNativeDriver: true
|
|
321
|
+
});
|
|
322
|
+
running.current.exit = exit;
|
|
323
|
+
exit.start();
|
|
324
|
+
const enter = startEnter(
|
|
325
|
+
enterT,
|
|
326
|
+
variant,
|
|
327
|
+
reduced,
|
|
328
|
+
reduced ? 0 : CARD_EXIT_LEAD_MS,
|
|
329
|
+
() => setLeaving(null)
|
|
330
|
+
);
|
|
331
|
+
running.current.enter = enter;
|
|
332
|
+
});
|
|
333
|
+
React7.useEffect(
|
|
334
|
+
() => () => {
|
|
335
|
+
var _a, _b;
|
|
336
|
+
(_a = running.current.enter) == null ? void 0 : _a.stop();
|
|
337
|
+
(_b = running.current.exit) == null ? void 0 : _b.stop();
|
|
338
|
+
},
|
|
339
|
+
[]
|
|
340
|
+
);
|
|
341
|
+
const enterStyle = {
|
|
342
|
+
opacity: enterT,
|
|
343
|
+
transform: transformFor(variant, enterT, reduced, "enter", direction)
|
|
344
|
+
};
|
|
345
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
346
|
+
leaving ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
347
|
+
reactNative.Animated.View,
|
|
348
|
+
{
|
|
349
|
+
pointerEvents: "none",
|
|
350
|
+
style: [
|
|
351
|
+
reactNative.StyleSheet.absoluteFill,
|
|
352
|
+
{
|
|
353
|
+
opacity: exitT.interpolate({ inputRange: [0, 1], outputRange: [1, 0] }),
|
|
354
|
+
// The CURRENT direction drives BOTH layers: one transition, one geography.
|
|
355
|
+
transform: transformFor(leaving.variant, exitT, reduced, "exit", direction)
|
|
356
|
+
}
|
|
357
|
+
],
|
|
358
|
+
children: leaving.node
|
|
359
|
+
}
|
|
360
|
+
) : null,
|
|
361
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Animated.View, { style: [layout === "fill" ? styles3.fill : null, enterStyle], children }, variant)
|
|
362
|
+
] });
|
|
363
|
+
};
|
|
364
|
+
var startEnter = (value, variant, reduced, delay, onDone) => {
|
|
365
|
+
value.setValue(0);
|
|
366
|
+
const anim = !reduced && variant === "spring" ? reactNative.Animated.spring(value, {
|
|
367
|
+
toValue: 1,
|
|
368
|
+
delay: scaledMs(delay),
|
|
369
|
+
damping: CARD_SPRING.damping,
|
|
370
|
+
stiffness: CARD_SPRING.stiffness,
|
|
371
|
+
mass: CARD_SPRING.mass,
|
|
372
|
+
useNativeDriver: true
|
|
373
|
+
}) : reactNative.Animated.timing(value, {
|
|
374
|
+
toValue: 1,
|
|
375
|
+
delay: scaledMs(delay),
|
|
376
|
+
duration: scaledMs(
|
|
377
|
+
reduced ? REDUCED_FADE_MS : variant === "fade" ? LOADER_HANDOFF_FADE_MS : CARD_ENTER_MS
|
|
378
|
+
),
|
|
379
|
+
easing: easeWire,
|
|
380
|
+
useNativeDriver: true
|
|
381
|
+
});
|
|
382
|
+
anim.start(({ finished }) => {
|
|
383
|
+
if (finished) onDone == null ? void 0 : onDone();
|
|
384
|
+
});
|
|
385
|
+
return anim;
|
|
386
|
+
};
|
|
387
|
+
var transformFor = (variant, t, reduced, layer, travel) => {
|
|
388
|
+
if (reduced || variant === "fade") return [];
|
|
389
|
+
if (variant === "spring" && layer === "enter") {
|
|
390
|
+
return [{ scale: t.interpolate({ inputRange: [0, 1], outputRange: [CARD_ENTER_SCALE_FROM, 1] }) }];
|
|
391
|
+
}
|
|
392
|
+
const slide = travel === "forward" ? CARD_SLIDE_PX : -CARD_SLIDE_PX;
|
|
393
|
+
return [
|
|
394
|
+
{
|
|
395
|
+
translateX: t.interpolate({
|
|
396
|
+
inputRange: [0, 1],
|
|
397
|
+
outputRange: layer === "enter" ? [slide, 0] : [0, -slide]
|
|
398
|
+
})
|
|
399
|
+
}
|
|
400
|
+
];
|
|
401
|
+
};
|
|
402
|
+
var CardHandoff = React7__default.default.memo(_CardHandoff);
|
|
403
|
+
var styles3 = reactNative.StyleSheet.create({
|
|
404
|
+
fill: { flex: 1 }
|
|
405
|
+
});
|
|
406
|
+
var easeWire2 = reactNative.Easing.bezier(...WIRE_BEZIER);
|
|
407
|
+
var _ModalPaneHost = ({
|
|
408
|
+
paneKey,
|
|
409
|
+
direction = "forward",
|
|
410
|
+
header,
|
|
411
|
+
children
|
|
412
|
+
}) => {
|
|
413
|
+
const reduced = useReducedMotion();
|
|
414
|
+
const height = React7.useRef(new reactNative.Animated.Value(0)).current;
|
|
415
|
+
const measured = React7.useRef(null);
|
|
416
|
+
const [sized, setSized] = React7.useState(false);
|
|
417
|
+
const paneChanged = React7.useRef(false);
|
|
418
|
+
const running = React7.useRef(null);
|
|
419
|
+
React7.useEffect(() => {
|
|
420
|
+
paneChanged.current = true;
|
|
421
|
+
}, [paneKey]);
|
|
422
|
+
React7.useEffect(() => () => {
|
|
423
|
+
var _a;
|
|
424
|
+
return (_a = running.current) == null ? void 0 : _a.stop();
|
|
425
|
+
}, []);
|
|
426
|
+
const onLayout = React7.useCallback(
|
|
427
|
+
(event) => {
|
|
428
|
+
var _a;
|
|
429
|
+
const next = Math.round(event.nativeEvent.layout.height);
|
|
430
|
+
if (!next) return;
|
|
431
|
+
const previous = measured.current;
|
|
432
|
+
if (previous !== null && Math.abs(previous - next) < 1) return;
|
|
433
|
+
const first = previous === null;
|
|
434
|
+
const ease = !first && !reduced && paneChanged.current;
|
|
435
|
+
paneChanged.current = false;
|
|
436
|
+
measured.current = next;
|
|
437
|
+
(_a = running.current) == null ? void 0 : _a.stop();
|
|
438
|
+
if (!ease) {
|
|
439
|
+
height.setValue(next);
|
|
440
|
+
if (first) setSized(true);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
const anim = reactNative.Animated.timing(height, {
|
|
444
|
+
toValue: next,
|
|
445
|
+
duration: scaledMs(CARD_ENTER_MS),
|
|
446
|
+
easing: easeWire2,
|
|
447
|
+
// height is layout, not transform — see the header note.
|
|
448
|
+
useNativeDriver: false
|
|
449
|
+
});
|
|
450
|
+
running.current = anim;
|
|
451
|
+
anim.start();
|
|
452
|
+
},
|
|
453
|
+
[height, reduced]
|
|
454
|
+
);
|
|
455
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Animated.View, { style: sized ? { height, overflow: "hidden" } : void 0, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { onLayout, style: styles4.pane, children: [
|
|
456
|
+
header,
|
|
457
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardHandoff, { transitionKey: paneKey, direction, layout: "auto", variant: "slide", children })
|
|
458
|
+
] }) });
|
|
459
|
+
};
|
|
460
|
+
var ModalPaneHost = React7__default.default.memo(_ModalPaneHost);
|
|
461
|
+
ModalPaneHost.displayName = "ModalPaneHost";
|
|
462
|
+
var styles4 = reactNative.StyleSheet.create({
|
|
463
|
+
pane: { width: "100%" }
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
// src/haptics/haptics.ts
|
|
467
|
+
var play = async (tone) => {
|
|
468
|
+
try {
|
|
469
|
+
const mod = await import('expo-haptics');
|
|
470
|
+
if (!mod) return false;
|
|
471
|
+
if (tone === "selection") {
|
|
472
|
+
await mod.selectionAsync();
|
|
473
|
+
return true;
|
|
474
|
+
}
|
|
475
|
+
if (tone === "light") {
|
|
476
|
+
await mod.impactAsync(mod.ImpactFeedbackStyle.Light);
|
|
477
|
+
return true;
|
|
478
|
+
}
|
|
479
|
+
await mod.notificationAsync(mod.NotificationFeedbackType.Success);
|
|
480
|
+
return true;
|
|
481
|
+
} catch {
|
|
482
|
+
return false;
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
var playHaptic = (tone) => {
|
|
486
|
+
void play(tone);
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
// src/theme/mergeThemeOver.ts
|
|
490
|
+
var mergeThemeOver = (base, override) => {
|
|
491
|
+
if (!override) return base;
|
|
492
|
+
return {
|
|
493
|
+
colors: { ...base.colors, ...override.colors },
|
|
494
|
+
fonts: { ...base.fonts, ...override.fonts },
|
|
495
|
+
radius: { ...base.radius, ...override.radius },
|
|
496
|
+
spacing: { ...base.spacing, ...override.spacing },
|
|
497
|
+
button: { ...base.button, ...override.button }
|
|
498
|
+
};
|
|
499
|
+
};
|
|
219
500
|
|
|
220
501
|
// src/reviews/decision.ts
|
|
221
502
|
var decideReview = (local, decision) => decision != null ? decision : local;
|
|
@@ -294,13 +575,13 @@ var centeredModalFrame = (progress, backdropMax = CENTERED_MODAL_ANIM.backdropOp
|
|
|
294
575
|
// src/reviews/presentation.ts
|
|
295
576
|
var DEFAULT_PRESENTATION = "modal";
|
|
296
577
|
var resolvePresentation = (p) => p != null ? p : DEFAULT_PRESENTATION;
|
|
297
|
-
var _CenteredModal =
|
|
578
|
+
var _CenteredModal = React7.forwardRef(
|
|
298
579
|
({ theme, dismissible = true, onDismiss, children }, ref) => {
|
|
299
|
-
const progress =
|
|
300
|
-
const [open, setOpen] =
|
|
301
|
-
const closingRef =
|
|
580
|
+
const progress = React7.useRef(new reactNative.Animated.Value(0)).current;
|
|
581
|
+
const [open, setOpen] = React7.useState(true);
|
|
582
|
+
const closingRef = React7.useRef(false);
|
|
302
583
|
const reduced = useReducedMotion();
|
|
303
|
-
|
|
584
|
+
React7.useEffect(() => {
|
|
304
585
|
const anim = reduced ? reactNative.Animated.timing(progress, {
|
|
305
586
|
toValue: 1,
|
|
306
587
|
duration: REDUCED_FADE_MS,
|
|
@@ -314,7 +595,7 @@ var _CenteredModal = React4.forwardRef(
|
|
|
314
595
|
anim.start();
|
|
315
596
|
return () => anim.stop();
|
|
316
597
|
}, [progress, reduced]);
|
|
317
|
-
const close =
|
|
598
|
+
const close = React7.useCallback(() => {
|
|
318
599
|
if (closingRef.current) return;
|
|
319
600
|
closingRef.current = true;
|
|
320
601
|
reactNative.Animated.timing(progress, {
|
|
@@ -327,15 +608,15 @@ var _CenteredModal = React4.forwardRef(
|
|
|
327
608
|
onDismiss();
|
|
328
609
|
});
|
|
329
610
|
}, [progress, onDismiss, reduced]);
|
|
330
|
-
|
|
331
|
-
|
|
611
|
+
React7.useImperativeHandle(ref, () => ({ close }), [close]);
|
|
612
|
+
React7.useEffect(() => {
|
|
332
613
|
const sub = reactNative.BackHandler.addEventListener("hardwareBackPress", () => {
|
|
333
614
|
if (dismissible) close();
|
|
334
615
|
return true;
|
|
335
616
|
});
|
|
336
617
|
return () => sub.remove();
|
|
337
618
|
}, [dismissible, close]);
|
|
338
|
-
const onBackdrop =
|
|
619
|
+
const onBackdrop = React7.useCallback(() => {
|
|
339
620
|
if (dismissible) close();
|
|
340
621
|
}, [dismissible, close]);
|
|
341
622
|
const backdropOpacity = progress.interpolate({
|
|
@@ -358,25 +639,25 @@ var _CenteredModal = React4.forwardRef(
|
|
|
358
639
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
359
640
|
reactNative.Animated.View,
|
|
360
641
|
{
|
|
361
|
-
style: [
|
|
642
|
+
style: [styles5.backdrop, { opacity: backdropOpacity }],
|
|
362
643
|
pointerEvents: "none"
|
|
363
644
|
}
|
|
364
645
|
),
|
|
365
646
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
366
647
|
reactNative.Pressable,
|
|
367
648
|
{
|
|
368
|
-
style:
|
|
649
|
+
style: styles5.fill,
|
|
369
650
|
onPress: onBackdrop,
|
|
370
651
|
accessibilityRole: "button",
|
|
371
652
|
accessibilityLabel: "Dismiss",
|
|
372
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(reactNativeSafeAreaContext.SafeAreaView, { style:
|
|
373
|
-
}, style:
|
|
653
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNativeSafeAreaContext.SafeAreaView, { style: styles5.center, pointerEvents: "box-none", children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: () => {
|
|
654
|
+
}, style: styles5.cardWrap, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
374
655
|
reactNative.Animated.View,
|
|
375
656
|
{
|
|
376
657
|
accessibilityViewIsModal: true,
|
|
377
658
|
accessibilityRole: "alert",
|
|
378
659
|
style: [
|
|
379
|
-
|
|
660
|
+
styles5.card,
|
|
380
661
|
{
|
|
381
662
|
backgroundColor: theme.colors.surface,
|
|
382
663
|
borderRadius: theme.radius.lg,
|
|
@@ -397,8 +678,8 @@ var _CenteredModal = React4.forwardRef(
|
|
|
397
678
|
}
|
|
398
679
|
);
|
|
399
680
|
_CenteredModal.displayName = "CenteredModal";
|
|
400
|
-
var CenteredModal =
|
|
401
|
-
var
|
|
681
|
+
var CenteredModal = React7__default.default.memo(_CenteredModal);
|
|
682
|
+
var styles5 = reactNative.StyleSheet.create({
|
|
402
683
|
fill: { flex: 1 },
|
|
403
684
|
backdrop: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, backgroundColor: "#000" },
|
|
404
685
|
center: { flex: 1, alignItems: "center", justifyContent: "center", padding: 24 },
|
|
@@ -463,6 +744,26 @@ var submitReview = (target, review) => {
|
|
|
463
744
|
} catch {
|
|
464
745
|
}
|
|
465
746
|
};
|
|
747
|
+
var fetchReviewDecision = async (target, options = {}) => {
|
|
748
|
+
if (!(target == null ? void 0 : target.serverUrl)) return null;
|
|
749
|
+
try {
|
|
750
|
+
const base = target.serverUrl.replace(/\/$/, "");
|
|
751
|
+
const params = new URLSearchParams();
|
|
752
|
+
if (options.sessionId) params.set("session_id", options.sessionId);
|
|
753
|
+
if (options.deviceKey) params.set("device_key", options.deviceKey);
|
|
754
|
+
const qs = params.toString();
|
|
755
|
+
const url = `${base}/v1/reviews/decision${qs ? `?${qs}` : ""}`;
|
|
756
|
+
const headers = {};
|
|
757
|
+
if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;
|
|
758
|
+
const res = await fetch(url, { headers });
|
|
759
|
+
if (!res || !res.ok) return null;
|
|
760
|
+
const json = await res.json();
|
|
761
|
+
if (!json || typeof json.fire !== "boolean") return null;
|
|
762
|
+
return json;
|
|
763
|
+
} catch {
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
766
|
+
};
|
|
466
767
|
var reportAppEvent = (target, name, options = {}) => {
|
|
467
768
|
if (!(target == null ? void 0 : target.serverUrl) || !name) return;
|
|
468
769
|
try {
|
|
@@ -487,16 +788,6 @@ var reportAppEvent = (target, name, options = {}) => {
|
|
|
487
788
|
} catch {
|
|
488
789
|
}
|
|
489
790
|
};
|
|
490
|
-
var mergeThemeOver = (base, override) => {
|
|
491
|
-
if (!override) return base;
|
|
492
|
-
return {
|
|
493
|
-
colors: { ...base.colors, ...override.colors },
|
|
494
|
-
fonts: { ...base.fonts, ...override.fonts },
|
|
495
|
-
radius: { ...base.radius, ...override.radius },
|
|
496
|
-
spacing: { ...base.spacing, ...override.spacing },
|
|
497
|
-
button: { ...base.button, ...override.button }
|
|
498
|
-
};
|
|
499
|
-
};
|
|
500
791
|
var _ReviewGate = ({
|
|
501
792
|
appName,
|
|
502
793
|
store,
|
|
@@ -512,36 +803,60 @@ var _ReviewGate = ({
|
|
|
512
803
|
id = "default"
|
|
513
804
|
}) => {
|
|
514
805
|
const active = useOnboardingTheme();
|
|
515
|
-
const t =
|
|
806
|
+
const t = React7.useMemo(() => mergeThemeOver(active, themeOverride), [active, themeOverride]);
|
|
516
807
|
const mode = resolvePresentation(presentation);
|
|
517
|
-
const modalRef =
|
|
518
|
-
const [phase, setPhase] =
|
|
519
|
-
const [stars, setStars] =
|
|
520
|
-
const [hovered, setHovered] =
|
|
521
|
-
const [feedback, setFeedback] =
|
|
522
|
-
const [suggestion, setSuggestion] =
|
|
523
|
-
const shownRef =
|
|
524
|
-
const onShownRef =
|
|
525
|
-
const onEventRef =
|
|
808
|
+
const modalRef = React7__default.default.useRef(null);
|
|
809
|
+
const [phase, setPhase] = React7.useState("rating");
|
|
810
|
+
const [stars, setStars] = React7.useState(0);
|
|
811
|
+
const [hovered, setHovered] = React7.useState(0);
|
|
812
|
+
const [feedback, setFeedback] = React7.useState("");
|
|
813
|
+
const [suggestion, setSuggestion] = React7.useState("");
|
|
814
|
+
const shownRef = React7__default.default.useRef(false);
|
|
815
|
+
const onShownRef = React7__default.default.useRef(onShown);
|
|
816
|
+
const onEventRef = React7__default.default.useRef(onEvent);
|
|
526
817
|
onShownRef.current = onShown;
|
|
527
818
|
onEventRef.current = onEvent;
|
|
528
|
-
|
|
819
|
+
React7__default.default.useEffect(() => {
|
|
529
820
|
var _a, _b;
|
|
530
821
|
if (shownRef.current) return;
|
|
531
822
|
shownRef.current = true;
|
|
532
823
|
(_a = onShownRef.current) == null ? void 0 : _a.call(onShownRef);
|
|
533
824
|
(_b = onEventRef.current) == null ? void 0 : _b.call(onEventRef, { name: "review_prompt_shown", id });
|
|
534
825
|
}, []);
|
|
535
|
-
const resolveTimer =
|
|
536
|
-
|
|
826
|
+
const resolveTimer = React7__default.default.useRef(null);
|
|
827
|
+
React7__default.default.useEffect(() => {
|
|
537
828
|
return () => {
|
|
538
829
|
if (resolveTimer.current) clearTimeout(resolveTimer.current);
|
|
539
830
|
};
|
|
540
831
|
}, []);
|
|
541
|
-
const resolve =
|
|
832
|
+
const resolve = React7.useCallback(() => {
|
|
542
833
|
onResolved == null ? void 0 : onResolved();
|
|
543
834
|
}, [onResolved]);
|
|
544
|
-
const
|
|
835
|
+
const postedRef = React7__default.default.useRef(false);
|
|
836
|
+
const postOnce = React7.useCallback(
|
|
837
|
+
(body) => {
|
|
838
|
+
if (postedRef.current) return;
|
|
839
|
+
postedRef.current = true;
|
|
840
|
+
submitReview(target, body);
|
|
841
|
+
},
|
|
842
|
+
[target]
|
|
843
|
+
);
|
|
844
|
+
const abandonRef = React7__default.default.useRef(() => {
|
|
845
|
+
});
|
|
846
|
+
abandonRef.current = () => {
|
|
847
|
+
if (postedRef.current || stars < 1) return;
|
|
848
|
+
postOnce(
|
|
849
|
+
buildReviewSubmission({
|
|
850
|
+
stars,
|
|
851
|
+
feedbackText: feedback,
|
|
852
|
+
suggestion,
|
|
853
|
+
sessionId,
|
|
854
|
+
meta: { ...meta, abandoned: true }
|
|
855
|
+
})
|
|
856
|
+
);
|
|
857
|
+
};
|
|
858
|
+
React7__default.default.useEffect(() => () => abandonRef.current(), []);
|
|
859
|
+
const resolveWithDelay = React7.useCallback(() => {
|
|
545
860
|
if (resolveTimer.current) clearTimeout(resolveTimer.current);
|
|
546
861
|
resolveTimer.current = setTimeout(() => {
|
|
547
862
|
var _a;
|
|
@@ -552,22 +867,32 @@ var _ReviewGate = ({
|
|
|
552
867
|
}
|
|
553
868
|
}, 1500);
|
|
554
869
|
}, [mode, onResolved]);
|
|
555
|
-
const pickRating =
|
|
870
|
+
const pickRating = React7.useCallback(
|
|
556
871
|
async (value) => {
|
|
557
872
|
setStars(value);
|
|
873
|
+
playHaptic("selection");
|
|
558
874
|
onEvent == null ? void 0 : onEvent({ name: "review_rating_selected", id, stars: value });
|
|
559
875
|
if (routeRating(value) === "store") {
|
|
560
876
|
onEvent == null ? void 0 : onEvent({ name: "store_review_requested", id, stars: value });
|
|
561
|
-
await requestStoreReview(store);
|
|
877
|
+
const route = await requestStoreReview(store);
|
|
878
|
+
postOnce(
|
|
879
|
+
buildReviewSubmission({
|
|
880
|
+
stars: value,
|
|
881
|
+
sessionId,
|
|
882
|
+
meta: { ...meta, store_route: route }
|
|
883
|
+
})
|
|
884
|
+
);
|
|
562
885
|
setPhase("thanks");
|
|
563
886
|
resolveWithDelay();
|
|
564
887
|
} else {
|
|
565
888
|
setPhase("feedback");
|
|
566
889
|
}
|
|
567
890
|
},
|
|
568
|
-
[onEvent, id, store, resolveWithDelay]
|
|
891
|
+
[onEvent, id, store, postOnce, sessionId, meta, resolveWithDelay]
|
|
569
892
|
);
|
|
570
|
-
const
|
|
893
|
+
const feedbackReady = feedback.trim().length > 0;
|
|
894
|
+
const sendFeedback = React7.useCallback(() => {
|
|
895
|
+
if (!feedbackReady) return;
|
|
571
896
|
const body = buildReviewSubmission({
|
|
572
897
|
stars,
|
|
573
898
|
feedbackText: feedback,
|
|
@@ -575,21 +900,21 @@ var _ReviewGate = ({
|
|
|
575
900
|
sessionId,
|
|
576
901
|
meta
|
|
577
902
|
});
|
|
578
|
-
|
|
903
|
+
postOnce(body);
|
|
579
904
|
onEvent == null ? void 0 : onEvent({ name: "review_feedback_submitted", id, stars });
|
|
905
|
+
playHaptic("success");
|
|
580
906
|
setPhase("thanks");
|
|
581
907
|
resolveWithDelay();
|
|
582
|
-
}, [stars, feedback, suggestion, sessionId, meta,
|
|
583
|
-
const dismiss =
|
|
908
|
+
}, [feedbackReady, stars, feedback, suggestion, sessionId, meta, postOnce, onEvent, id, resolveWithDelay]);
|
|
909
|
+
const dismiss = React7.useCallback(() => {
|
|
584
910
|
setPhase("thanks");
|
|
585
911
|
resolveWithDelay();
|
|
586
912
|
}, [resolveWithDelay]);
|
|
587
|
-
const handleDismissTap =
|
|
913
|
+
const handleDismissTap = React7.useCallback(() => {
|
|
588
914
|
var _a;
|
|
589
915
|
if (mode === "modal") (_a = modalRef.current) == null ? void 0 : _a.close();
|
|
590
916
|
else dismiss();
|
|
591
917
|
}, [mode, dismiss]);
|
|
592
|
-
const title = phase === "feedback" ? "What should we improve?" : phase === "thanks" ? "Thank you" : appName ? `Enjoying ${appName}?` : "How's your experience?";
|
|
593
918
|
const surface = {
|
|
594
919
|
backgroundColor: t.colors.surface,
|
|
595
920
|
borderRadius: t.radius.lg,
|
|
@@ -597,113 +922,121 @@ var _ReviewGate = ({
|
|
|
597
922
|
gap: t.spacing.md
|
|
598
923
|
};
|
|
599
924
|
const feedbackBox = feedbackInputHeights(t.fonts, t.spacing.md);
|
|
600
|
-
const
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
{
|
|
609
|
-
onPressIn: () => setHovered(n),
|
|
610
|
-
onPressOut: () => setHovered(0),
|
|
611
|
-
onPress: () => void pickRating(n),
|
|
612
|
-
accessibilityRole: "button",
|
|
613
|
-
accessibilityLabel: `${n} star${n > 1 ? "s" : ""}`,
|
|
614
|
-
hitSlop: 8,
|
|
615
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
616
|
-
reactNative.Text,
|
|
617
|
-
{
|
|
618
|
-
style: [
|
|
619
|
-
styles3.star,
|
|
620
|
-
{ color: filled ? t.colors.primary : t.colors.border }
|
|
621
|
-
],
|
|
622
|
-
children: filled ? "\u2605" : "\u2606"
|
|
623
|
-
}
|
|
624
|
-
)
|
|
625
|
-
},
|
|
626
|
-
n
|
|
627
|
-
);
|
|
628
|
-
}) }),
|
|
629
|
-
dismissible && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: handleDismissTap, hitSlop: 8, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [captionStyle(t.fonts), styles3.link, { color: t.colors.textMuted }], children: "Not now" }) })
|
|
630
|
-
] }),
|
|
631
|
-
phase === "feedback" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
632
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [captionStyle(t.fonts), { color: t.colors.textMuted }], children: "A few words is plenty. Both boxes are optional, so send whatever you like." }),
|
|
633
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [captionStyle(t.fonts), { color: t.colors.textMuted }], children: "Why this rating? (optional)" }),
|
|
634
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
635
|
-
reactNative.TextInput,
|
|
636
|
-
{
|
|
637
|
-
value: feedback,
|
|
638
|
-
onChangeText: setFeedback,
|
|
639
|
-
placeholder: "e.g. too many steps",
|
|
640
|
-
placeholderTextColor: t.colors.textMuted,
|
|
641
|
-
multiline: true,
|
|
642
|
-
textAlignVertical: "top",
|
|
643
|
-
style: [
|
|
644
|
-
bodyStyle(t.fonts),
|
|
645
|
-
styles3.input,
|
|
646
|
-
{
|
|
647
|
-
color: t.colors.text,
|
|
648
|
-
backgroundColor: t.colors.background,
|
|
649
|
-
borderRadius: t.radius.md,
|
|
650
|
-
borderColor: t.colors.border,
|
|
651
|
-
padding: t.spacing.md,
|
|
652
|
-
minHeight: feedbackBox.minHeight,
|
|
653
|
-
maxHeight: feedbackBox.maxHeight
|
|
654
|
-
}
|
|
655
|
-
]
|
|
656
|
-
}
|
|
657
|
-
),
|
|
658
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [captionStyle(t.fonts), { color: t.colors.textMuted }], children: "What can we do better? (optional)" }),
|
|
659
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
660
|
-
reactNative.TextInput,
|
|
661
|
-
{
|
|
662
|
-
value: suggestion,
|
|
663
|
-
onChangeText: setSuggestion,
|
|
664
|
-
placeholder: "e.g. save my place between steps",
|
|
665
|
-
placeholderTextColor: t.colors.textMuted,
|
|
666
|
-
style: [
|
|
667
|
-
bodyStyle(t.fonts),
|
|
668
|
-
styles3.input,
|
|
669
|
-
{
|
|
670
|
-
color: t.colors.text,
|
|
671
|
-
backgroundColor: t.colors.background,
|
|
672
|
-
borderRadius: t.radius.md,
|
|
673
|
-
borderColor: t.colors.border,
|
|
674
|
-
paddingHorizontal: t.spacing.md,
|
|
675
|
-
paddingVertical: t.spacing.sm,
|
|
676
|
-
minHeight: t.button.height
|
|
677
|
-
}
|
|
678
|
-
]
|
|
679
|
-
}
|
|
680
|
-
),
|
|
681
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
682
|
-
Button,
|
|
925
|
+
const linkStyle = [captionStyle(t.fonts), styles6.link, { color: t.colors.textMuted }];
|
|
926
|
+
const ratingPane = /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { gap: t.spacing.md }, children: [
|
|
927
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [headingStyle(t.fonts), { color: t.colors.text }], children: appName ? `Enjoying ${appName}?` : "How's your experience?" }),
|
|
928
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [captionStyle(t.fonts), { color: t.colors.textMuted }], children: "Tap a star to let us know." }),
|
|
929
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles6.stars, children: [1, 2, 3, 4, 5].map((n) => {
|
|
930
|
+
const filled = n <= (hovered || stars);
|
|
931
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
932
|
+
reactNative.Pressable,
|
|
683
933
|
{
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
934
|
+
onPressIn: () => setHovered(n),
|
|
935
|
+
onPressOut: () => setHovered(0),
|
|
936
|
+
onPress: () => void pickRating(n),
|
|
937
|
+
accessibilityRole: "button",
|
|
938
|
+
accessibilityLabel: `${n} star${n > 1 ? "s" : ""}`,
|
|
939
|
+
hitSlop: 8,
|
|
940
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.star, { color: filled ? t.colors.primary : t.colors.border }], children: filled ? "\u2605" : "\u2606" })
|
|
941
|
+
},
|
|
942
|
+
n
|
|
943
|
+
);
|
|
944
|
+
}) }),
|
|
945
|
+
dismissible && /* @__PURE__ */ jsxRuntime.jsx(
|
|
946
|
+
reactNative.Pressable,
|
|
947
|
+
{
|
|
948
|
+
onPress: handleDismissTap,
|
|
949
|
+
hitSlop: 8,
|
|
950
|
+
accessibilityRole: "button",
|
|
951
|
+
accessibilityLabel: "Dismiss the review prompt",
|
|
952
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: linkStyle, children: "Not now" })
|
|
953
|
+
}
|
|
954
|
+
)
|
|
955
|
+
] });
|
|
956
|
+
const feedbackPane = /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { gap: t.spacing.md }, children: [
|
|
957
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [headingStyle(t.fonts), { color: t.colors.text }], children: "What should we improve?" }),
|
|
958
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [captionStyle(t.fonts), { color: t.colors.textMuted }], children: "A few words is plenty \u2014 tell us what went wrong and we'll fix it." }),
|
|
959
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [captionStyle(t.fonts), { color: t.colors.textMuted }], children: "Why this rating?" }),
|
|
960
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
961
|
+
reactNative.TextInput,
|
|
962
|
+
{
|
|
963
|
+
value: feedback,
|
|
964
|
+
onChangeText: setFeedback,
|
|
965
|
+
placeholder: "e.g. too many steps",
|
|
966
|
+
placeholderTextColor: t.colors.textMuted,
|
|
967
|
+
accessibilityLabel: "Why this rating?",
|
|
968
|
+
multiline: true,
|
|
969
|
+
textAlignVertical: "top",
|
|
970
|
+
style: [
|
|
971
|
+
bodyStyle(t.fonts),
|
|
972
|
+
styles6.input,
|
|
973
|
+
{
|
|
974
|
+
color: t.colors.text,
|
|
975
|
+
backgroundColor: t.colors.background,
|
|
976
|
+
borderRadius: t.radius.md,
|
|
977
|
+
borderColor: t.colors.border,
|
|
978
|
+
padding: t.spacing.md,
|
|
979
|
+
minHeight: feedbackBox.minHeight,
|
|
980
|
+
maxHeight: feedbackBox.maxHeight
|
|
981
|
+
}
|
|
982
|
+
]
|
|
983
|
+
}
|
|
984
|
+
),
|
|
985
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [captionStyle(t.fonts), { color: t.colors.textMuted }], children: "What can we do better? (optional)" }),
|
|
986
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
987
|
+
reactNative.TextInput,
|
|
988
|
+
{
|
|
989
|
+
value: suggestion,
|
|
990
|
+
onChangeText: setSuggestion,
|
|
991
|
+
placeholder: "e.g. save my place between steps",
|
|
992
|
+
placeholderTextColor: t.colors.textMuted,
|
|
993
|
+
accessibilityLabel: "What can we do better? (optional)",
|
|
994
|
+
style: [
|
|
995
|
+
bodyStyle(t.fonts),
|
|
996
|
+
styles6.input,
|
|
997
|
+
{
|
|
998
|
+
color: t.colors.text,
|
|
999
|
+
backgroundColor: t.colors.background,
|
|
1000
|
+
borderRadius: t.radius.md,
|
|
1001
|
+
borderColor: t.colors.border,
|
|
1002
|
+
paddingHorizontal: t.spacing.md,
|
|
1003
|
+
paddingVertical: t.spacing.sm,
|
|
1004
|
+
minHeight: t.button.height
|
|
1005
|
+
}
|
|
1006
|
+
]
|
|
1007
|
+
}
|
|
1008
|
+
),
|
|
1009
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1010
|
+
Button,
|
|
1011
|
+
{
|
|
1012
|
+
title: "Send feedback",
|
|
1013
|
+
onPress: sendFeedback,
|
|
1014
|
+
variant: "primary",
|
|
1015
|
+
fullWidth: true,
|
|
1016
|
+
disabled: !feedbackReady
|
|
1017
|
+
}
|
|
1018
|
+
)
|
|
693
1019
|
] });
|
|
1020
|
+
const thanksPane = /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { gap: t.spacing.md }, children: [
|
|
1021
|
+
/* @__PURE__ */ jsxRuntime.jsx(DoneCheck, { theme: t }),
|
|
1022
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [headingStyle(t.fonts), styles6.centerText, { color: t.colors.text }], children: "Thank you" }),
|
|
1023
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [bodyStyle(t.fonts), styles6.centerText, { color: t.colors.textMuted }], children: "We appreciate you. This helps us make it better." })
|
|
1024
|
+
] });
|
|
1025
|
+
const content = /* @__PURE__ */ jsxRuntime.jsx(ModalPaneHost, { paneKey: phase, children: phase === "rating" ? ratingPane : phase === "feedback" ? feedbackPane : thanksPane });
|
|
694
1026
|
if (mode === "modal") {
|
|
695
1027
|
return /* @__PURE__ */ jsxRuntime.jsx(CenteredModal, { ref: modalRef, theme: t, dismissible, onDismiss: resolve, children: content });
|
|
696
1028
|
}
|
|
697
|
-
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [
|
|
1029
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles6.card, surface], children: content });
|
|
698
1030
|
};
|
|
699
|
-
var ReviewGate =
|
|
1031
|
+
var ReviewGate = React7__default.default.memo(_ReviewGate);
|
|
700
1032
|
ReviewGate.displayName = "ReviewGate";
|
|
701
|
-
var
|
|
1033
|
+
var styles6 = reactNative.StyleSheet.create({
|
|
702
1034
|
card: { width: "100%" },
|
|
703
1035
|
stars: { flexDirection: "row", justifyContent: "center", gap: 8 },
|
|
704
1036
|
star: { fontSize: 40, lineHeight: 46 },
|
|
705
1037
|
input: { borderWidth: 1 },
|
|
706
|
-
link: { textAlign: "center", textDecorationLine: "underline" }
|
|
1038
|
+
link: { textAlign: "center", textDecorationLine: "underline" },
|
|
1039
|
+
centerText: { textAlign: "center" }
|
|
707
1040
|
});
|
|
708
1041
|
|
|
709
1042
|
// src/coachmarks/runtime.ts
|
|
@@ -845,16 +1178,16 @@ var fetchWireFeatures = async (config) => {
|
|
|
845
1178
|
|
|
846
1179
|
// src/features/useWireFeatures.ts
|
|
847
1180
|
var useStableValue = (value, isEqual) => {
|
|
848
|
-
const ref =
|
|
1181
|
+
const ref = React7.useRef(value);
|
|
849
1182
|
if (!isEqual(ref.current, value)) ref.current = value;
|
|
850
1183
|
return ref.current;
|
|
851
1184
|
};
|
|
852
1185
|
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);
|
|
853
1186
|
var useWireFeatures = (config) => {
|
|
854
1187
|
const stableConfig = useStableValue(config, sameFetchKey);
|
|
855
|
-
const [flags, setFlags] =
|
|
1188
|
+
const [flags, setFlags] = React7.useState(defaultWireFeatures);
|
|
856
1189
|
const canFetch = !!(stableConfig == null ? void 0 : stableConfig.serverUrl) && !!(stableConfig == null ? void 0 : stableConfig.apiKey);
|
|
857
|
-
|
|
1190
|
+
React7.useEffect(() => {
|
|
858
1191
|
if (!canFetch) {
|
|
859
1192
|
setFlags((prev) => featuresEqual(prev, defaultWireFeatures) ? prev : defaultWireFeatures);
|
|
860
1193
|
return;
|
|
@@ -873,10 +1206,10 @@ var useWireFeatures = (config) => {
|
|
|
873
1206
|
var CONTEXT_SYMBOL = /* @__PURE__ */ Symbol.for("wireai.features.context");
|
|
874
1207
|
var globalObj = global;
|
|
875
1208
|
if (!globalObj[CONTEXT_SYMBOL]) {
|
|
876
|
-
globalObj[CONTEXT_SYMBOL] =
|
|
1209
|
+
globalObj[CONTEXT_SYMBOL] = React7.createContext(null);
|
|
877
1210
|
}
|
|
878
1211
|
var WireFeaturesContext = globalObj[CONTEXT_SYMBOL];
|
|
879
|
-
var useWireFeaturesContext = () =>
|
|
1212
|
+
var useWireFeaturesContext = () => React7.useContext(WireFeaturesContext);
|
|
880
1213
|
var useResolvedFeatures = (options) => {
|
|
881
1214
|
var _a, _b, _c;
|
|
882
1215
|
const ctx = useWireFeaturesContext();
|
|
@@ -931,7 +1264,7 @@ var writeInt = (storage, key, value) => {
|
|
|
931
1264
|
|
|
932
1265
|
// src/reviews/useReviewGate.ts
|
|
933
1266
|
var useStableValue2 = (value, isEqual) => {
|
|
934
|
-
const ref =
|
|
1267
|
+
const ref = React7.useRef(value);
|
|
935
1268
|
if (!isEqual(ref.current, value)) ref.current = value;
|
|
936
1269
|
return ref.current;
|
|
937
1270
|
};
|
|
@@ -954,7 +1287,7 @@ var useReviewGate = ({
|
|
|
954
1287
|
const seenKey = reviewSeenKey(config.id, config.oncePerVersion === false ? void 0 : config.appVersion);
|
|
955
1288
|
const lastKey = reviewLastShownKey(config.id);
|
|
956
1289
|
const sessionsKey = reviewSessionsKey(config.id);
|
|
957
|
-
const sessions =
|
|
1290
|
+
const sessions = React7.useState(() => {
|
|
958
1291
|
const store = resolveStorage(storage);
|
|
959
1292
|
const next = readInt(store, sessionsKey) + 1;
|
|
960
1293
|
writeInt(store, sessionsKey, next);
|
|
@@ -963,8 +1296,8 @@ var useReviewGate = ({
|
|
|
963
1296
|
const hasServerDecision = decision != null;
|
|
964
1297
|
const timeoutMs = config.timeoutFallbackMs;
|
|
965
1298
|
const waits = !hasServerDecision && timeoutMs != null && timeoutMs > 0;
|
|
966
|
-
const [elapsed, setElapsed] =
|
|
967
|
-
|
|
1299
|
+
const [elapsed, setElapsed] = React7.useState(!waits);
|
|
1300
|
+
React7.useEffect(() => {
|
|
968
1301
|
if (!waits) {
|
|
969
1302
|
setElapsed(true);
|
|
970
1303
|
return;
|
|
@@ -972,7 +1305,7 @@ var useReviewGate = ({
|
|
|
972
1305
|
const timer = setTimeout(() => setElapsed(true), timeoutMs);
|
|
973
1306
|
return () => clearTimeout(timer);
|
|
974
1307
|
}, [waits, timeoutMs]);
|
|
975
|
-
const { visible, verdict } =
|
|
1308
|
+
const { visible, verdict } = React7.useMemo(() => {
|
|
976
1309
|
if (!featureEnabled) return { visible: false, verdict: { fire: false, reason: "feature_disabled" } };
|
|
977
1310
|
if (testing2) return { visible: true, verdict: { fire: true, reason: "testing" } };
|
|
978
1311
|
const store = resolveStorage(storage);
|
|
@@ -988,16 +1321,16 @@ var useReviewGate = ({
|
|
|
988
1321
|
const ready = hasServerDecision || elapsed;
|
|
989
1322
|
return { visible: ready && resolved.fire && !alreadySeen, verdict: resolved };
|
|
990
1323
|
}, [featureEnabled, testing2, config, decision, events, sessions, elapsed, hasServerDecision, seenKey, lastKey, storage, isTesting]);
|
|
991
|
-
const shownRef =
|
|
992
|
-
const markShown =
|
|
1324
|
+
const shownRef = React7.useRef(false);
|
|
1325
|
+
const markShown = React7.useCallback(() => {
|
|
993
1326
|
if (shownRef.current) return;
|
|
994
1327
|
shownRef.current = true;
|
|
995
1328
|
writeInt(resolveStorage(storage), lastKey, Date.now());
|
|
996
1329
|
}, [storage, lastKey]);
|
|
997
|
-
const markResolved =
|
|
1330
|
+
const markResolved = React7.useCallback(() => {
|
|
998
1331
|
markSeenGate(seenKey, storage, isTesting);
|
|
999
1332
|
}, [seenKey, storage, isTesting]);
|
|
1000
|
-
return
|
|
1333
|
+
return React7.useMemo(
|
|
1001
1334
|
() => ({ visible, decision: verdict, markShown, markResolved }),
|
|
1002
1335
|
[visible, verdict, markShown, markResolved]
|
|
1003
1336
|
);
|
|
@@ -1014,6 +1347,7 @@ exports.decideReview = decideReview;
|
|
|
1014
1347
|
exports.entranceFrame = centeredModalFrame;
|
|
1015
1348
|
exports.evaluateGate = evaluateGate;
|
|
1016
1349
|
exports.feedbackInputHeights = feedbackInputHeights;
|
|
1350
|
+
exports.fetchReviewDecision = fetchReviewDecision;
|
|
1017
1351
|
exports.openStoreListing = openStoreListing;
|
|
1018
1352
|
exports.reportAppEvent = reportAppEvent;
|
|
1019
1353
|
exports.requestStoreReview = requestStoreReview;
|