@wireai/activation 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +73 -0
- package/CHANGELOG.md +572 -0
- package/INTEGRATION_PROMPT.md +55 -0
- package/LICENSE +21 -0
- package/README.md +923 -0
- package/dist/CenteredModal-C3qQBHsA.d.mts +38 -0
- package/dist/CenteredModal-Cdgns6--.d.ts +38 -0
- package/dist/coachmarks/index.d.mts +352 -0
- package/dist/coachmarks/index.d.ts +352 -0
- package/dist/coachmarks/index.js +944 -0
- package/dist/coachmarks/index.js.map +1 -0
- package/dist/coachmarks/index.mjs +918 -0
- package/dist/coachmarks/index.mjs.map +1 -0
- package/dist/decision-CFvGY6nP.d.ts +228 -0
- package/dist/decision-Cl8OFYzu.d.mts +228 -0
- package/dist/index.d.mts +1390 -0
- package/dist/index.d.ts +1390 -0
- package/dist/index.js +3260 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3186 -0
- package/dist/index.mjs.map +1 -0
- package/dist/questionnaire/index.d.mts +249 -0
- package/dist/questionnaire/index.d.ts +249 -0
- package/dist/questionnaire/index.js +922 -0
- package/dist/questionnaire/index.js.map +1 -0
- package/dist/questionnaire/index.mjs +906 -0
- package/dist/questionnaire/index.mjs.map +1 -0
- package/dist/reviews/index.d.mts +213 -0
- package/dist/reviews/index.d.ts +213 -0
- package/dist/reviews/index.js +1001 -0
- package/dist/reviews/index.js.map +1 -0
- package/dist/reviews/index.mjs +971 -0
- package/dist/reviews/index.mjs.map +1 -0
- package/dist/showcase/index.d.mts +119 -0
- package/dist/showcase/index.d.ts +119 -0
- package/dist/showcase/index.js +660 -0
- package/dist/showcase/index.js.map +1 -0
- package/dist/showcase/index.mjs +651 -0
- package/dist/showcase/index.mjs.map +1 -0
- package/dist/types-BKfpdZzX.d.mts +84 -0
- package/dist/types-BKfpdZzX.d.ts +84 -0
- package/dist/types-CMuOexw0.d.mts +116 -0
- package/dist/types-CMuOexw0.d.ts +116 -0
- package/dist/types-GL_hQ0TN.d.mts +46 -0
- package/dist/types-GL_hQ0TN.d.ts +46 -0
- package/llms.txt +36 -0
- package/metro/index.d.ts +32 -0
- package/metro/index.js +83 -0
- package/package.json +171 -0
- package/src/DemoOnboarding.tsx +133 -0
- package/src/OnboardingFlow.tsx +504 -0
- package/src/WireOnboarding.tsx +248 -0
- package/src/analytics/analyticsEvent.ts +69 -0
- package/src/analytics/reportClientEvent.ts +119 -0
- package/src/analytics/sendPreview.ts +73 -0
- package/src/attribution/attribution.ts +46 -0
- package/src/cards/ChipSelectCard.tsx +265 -0
- package/src/cards/InterstitialCard.tsx +299 -0
- package/src/cards/NumberStepperCard.tsx +121 -0
- package/src/cards/SelectionCard.tsx +270 -0
- package/src/cards/StatusCard.tsx +170 -0
- package/src/cards/TextInputCard.tsx +147 -0
- package/src/cards/index.ts +23 -0
- package/src/coachmarks/CoachmarkOverlayHost.tsx +44 -0
- package/src/coachmarks/CoachmarkProvider.tsx +108 -0
- package/src/coachmarks/GestureHint.tsx +331 -0
- package/src/coachmarks/SpotlightOverlay.tsx +254 -0
- package/src/coachmarks/coachmarkAnchorRegistry.ts +70 -0
- package/src/coachmarks/coachmarkOverlayStore.ts +63 -0
- package/src/coachmarks/index.ts +62 -0
- package/src/coachmarks/runtime.ts +103 -0
- package/src/coachmarks/selectTourSteps.ts +45 -0
- package/src/coachmarks/types.ts +58 -0
- package/src/coachmarks/useCoachmarkAnchor.ts +49 -0
- package/src/coachmarks/useCoachmarkTour.ts +200 -0
- package/src/components/AnimatedSparkle.tsx +101 -0
- package/src/components/Button.tsx +129 -0
- package/src/components/CardHandoff.tsx +212 -0
- package/src/components/CardLayout.tsx +103 -0
- package/src/components/CenteredModal.tsx +184 -0
- package/src/components/CompletionView.tsx +186 -0
- package/src/components/DoneBlock.tsx +37 -0
- package/src/components/ErrorBlock.tsx +42 -0
- package/src/components/Illustration.tsx +36 -0
- package/src/components/LoadingBlock.tsx +182 -0
- package/src/components/LoadingScreen.tsx +35 -0
- package/src/components/OnboardingScaffold.tsx +105 -0
- package/src/components/StepProgress.tsx +85 -0
- package/src/components/loaderChrome.ts +28 -0
- package/src/config/onboardingFlag.ts +39 -0
- package/src/config/wireConfigFromEnv.ts +52 -0
- package/src/device/deviceContext.ts +158 -0
- package/src/features/WireFeaturesProvider.tsx +81 -0
- package/src/features/cache.ts +92 -0
- package/src/features/defaults.ts +67 -0
- package/src/features/fetchWireFeatures.ts +86 -0
- package/src/features/index.ts +39 -0
- package/src/features/types.ts +61 -0
- package/src/features/useWireFeatures.ts +57 -0
- package/src/identity/userIdentity.ts +100 -0
- package/src/illustrations/defaultIllustrations.tsx +99 -0
- package/src/index.ts +159 -0
- package/src/motion/centeredModalMotion.ts +45 -0
- package/src/motion/motionSpec.ts +133 -0
- package/src/motion/useReducedMotion.ts +43 -0
- package/src/questionnaire/QuestionnaireGate.tsx +235 -0
- package/src/questionnaire/decision.ts +63 -0
- package/src/questionnaire/index.ts +54 -0
- package/src/questionnaire/runtime.ts +20 -0
- package/src/questionnaire/transport.ts +83 -0
- package/src/questionnaire/types.ts +182 -0
- package/src/questionnaire/useQuestionnaireGate.ts +159 -0
- package/src/reviews/ReviewGate.tsx +316 -0
- package/src/reviews/ReviewModal.tsx +13 -0
- package/src/reviews/decision.ts +120 -0
- package/src/reviews/equality.ts +54 -0
- package/src/reviews/expo-store-review.d.ts +16 -0
- package/src/reviews/feedbackForm.ts +41 -0
- package/src/reviews/index.ts +78 -0
- package/src/reviews/presentation.ts +29 -0
- package/src/reviews/runtime.ts +45 -0
- package/src/reviews/storeReview.ts +62 -0
- package/src/reviews/transport.ts +89 -0
- package/src/reviews/types.ts +172 -0
- package/src/reviews/useReviewGate.ts +137 -0
- package/src/session/persistedSession.ts +143 -0
- package/src/session-analytics/index.ts +18 -0
- package/src/session-analytics/reportSessionStart.ts +144 -0
- package/src/session-analytics/useSessionStart.ts +114 -0
- package/src/showcase/FeatureShowcase.tsx +233 -0
- package/src/showcase/index.ts +19 -0
- package/src/showcase/selectShowcaseSlides.ts +45 -0
- package/src/showcase/showcaseColors.ts +91 -0
- package/src/showcase/types.ts +74 -0
- package/src/theme/ThemeContext.tsx +38 -0
- package/src/theme/defaultTheme.ts +70 -0
- package/src/theme/themeFromBrand.ts +93 -0
- package/src/theme/types.ts +88 -0
- package/src/theme/typography.ts +46 -0
- package/src/types.ts +215 -0
- package/src/utils/deriveAnswers.ts +43 -0
- package/src/utils/readProgress.ts +33 -0
|
@@ -0,0 +1,944 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React3 = require('react');
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
var expoBlur = require('expo-blur');
|
|
6
|
+
var reactNative = require('react-native');
|
|
7
|
+
var Animated = require('react-native-reanimated');
|
|
8
|
+
|
|
9
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
+
|
|
11
|
+
var React3__default = /*#__PURE__*/_interopDefault(React3);
|
|
12
|
+
var Animated__default = /*#__PURE__*/_interopDefault(Animated);
|
|
13
|
+
|
|
14
|
+
// src/coachmarks/CoachmarkProvider.tsx
|
|
15
|
+
|
|
16
|
+
// src/features/defaults.ts
|
|
17
|
+
var defaultWireFeatures = Object.freeze({
|
|
18
|
+
coachmarks: Object.freeze({ enabled: true }),
|
|
19
|
+
showcase: Object.freeze({ enabled: true }),
|
|
20
|
+
onboarding: Object.freeze({ mode: "ai" }),
|
|
21
|
+
review: Object.freeze({ enabled: true }),
|
|
22
|
+
questionnaire: Object.freeze({ enabled: true }),
|
|
23
|
+
announcements: Object.freeze({ enabled: true })
|
|
24
|
+
});
|
|
25
|
+
var VALID_MODES = ["ai", "static", "off"];
|
|
26
|
+
var readEnabled = (raw) => {
|
|
27
|
+
if (raw && typeof raw === "object" && typeof raw.enabled === "boolean") {
|
|
28
|
+
return { enabled: raw.enabled };
|
|
29
|
+
}
|
|
30
|
+
return { enabled: true };
|
|
31
|
+
};
|
|
32
|
+
var parseWireFeatures = (raw) => {
|
|
33
|
+
var _a;
|
|
34
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return defaultWireFeatures;
|
|
35
|
+
const src = raw;
|
|
36
|
+
const modeRaw = (_a = src.onboarding) == null ? void 0 : _a.mode;
|
|
37
|
+
const mode = typeof modeRaw === "string" && VALID_MODES.includes(modeRaw) ? modeRaw : "ai";
|
|
38
|
+
return {
|
|
39
|
+
coachmarks: readEnabled(src.coachmarks),
|
|
40
|
+
showcase: readEnabled(src.showcase),
|
|
41
|
+
onboarding: { mode },
|
|
42
|
+
review: readEnabled(src.review),
|
|
43
|
+
questionnaire: readEnabled(src.questionnaire),
|
|
44
|
+
announcements: readEnabled(src.announcements)
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
var featuresEqual = (a, b) => a === b || a.coachmarks.enabled === b.coachmarks.enabled && a.showcase.enabled === b.showcase.enabled && a.onboarding.mode === b.onboarding.mode && a.review.enabled === b.review.enabled && a.questionnaire.enabled === b.questionnaire.enabled && a.announcements.enabled === b.announcements.enabled;
|
|
48
|
+
|
|
49
|
+
// src/features/cache.ts
|
|
50
|
+
var READ_TIMEOUT_MS = 1500;
|
|
51
|
+
var featuresCacheKey = (appId) => `wireai:features:${appId && appId.length > 0 ? appId : "default"}`;
|
|
52
|
+
var withTimeout = (p, ms) => {
|
|
53
|
+
let timer;
|
|
54
|
+
const timeout = new Promise((resolve) => {
|
|
55
|
+
timer = setTimeout(() => resolve(void 0), ms);
|
|
56
|
+
});
|
|
57
|
+
return Promise.race([p, timeout]).finally(() => clearTimeout(timer));
|
|
58
|
+
};
|
|
59
|
+
var readCachedFeatures = async (storage, key) => {
|
|
60
|
+
let raw;
|
|
61
|
+
try {
|
|
62
|
+
raw = await withTimeout(storage.getItem(key), READ_TIMEOUT_MS);
|
|
63
|
+
} catch {
|
|
64
|
+
return void 0;
|
|
65
|
+
}
|
|
66
|
+
if (!raw) return void 0;
|
|
67
|
+
try {
|
|
68
|
+
const parsed = JSON.parse(raw);
|
|
69
|
+
if (parsed && typeof parsed === "object" && typeof parsed.ts === "number") {
|
|
70
|
+
const ts = parsed.ts;
|
|
71
|
+
return {
|
|
72
|
+
features: parseWireFeatures(parsed.features),
|
|
73
|
+
ageMs: Date.now() - ts
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
} catch {
|
|
77
|
+
}
|
|
78
|
+
return void 0;
|
|
79
|
+
};
|
|
80
|
+
var writeCachedFeatures = (storage, key, features) => {
|
|
81
|
+
try {
|
|
82
|
+
void storage.setItem(key, JSON.stringify({ features, ts: Date.now() })).catch(() => {
|
|
83
|
+
});
|
|
84
|
+
} catch {
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// src/features/fetchWireFeatures.ts
|
|
89
|
+
var DEFAULT_TIMEOUT_MS = 4e3;
|
|
90
|
+
var failOpen = async (storage, key) => {
|
|
91
|
+
var _a;
|
|
92
|
+
if (!storage) return defaultWireFeatures;
|
|
93
|
+
const cached = await readCachedFeatures(storage, key);
|
|
94
|
+
return (_a = cached == null ? void 0 : cached.features) != null ? _a : defaultWireFeatures;
|
|
95
|
+
};
|
|
96
|
+
var fetchWithTimeout = async (url, apiKey, timeoutMs) => {
|
|
97
|
+
const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
|
|
98
|
+
const timer = setTimeout(() => controller == null ? void 0 : controller.abort(), timeoutMs);
|
|
99
|
+
try {
|
|
100
|
+
return await fetch(url, {
|
|
101
|
+
method: "GET",
|
|
102
|
+
headers: { Accept: "application/json", Authorization: `Bearer ${apiKey}` },
|
|
103
|
+
signal: controller == null ? void 0 : controller.signal
|
|
104
|
+
});
|
|
105
|
+
} finally {
|
|
106
|
+
clearTimeout(timer);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
var fetchWireFeatures = async (config) => {
|
|
110
|
+
const key = featuresCacheKey(config == null ? void 0 : config.appId);
|
|
111
|
+
const storage = config == null ? void 0 : config.storage;
|
|
112
|
+
if (!(config == null ? void 0 : config.serverUrl) || !(config == null ? void 0 : config.apiKey)) return failOpen(storage, key);
|
|
113
|
+
const url = `${config.serverUrl.replace(/\/$/, "")}/v1/features`;
|
|
114
|
+
const timeoutMs = config.timeoutMs && config.timeoutMs > 0 ? config.timeoutMs : DEFAULT_TIMEOUT_MS;
|
|
115
|
+
try {
|
|
116
|
+
const res = await fetchWithTimeout(url, config.apiKey, timeoutMs);
|
|
117
|
+
if (!res || !res.ok) return failOpen(storage, key);
|
|
118
|
+
const json = await res.json();
|
|
119
|
+
const features = parseWireFeatures(json);
|
|
120
|
+
if (storage) writeCachedFeatures(storage, key, features);
|
|
121
|
+
return features;
|
|
122
|
+
} catch {
|
|
123
|
+
return failOpen(storage, key);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// src/features/useWireFeatures.ts
|
|
128
|
+
var useStableValue = (value, isEqual) => {
|
|
129
|
+
const ref = React3.useRef(value);
|
|
130
|
+
if (!isEqual(ref.current, value)) ref.current = value;
|
|
131
|
+
return ref.current;
|
|
132
|
+
};
|
|
133
|
+
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);
|
|
134
|
+
var useWireFeatures = (config) => {
|
|
135
|
+
const stableConfig = useStableValue(config, sameFetchKey);
|
|
136
|
+
const [flags, setFlags] = React3.useState(defaultWireFeatures);
|
|
137
|
+
const canFetch = !!(stableConfig == null ? void 0 : stableConfig.serverUrl) && !!(stableConfig == null ? void 0 : stableConfig.apiKey);
|
|
138
|
+
React3.useEffect(() => {
|
|
139
|
+
if (!canFetch) {
|
|
140
|
+
setFlags((prev) => featuresEqual(prev, defaultWireFeatures) ? prev : defaultWireFeatures);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
let alive = true;
|
|
144
|
+
void fetchWireFeatures(stableConfig).then((next) => {
|
|
145
|
+
if (!alive) return;
|
|
146
|
+
setFlags((prev) => featuresEqual(prev, next) ? prev : next);
|
|
147
|
+
});
|
|
148
|
+
return () => {
|
|
149
|
+
alive = false;
|
|
150
|
+
};
|
|
151
|
+
}, [canFetch, stableConfig]);
|
|
152
|
+
return flags;
|
|
153
|
+
};
|
|
154
|
+
var WireFeaturesContext = React3.createContext(null);
|
|
155
|
+
var useWireFeaturesContext = () => React3.useContext(WireFeaturesContext);
|
|
156
|
+
var useResolvedFeatures = (options) => {
|
|
157
|
+
var _a, _b, _c;
|
|
158
|
+
const ctx = useWireFeaturesContext();
|
|
159
|
+
const shouldFetch = !(options == null ? void 0 : options.flags) && ctx == null;
|
|
160
|
+
const fetched = useWireFeatures(shouldFetch ? options == null ? void 0 : options.config : void 0);
|
|
161
|
+
return (_c = (_b = (_a = options == null ? void 0 : options.flags) != null ? _a : ctx) != null ? _b : fetched) != null ? _c : defaultWireFeatures;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// src/coachmarks/runtime.ts
|
|
165
|
+
var storageSingleton = null;
|
|
166
|
+
var testing = false;
|
|
167
|
+
var coachmarksEnabled = true;
|
|
168
|
+
var setCoachmarksEnabled = (value) => {
|
|
169
|
+
coachmarksEnabled = value;
|
|
170
|
+
};
|
|
171
|
+
var areCoachmarksEnabled = () => coachmarksEnabled;
|
|
172
|
+
var setCoachmarkStorage = (storage) => {
|
|
173
|
+
storageSingleton = storage;
|
|
174
|
+
};
|
|
175
|
+
var getCoachmarkStorage = () => storageSingleton;
|
|
176
|
+
var setCoachmarkTesting = (value) => {
|
|
177
|
+
testing = value;
|
|
178
|
+
};
|
|
179
|
+
var isCoachmarkTesting = () => testing;
|
|
180
|
+
var coachmarkGateKey = (tourId) => `wire_coachmark_${tourId}_seen`;
|
|
181
|
+
var showcaseGateKey = (showcaseId) => `wire_showcase_${showcaseId}_seen`;
|
|
182
|
+
var SEEN_VALUE = "1";
|
|
183
|
+
var hasSeenGate = (key, storageOverride, testingOverride) => {
|
|
184
|
+
if (testingOverride != null ? testingOverride : testing) return false;
|
|
185
|
+
const storage = storageOverride != null ? storageOverride : storageSingleton;
|
|
186
|
+
if (!storage) return false;
|
|
187
|
+
try {
|
|
188
|
+
return storage.getItem(key) === SEEN_VALUE;
|
|
189
|
+
} catch {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
var markSeenGate = (key, storageOverride, testingOverride) => {
|
|
194
|
+
if (testingOverride != null ? testingOverride : testing) return;
|
|
195
|
+
const storage = storageOverride != null ? storageOverride : storageSingleton;
|
|
196
|
+
if (!storage) return;
|
|
197
|
+
try {
|
|
198
|
+
storage.setItem(key, SEEN_VALUE);
|
|
199
|
+
} catch {
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
// src/coachmarks/coachmarkOverlayStore.ts
|
|
204
|
+
var current = null;
|
|
205
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
206
|
+
var emit = () => {
|
|
207
|
+
for (const listener of listeners) listener();
|
|
208
|
+
};
|
|
209
|
+
var coachmarkOverlay = {
|
|
210
|
+
show(state) {
|
|
211
|
+
if (!areCoachmarksEnabled()) return;
|
|
212
|
+
current = state;
|
|
213
|
+
emit();
|
|
214
|
+
},
|
|
215
|
+
hide() {
|
|
216
|
+
if (current === null) return;
|
|
217
|
+
current = null;
|
|
218
|
+
emit();
|
|
219
|
+
},
|
|
220
|
+
subscribe(listener) {
|
|
221
|
+
listeners.add(listener);
|
|
222
|
+
return () => {
|
|
223
|
+
listeners.delete(listener);
|
|
224
|
+
};
|
|
225
|
+
},
|
|
226
|
+
getSnapshot() {
|
|
227
|
+
return current;
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
var useCoachmarkOverlay = () => React3.useSyncExternalStore(
|
|
231
|
+
coachmarkOverlay.subscribe,
|
|
232
|
+
coachmarkOverlay.getSnapshot,
|
|
233
|
+
coachmarkOverlay.getSnapshot
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
// src/theme/defaultTheme.ts
|
|
237
|
+
var defaultOnboardingTheme = {
|
|
238
|
+
colors: {
|
|
239
|
+
background: "#FFFFFF",
|
|
240
|
+
surface: "#F7F7F8",
|
|
241
|
+
text: "#101114",
|
|
242
|
+
textMuted: "#6B7280",
|
|
243
|
+
primary: "#111111",
|
|
244
|
+
onPrimary: "#FFFFFF",
|
|
245
|
+
primarySoft: "rgba(17,17,17,0.06)",
|
|
246
|
+
border: "#E4E4E7",
|
|
247
|
+
borderStrong: "#C7C7CC",
|
|
248
|
+
disabled: "#EBEBEB",
|
|
249
|
+
success: "#16A34A",
|
|
250
|
+
warning: "#D97706",
|
|
251
|
+
error: "#DC2626",
|
|
252
|
+
info: "#2563EB"
|
|
253
|
+
},
|
|
254
|
+
fonts: {
|
|
255
|
+
regular: void 0,
|
|
256
|
+
medium: void 0,
|
|
257
|
+
bold: void 0,
|
|
258
|
+
baseSize: 16,
|
|
259
|
+
scale: 1
|
|
260
|
+
},
|
|
261
|
+
radius: {
|
|
262
|
+
sm: 8,
|
|
263
|
+
md: 12,
|
|
264
|
+
lg: 16,
|
|
265
|
+
full: 9999
|
|
266
|
+
},
|
|
267
|
+
spacing: {
|
|
268
|
+
xs: 4,
|
|
269
|
+
sm: 8,
|
|
270
|
+
md: 16,
|
|
271
|
+
lg: 24,
|
|
272
|
+
xl: 32
|
|
273
|
+
},
|
|
274
|
+
// Circled (pill) CTA by default; an app can pass `button: { radius: 12 }` for
|
|
275
|
+
// a rounded rectangle, or override height/paddingX to match its design system.
|
|
276
|
+
button: {
|
|
277
|
+
radius: 9999,
|
|
278
|
+
height: 52,
|
|
279
|
+
paddingX: 24
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
var mergeTheme = (override) => {
|
|
283
|
+
return defaultOnboardingTheme;
|
|
284
|
+
};
|
|
285
|
+
var OnboardingThemeContext = React3.createContext(null);
|
|
286
|
+
var useOnboardingTheme = () => {
|
|
287
|
+
const ctx = React3.useContext(OnboardingThemeContext);
|
|
288
|
+
return ctx != null ? ctx : mergeTheme();
|
|
289
|
+
};
|
|
290
|
+
var SWIPE_MS = 700;
|
|
291
|
+
var TAP_MS = 320;
|
|
292
|
+
var SPREAD_MS = 620;
|
|
293
|
+
var GLYPH_ROTATION = {
|
|
294
|
+
swipe_up: "0deg",
|
|
295
|
+
swipe_down: "180deg",
|
|
296
|
+
swipe_left: "-90deg",
|
|
297
|
+
swipe_right: "90deg"
|
|
298
|
+
};
|
|
299
|
+
var FingerPair = ({ size, color }) => {
|
|
300
|
+
const { row, index, middle } = React3.useMemo(() => {
|
|
301
|
+
const w = size * 0.2;
|
|
302
|
+
const r = w / 2;
|
|
303
|
+
return {
|
|
304
|
+
row: { flexDirection: "row", alignItems: "flex-end", gap: size * 0.08 },
|
|
305
|
+
index: { width: w, height: size * 0.54, borderRadius: r, backgroundColor: color },
|
|
306
|
+
middle: { width: w, height: size * 0.44, borderRadius: r, backgroundColor: color }
|
|
307
|
+
};
|
|
308
|
+
}, [size, color]);
|
|
309
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: row, pointerEvents: "none", children: [
|
|
310
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: index }),
|
|
311
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: middle })
|
|
312
|
+
] });
|
|
313
|
+
};
|
|
314
|
+
var MotionTrack = ({ size, color }) => {
|
|
315
|
+
const { wrap, tick0, tick1 } = React3.useMemo(() => {
|
|
316
|
+
const h = Math.max(2, size * 0.05);
|
|
317
|
+
return {
|
|
318
|
+
wrap: { alignItems: "center", marginTop: size * 0.1, gap: size * 0.06 },
|
|
319
|
+
tick0: {
|
|
320
|
+
width: size * 0.3,
|
|
321
|
+
height: h,
|
|
322
|
+
borderRadius: h / 2,
|
|
323
|
+
backgroundColor: color,
|
|
324
|
+
opacity: 0.4
|
|
325
|
+
},
|
|
326
|
+
tick1: {
|
|
327
|
+
width: size * 0.18,
|
|
328
|
+
height: h,
|
|
329
|
+
borderRadius: h / 2,
|
|
330
|
+
backgroundColor: color,
|
|
331
|
+
opacity: 0.16
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
}, [size, color]);
|
|
335
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: wrap, pointerEvents: "none", children: [
|
|
336
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: tick0 }),
|
|
337
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: tick1 })
|
|
338
|
+
] });
|
|
339
|
+
};
|
|
340
|
+
var GestureHintComponent = ({
|
|
341
|
+
type,
|
|
342
|
+
size = 34,
|
|
343
|
+
color = "white",
|
|
344
|
+
renderHand
|
|
345
|
+
}) => {
|
|
346
|
+
var _a;
|
|
347
|
+
const translate = Animated.useSharedValue(0);
|
|
348
|
+
const opacity = Animated.useSharedValue(1);
|
|
349
|
+
const pulse = Animated.useSharedValue(1);
|
|
350
|
+
const spread = Animated.useSharedValue(0);
|
|
351
|
+
const reduced = Animated.useReducedMotion();
|
|
352
|
+
const isSwipe = type === "swipe_up" || type === "swipe_down" || type === "swipe_left" || type === "swipe_right";
|
|
353
|
+
const isSpread = type === "pinch" || type === "zoom";
|
|
354
|
+
const isTap = type === "tap" || type === "double_tap";
|
|
355
|
+
React3.useEffect(() => {
|
|
356
|
+
if (type === "none" || reduced) return void 0;
|
|
357
|
+
if (isTap) {
|
|
358
|
+
pulse.value = Animated.withRepeat(
|
|
359
|
+
Animated.withSequence(
|
|
360
|
+
Animated.withTiming(0.82, { duration: TAP_MS }),
|
|
361
|
+
Animated.withTiming(1, { duration: TAP_MS })
|
|
362
|
+
),
|
|
363
|
+
-1,
|
|
364
|
+
false
|
|
365
|
+
);
|
|
366
|
+
opacity.value = Animated.withRepeat(
|
|
367
|
+
Animated.withSequence(
|
|
368
|
+
Animated.withTiming(0.55, { duration: TAP_MS }),
|
|
369
|
+
Animated.withTiming(1, { duration: TAP_MS })
|
|
370
|
+
),
|
|
371
|
+
-1,
|
|
372
|
+
false
|
|
373
|
+
);
|
|
374
|
+
return void 0;
|
|
375
|
+
}
|
|
376
|
+
if (isSpread) {
|
|
377
|
+
spread.value = Animated.withRepeat(
|
|
378
|
+
Animated.withSequence(
|
|
379
|
+
Animated.withTiming(size * 0.26, { duration: SPREAD_MS }),
|
|
380
|
+
Animated.withTiming(0, { duration: SPREAD_MS })
|
|
381
|
+
),
|
|
382
|
+
-1,
|
|
383
|
+
false
|
|
384
|
+
);
|
|
385
|
+
return void 0;
|
|
386
|
+
}
|
|
387
|
+
translate.value = Animated.withRepeat(
|
|
388
|
+
Animated.withSequence(
|
|
389
|
+
Animated.withTiming(0, { duration: 0 }),
|
|
390
|
+
Animated.withTiming(-26, { duration: SWIPE_MS }),
|
|
391
|
+
Animated.withDelay(140, Animated.withTiming(0, { duration: 0 }))
|
|
392
|
+
),
|
|
393
|
+
-1,
|
|
394
|
+
false
|
|
395
|
+
);
|
|
396
|
+
opacity.value = Animated.withRepeat(
|
|
397
|
+
Animated.withSequence(
|
|
398
|
+
Animated.withTiming(1, { duration: SWIPE_MS / 2 }),
|
|
399
|
+
Animated.withTiming(0, { duration: SWIPE_MS / 2 }),
|
|
400
|
+
Animated.withDelay(140, Animated.withTiming(1, { duration: 0 }))
|
|
401
|
+
),
|
|
402
|
+
-1,
|
|
403
|
+
false
|
|
404
|
+
);
|
|
405
|
+
return void 0;
|
|
406
|
+
}, [type, reduced, isTap, isSpread, translate, opacity, pulse, spread, size]);
|
|
407
|
+
const swipeAnimatedStyle = Animated.useAnimatedStyle(() => ({
|
|
408
|
+
opacity: opacity.value,
|
|
409
|
+
transform: [{ translateY: translate.value }]
|
|
410
|
+
}));
|
|
411
|
+
const tapAnimatedStyle = Animated.useAnimatedStyle(() => ({
|
|
412
|
+
opacity: opacity.value,
|
|
413
|
+
transform: [{ scale: pulse.value }]
|
|
414
|
+
}));
|
|
415
|
+
const spreadLeftStyle = Animated.useAnimatedStyle(() => ({
|
|
416
|
+
transform: [{ translateX: -spread.value }]
|
|
417
|
+
}));
|
|
418
|
+
const spreadRightStyle = Animated.useAnimatedStyle(() => ({
|
|
419
|
+
transform: [{ translateX: spread.value }]
|
|
420
|
+
}));
|
|
421
|
+
if (type === "none") return null;
|
|
422
|
+
const customGlyph = renderHand ? renderHand({ size, color }) : null;
|
|
423
|
+
if (isSwipe) {
|
|
424
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
425
|
+
reactNative.View,
|
|
426
|
+
{
|
|
427
|
+
style: { transform: [{ rotate: (_a = GLYPH_ROTATION[type]) != null ? _a : "0deg" }] },
|
|
428
|
+
pointerEvents: "none",
|
|
429
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.swipeStack, children: [
|
|
430
|
+
/* @__PURE__ */ jsxRuntime.jsx(Animated__default.default.View, { style: swipeAnimatedStyle, pointerEvents: "none", children: customGlyph != null ? customGlyph : /* @__PURE__ */ jsxRuntime.jsx(FingerPair, { size, color }) }),
|
|
431
|
+
/* @__PURE__ */ jsxRuntime.jsx(MotionTrack, { size, color })
|
|
432
|
+
] })
|
|
433
|
+
}
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
if (isSpread) {
|
|
437
|
+
const w = size * 0.2;
|
|
438
|
+
const capsule = {
|
|
439
|
+
width: w,
|
|
440
|
+
height: size * 0.5,
|
|
441
|
+
borderRadius: w / 2,
|
|
442
|
+
backgroundColor: color
|
|
443
|
+
};
|
|
444
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles.spreadRow, { gap: size * 0.12 }], pointerEvents: "none", children: [
|
|
445
|
+
/* @__PURE__ */ jsxRuntime.jsx(Animated__default.default.View, { style: spreadLeftStyle, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: capsule }) }),
|
|
446
|
+
/* @__PURE__ */ jsxRuntime.jsx(Animated__default.default.View, { style: spreadRightStyle, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: capsule }) })
|
|
447
|
+
] });
|
|
448
|
+
}
|
|
449
|
+
const ringSize = size * 0.86;
|
|
450
|
+
const ringBorder = Math.max(2, size * 0.08);
|
|
451
|
+
const dotSize = size * 0.32;
|
|
452
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles.tapWrap, { width: size, height: size }], pointerEvents: "none", children: customGlyph ? /* @__PURE__ */ jsxRuntime.jsx(Animated__default.default.View, { style: tapAnimatedStyle, pointerEvents: "none", children: customGlyph }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
453
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
454
|
+
Animated__default.default.View,
|
|
455
|
+
{
|
|
456
|
+
style: [
|
|
457
|
+
styles.tapRing,
|
|
458
|
+
{
|
|
459
|
+
width: ringSize,
|
|
460
|
+
height: ringSize,
|
|
461
|
+
borderRadius: ringSize / 2,
|
|
462
|
+
borderWidth: ringBorder,
|
|
463
|
+
borderColor: color
|
|
464
|
+
},
|
|
465
|
+
tapAnimatedStyle
|
|
466
|
+
],
|
|
467
|
+
pointerEvents: "none"
|
|
468
|
+
}
|
|
469
|
+
),
|
|
470
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
471
|
+
reactNative.View,
|
|
472
|
+
{
|
|
473
|
+
style: {
|
|
474
|
+
position: "absolute",
|
|
475
|
+
width: dotSize,
|
|
476
|
+
height: dotSize,
|
|
477
|
+
borderRadius: dotSize / 2,
|
|
478
|
+
backgroundColor: color
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
)
|
|
482
|
+
] }) });
|
|
483
|
+
};
|
|
484
|
+
var styles = reactNative.StyleSheet.create({
|
|
485
|
+
swipeStack: {
|
|
486
|
+
alignItems: "center",
|
|
487
|
+
justifyContent: "center"
|
|
488
|
+
},
|
|
489
|
+
spreadRow: {
|
|
490
|
+
flexDirection: "row",
|
|
491
|
+
alignItems: "center",
|
|
492
|
+
justifyContent: "center"
|
|
493
|
+
},
|
|
494
|
+
tapWrap: {
|
|
495
|
+
alignItems: "center",
|
|
496
|
+
justifyContent: "center"
|
|
497
|
+
},
|
|
498
|
+
tapRing: {
|
|
499
|
+
backgroundColor: "transparent"
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
var GestureHint = React3__default.default.memo(GestureHintComponent);
|
|
503
|
+
GestureHint.displayName = "GestureHint";
|
|
504
|
+
var AnimatedBlurView = Animated__default.default.createAnimatedComponent(expoBlur.BlurView);
|
|
505
|
+
var BLUR_INTENSITY = 26;
|
|
506
|
+
var FADE_MS = 280;
|
|
507
|
+
var RING_PADDING = 10;
|
|
508
|
+
var TOOLTIP_GAP = 14;
|
|
509
|
+
var SIDE_MARGIN = 20;
|
|
510
|
+
var PILL_MAX_WIDTH = 260;
|
|
511
|
+
var SpotlightOverlayComponent = ({
|
|
512
|
+
message,
|
|
513
|
+
targetRect,
|
|
514
|
+
gesture = "none",
|
|
515
|
+
placement = "bottom",
|
|
516
|
+
onEngage,
|
|
517
|
+
onDismiss,
|
|
518
|
+
accentColor,
|
|
519
|
+
accentTextColor
|
|
520
|
+
}) => {
|
|
521
|
+
const { height: screenH } = reactNative.useWindowDimensions();
|
|
522
|
+
const reduced = Animated.useReducedMotion();
|
|
523
|
+
const theme = useOnboardingTheme();
|
|
524
|
+
const accent = accentColor != null ? accentColor : theme.colors.primary;
|
|
525
|
+
const accentText = accentTextColor != null ? accentTextColor : theme.colors.onPrimary;
|
|
526
|
+
const showGesture = gesture !== "none" && gesture !== "tap" && gesture !== "double_tap";
|
|
527
|
+
const intensity = Animated.useSharedValue(0);
|
|
528
|
+
const ringScale = Animated.useSharedValue(1);
|
|
529
|
+
React3.useEffect(() => {
|
|
530
|
+
intensity.value = Animated.withTiming(BLUR_INTENSITY, { duration: FADE_MS });
|
|
531
|
+
}, [intensity]);
|
|
532
|
+
React3.useEffect(() => {
|
|
533
|
+
if (reduced) return;
|
|
534
|
+
ringScale.value = Animated.withRepeat(
|
|
535
|
+
Animated.withSequence(
|
|
536
|
+
Animated.withTiming(1.06, { duration: 700 }),
|
|
537
|
+
Animated.withTiming(1, { duration: 700 })
|
|
538
|
+
),
|
|
539
|
+
-1,
|
|
540
|
+
true
|
|
541
|
+
);
|
|
542
|
+
}, [reduced, ringScale]);
|
|
543
|
+
const blurAnimatedProps = Animated.useAnimatedProps(() => ({
|
|
544
|
+
intensity: intensity.value
|
|
545
|
+
}));
|
|
546
|
+
const ringAnimatedStyle = Animated.useAnimatedStyle(() => ({
|
|
547
|
+
transform: [{ scale: ringScale.value }]
|
|
548
|
+
}));
|
|
549
|
+
const geometry = React3.useMemo(() => {
|
|
550
|
+
if (!targetRect) return null;
|
|
551
|
+
const ringLeft = targetRect.x - RING_PADDING;
|
|
552
|
+
const ringTop = targetRect.y - RING_PADDING;
|
|
553
|
+
const ringW = targetRect.width + RING_PADDING * 2;
|
|
554
|
+
const ringH = targetRect.height + RING_PADDING * 2;
|
|
555
|
+
return {
|
|
556
|
+
ring: {
|
|
557
|
+
left: ringLeft,
|
|
558
|
+
top: ringTop,
|
|
559
|
+
width: ringW,
|
|
560
|
+
height: ringH,
|
|
561
|
+
borderRadius: Math.min(ringW, ringH) / 2
|
|
562
|
+
},
|
|
563
|
+
// Tooltip above or below the ring depending on placement.
|
|
564
|
+
tooltip: placement === "top" ? { bottom: screenH - ringTop + TOOLTIP_GAP } : { top: ringTop + ringH + TOOLTIP_GAP }
|
|
565
|
+
};
|
|
566
|
+
}, [targetRect, placement, screenH]);
|
|
567
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
568
|
+
Animated__default.default.View,
|
|
569
|
+
{
|
|
570
|
+
style: [reactNative.StyleSheet.absoluteFill, styles2.root],
|
|
571
|
+
entering: Animated.FadeIn.duration(FADE_MS),
|
|
572
|
+
exiting: Animated.FadeOut.duration(FADE_MS),
|
|
573
|
+
children: [
|
|
574
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
575
|
+
AnimatedBlurView,
|
|
576
|
+
{
|
|
577
|
+
tint: "dark",
|
|
578
|
+
animatedProps: blurAnimatedProps,
|
|
579
|
+
style: reactNative.StyleSheet.absoluteFill
|
|
580
|
+
}
|
|
581
|
+
),
|
|
582
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
583
|
+
reactNative.Pressable,
|
|
584
|
+
{
|
|
585
|
+
style: reactNative.StyleSheet.absoluteFill,
|
|
586
|
+
onPress: onDismiss,
|
|
587
|
+
accessibilityRole: "button",
|
|
588
|
+
accessibilityLabel: "Next hint"
|
|
589
|
+
}
|
|
590
|
+
),
|
|
591
|
+
geometry && /* @__PURE__ */ jsxRuntime.jsx(
|
|
592
|
+
Animated__default.default.View,
|
|
593
|
+
{
|
|
594
|
+
style: [
|
|
595
|
+
styles2.ring,
|
|
596
|
+
geometry.ring,
|
|
597
|
+
{ borderColor: accent, shadowColor: accent },
|
|
598
|
+
ringAnimatedStyle
|
|
599
|
+
],
|
|
600
|
+
pointerEvents: "box-none",
|
|
601
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
602
|
+
reactNative.Pressable,
|
|
603
|
+
{
|
|
604
|
+
style: reactNative.StyleSheet.absoluteFill,
|
|
605
|
+
onPress: onEngage,
|
|
606
|
+
accessibilityRole: "button",
|
|
607
|
+
accessibilityLabel: message
|
|
608
|
+
}
|
|
609
|
+
)
|
|
610
|
+
}
|
|
611
|
+
),
|
|
612
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
613
|
+
reactNative.View,
|
|
614
|
+
{
|
|
615
|
+
style: [
|
|
616
|
+
styles2.tooltipWrapper,
|
|
617
|
+
geometry ? geometry.tooltip : { top: screenH * 0.42, alignItems: "center" }
|
|
618
|
+
],
|
|
619
|
+
pointerEvents: "box-none",
|
|
620
|
+
children: [
|
|
621
|
+
showGesture && /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles2.gestureSlot, pointerEvents: "none", children: /* @__PURE__ */ jsxRuntime.jsx(GestureHint, { type: gesture, color: accent }) }),
|
|
622
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
623
|
+
reactNative.Pressable,
|
|
624
|
+
{
|
|
625
|
+
onPress: onEngage,
|
|
626
|
+
style: [styles2.pill, { backgroundColor: accent }],
|
|
627
|
+
accessibilityRole: "button",
|
|
628
|
+
accessibilityLabel: message,
|
|
629
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { numberOfLines: 3, style: [styles2.pillText, { color: accentText }], children: message })
|
|
630
|
+
}
|
|
631
|
+
)
|
|
632
|
+
]
|
|
633
|
+
}
|
|
634
|
+
)
|
|
635
|
+
]
|
|
636
|
+
}
|
|
637
|
+
);
|
|
638
|
+
};
|
|
639
|
+
var styles2 = reactNative.StyleSheet.create({
|
|
640
|
+
// High elevation/zIndex so the whole overlay (incl. the frosted blur) sits
|
|
641
|
+
// above the bottom tab bar on Android, where sibling z-order follows
|
|
642
|
+
// elevation, not tree order.
|
|
643
|
+
root: {
|
|
644
|
+
zIndex: 9999,
|
|
645
|
+
elevation: 9999
|
|
646
|
+
},
|
|
647
|
+
ring: {
|
|
648
|
+
position: "absolute",
|
|
649
|
+
borderWidth: 2.5,
|
|
650
|
+
backgroundColor: "transparent",
|
|
651
|
+
shadowOffset: { width: 0, height: 0 },
|
|
652
|
+
shadowOpacity: 0.9,
|
|
653
|
+
shadowRadius: 12,
|
|
654
|
+
elevation: 10
|
|
655
|
+
},
|
|
656
|
+
tooltipWrapper: {
|
|
657
|
+
position: "absolute",
|
|
658
|
+
left: SIDE_MARGIN,
|
|
659
|
+
right: SIDE_MARGIN,
|
|
660
|
+
alignItems: "center",
|
|
661
|
+
gap: 10
|
|
662
|
+
},
|
|
663
|
+
gestureSlot: {
|
|
664
|
+
alignItems: "center",
|
|
665
|
+
justifyContent: "center"
|
|
666
|
+
},
|
|
667
|
+
pill: {
|
|
668
|
+
maxWidth: PILL_MAX_WIDTH,
|
|
669
|
+
paddingVertical: 12,
|
|
670
|
+
paddingHorizontal: 18,
|
|
671
|
+
borderRadius: 18,
|
|
672
|
+
shadowColor: "#000",
|
|
673
|
+
shadowOffset: { width: 0, height: 2 },
|
|
674
|
+
shadowOpacity: 0.25,
|
|
675
|
+
shadowRadius: 6,
|
|
676
|
+
elevation: 6
|
|
677
|
+
},
|
|
678
|
+
pillText: {
|
|
679
|
+
fontSize: 14,
|
|
680
|
+
fontWeight: "800",
|
|
681
|
+
textAlign: "center"
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
var SpotlightOverlay = React3__default.default.memo(SpotlightOverlayComponent);
|
|
685
|
+
SpotlightOverlay.displayName = "SpotlightOverlay";
|
|
686
|
+
var CoachmarkOverlayHostComponent = ({
|
|
687
|
+
accentColor,
|
|
688
|
+
accentTextColor
|
|
689
|
+
}) => {
|
|
690
|
+
const active = useCoachmarkOverlay();
|
|
691
|
+
if (!active) return null;
|
|
692
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
693
|
+
SpotlightOverlay,
|
|
694
|
+
{
|
|
695
|
+
message: active.message,
|
|
696
|
+
targetRect: active.targetRect,
|
|
697
|
+
gesture: active.gesture,
|
|
698
|
+
placement: active.placement,
|
|
699
|
+
onEngage: active.onEngage,
|
|
700
|
+
onDismiss: active.onDismiss,
|
|
701
|
+
accentColor,
|
|
702
|
+
accentTextColor
|
|
703
|
+
}
|
|
704
|
+
);
|
|
705
|
+
};
|
|
706
|
+
var CoachmarkOverlayHost = React3__default.default.memo(CoachmarkOverlayHostComponent);
|
|
707
|
+
CoachmarkOverlayHost.displayName = "CoachmarkOverlayHost";
|
|
708
|
+
var CoachmarkProvider = ({
|
|
709
|
+
storage,
|
|
710
|
+
isTestingCoachmark = false,
|
|
711
|
+
accentColor,
|
|
712
|
+
accentTextColor,
|
|
713
|
+
features,
|
|
714
|
+
featuresConfig,
|
|
715
|
+
children
|
|
716
|
+
}) => {
|
|
717
|
+
const flags = useResolvedFeatures({ flags: features, config: featuresConfig });
|
|
718
|
+
const coachmarksOn = flags.coachmarks.enabled;
|
|
719
|
+
setCoachmarkStorage(storage);
|
|
720
|
+
setCoachmarkTesting(isTestingCoachmark);
|
|
721
|
+
setCoachmarksEnabled(coachmarksOn);
|
|
722
|
+
React3.useEffect(() => {
|
|
723
|
+
setCoachmarkStorage(storage);
|
|
724
|
+
setCoachmarkTesting(isTestingCoachmark);
|
|
725
|
+
setCoachmarksEnabled(coachmarksOn);
|
|
726
|
+
return () => {
|
|
727
|
+
if (getCoachmarkStorage() === storage) {
|
|
728
|
+
setCoachmarkStorage(null);
|
|
729
|
+
}
|
|
730
|
+
setCoachmarksEnabled(true);
|
|
731
|
+
};
|
|
732
|
+
}, [storage, isTestingCoachmark, coachmarksOn]);
|
|
733
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
734
|
+
children,
|
|
735
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
736
|
+
CoachmarkOverlayHost,
|
|
737
|
+
{
|
|
738
|
+
accentColor,
|
|
739
|
+
accentTextColor
|
|
740
|
+
}
|
|
741
|
+
)
|
|
742
|
+
] });
|
|
743
|
+
};
|
|
744
|
+
CoachmarkProvider.displayName = "CoachmarkProvider";
|
|
745
|
+
|
|
746
|
+
// src/coachmarks/coachmarkAnchorRegistry.ts
|
|
747
|
+
var anchors = /* @__PURE__ */ new Map();
|
|
748
|
+
var coachmarkAnchors = {
|
|
749
|
+
register(id, measurer) {
|
|
750
|
+
anchors.set(id, measurer);
|
|
751
|
+
},
|
|
752
|
+
/**
|
|
753
|
+
* Only clears the id when `measurer` is still the current registration, so a
|
|
754
|
+
* screen unmounting after a newer screen registered the same id does not wipe
|
|
755
|
+
* the live measurer. Omit `measurer` to force-delete regardless.
|
|
756
|
+
*/
|
|
757
|
+
unregister(id, measurer) {
|
|
758
|
+
if (measurer !== void 0 && anchors.get(id) !== measurer) return;
|
|
759
|
+
anchors.delete(id);
|
|
760
|
+
},
|
|
761
|
+
get(id) {
|
|
762
|
+
return anchors.get(id);
|
|
763
|
+
},
|
|
764
|
+
/**
|
|
765
|
+
* Resolve an anchor to a rect, retrying briefly while the target lays out
|
|
766
|
+
* (e.g. the first feed post, or a screen just navigated to). Cancellable via
|
|
767
|
+
* `shouldCancel` so a superseded step abandons its retry loop immediately.
|
|
768
|
+
*/
|
|
769
|
+
async resolve(id, {
|
|
770
|
+
retries = 6,
|
|
771
|
+
intervalMs = 120,
|
|
772
|
+
shouldCancel
|
|
773
|
+
} = {}) {
|
|
774
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
775
|
+
if (shouldCancel == null ? void 0 : shouldCancel()) return null;
|
|
776
|
+
const measurer = anchors.get(id);
|
|
777
|
+
if (measurer) {
|
|
778
|
+
const rect = await measurer();
|
|
779
|
+
if (rect && rect.width > 0 && rect.height > 0) return rect;
|
|
780
|
+
}
|
|
781
|
+
if (attempt < retries) {
|
|
782
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
return null;
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
// src/coachmarks/useCoachmarkAnchor.ts
|
|
790
|
+
var useCoachmarkAnchor = (id) => {
|
|
791
|
+
const ref = React3.useRef(null);
|
|
792
|
+
React3.useEffect(() => {
|
|
793
|
+
if (!id) return void 0;
|
|
794
|
+
const measure = () => new Promise((resolve) => {
|
|
795
|
+
const node = ref.current;
|
|
796
|
+
if (!node || typeof node.measureInWindow !== "function") {
|
|
797
|
+
return resolve(null);
|
|
798
|
+
}
|
|
799
|
+
node.measureInWindow((x, y, width, height) => {
|
|
800
|
+
if ([x, y, width, height].some(
|
|
801
|
+
(value) => value == null || Number.isNaN(value)
|
|
802
|
+
)) {
|
|
803
|
+
return resolve(null);
|
|
804
|
+
}
|
|
805
|
+
resolve({ x, y, width, height });
|
|
806
|
+
});
|
|
807
|
+
});
|
|
808
|
+
coachmarkAnchors.register(id, measure);
|
|
809
|
+
return () => coachmarkAnchors.unregister(id, measure);
|
|
810
|
+
}, [id]);
|
|
811
|
+
return ref;
|
|
812
|
+
};
|
|
813
|
+
var DEFAULT_START_DELAY_MS = 3e3;
|
|
814
|
+
var useCoachmarkTour = (steps, {
|
|
815
|
+
tourId,
|
|
816
|
+
showOnce = true,
|
|
817
|
+
enabled,
|
|
818
|
+
startDelayMs = DEFAULT_START_DELAY_MS,
|
|
819
|
+
onComplete,
|
|
820
|
+
onStepShown,
|
|
821
|
+
onStepEngaged,
|
|
822
|
+
onStepDismissed
|
|
823
|
+
}) => {
|
|
824
|
+
const [activeIndex, setActiveIndex] = React3.useState(null);
|
|
825
|
+
const startedRef = React3.useRef(false);
|
|
826
|
+
const finishedRef = React3.useRef(false);
|
|
827
|
+
const lastShownStepIdRef = React3.useRef(null);
|
|
828
|
+
const callbacksRef = React3.useRef({ onStepShown, onStepEngaged, onStepDismissed });
|
|
829
|
+
callbacksRef.current = { onStepShown, onStepEngaged, onStepDismissed };
|
|
830
|
+
const finish = React3.useCallback(() => {
|
|
831
|
+
if (finishedRef.current) return;
|
|
832
|
+
finishedRef.current = true;
|
|
833
|
+
if (showOnce) markSeenGate(coachmarkGateKey(tourId));
|
|
834
|
+
coachmarkOverlay.hide();
|
|
835
|
+
setActiveIndex(null);
|
|
836
|
+
onComplete == null ? void 0 : onComplete();
|
|
837
|
+
}, [onComplete, showOnce, tourId]);
|
|
838
|
+
React3.useEffect(() => {
|
|
839
|
+
if (!enabled || startedRef.current) return void 0;
|
|
840
|
+
if (!areCoachmarksEnabled()) return void 0;
|
|
841
|
+
if (showOnce && hasSeenGate(coachmarkGateKey(tourId))) return void 0;
|
|
842
|
+
const timer = setTimeout(() => {
|
|
843
|
+
startedRef.current = true;
|
|
844
|
+
setActiveIndex(0);
|
|
845
|
+
}, startDelayMs);
|
|
846
|
+
return () => clearTimeout(timer);
|
|
847
|
+
}, [enabled, startDelayMs, showOnce, tourId]);
|
|
848
|
+
React3.useEffect(() => {
|
|
849
|
+
if (activeIndex === null) return void 0;
|
|
850
|
+
if (!enabled) {
|
|
851
|
+
if (startedRef.current && !finishedRef.current) coachmarkOverlay.hide();
|
|
852
|
+
return void 0;
|
|
853
|
+
}
|
|
854
|
+
if (activeIndex >= steps.length) {
|
|
855
|
+
finish();
|
|
856
|
+
return void 0;
|
|
857
|
+
}
|
|
858
|
+
const step = steps[activeIndex];
|
|
859
|
+
if (!step) {
|
|
860
|
+
finish();
|
|
861
|
+
return void 0;
|
|
862
|
+
}
|
|
863
|
+
let cancelled = false;
|
|
864
|
+
(async () => {
|
|
865
|
+
var _a, _b, _c, _d;
|
|
866
|
+
const rect = step.anchorId ? await coachmarkAnchors.resolve(step.anchorId, {
|
|
867
|
+
shouldCancel: () => cancelled
|
|
868
|
+
}) : null;
|
|
869
|
+
if (cancelled) return;
|
|
870
|
+
if (lastShownStepIdRef.current !== step.id) {
|
|
871
|
+
lastShownStepIdRef.current = step.id;
|
|
872
|
+
(_b = (_a = callbacksRef.current).onStepShown) == null ? void 0 : _b.call(_a, step.id);
|
|
873
|
+
}
|
|
874
|
+
coachmarkOverlay.show({
|
|
875
|
+
message: step.message,
|
|
876
|
+
targetRect: rect,
|
|
877
|
+
gesture: (_c = step.gesture) != null ? _c : "none",
|
|
878
|
+
placement: (_d = step.placement) != null ? _d : "bottom",
|
|
879
|
+
onEngage: () => {
|
|
880
|
+
var _a2, _b2, _c2;
|
|
881
|
+
(_b2 = (_a2 = callbacksRef.current).onStepEngaged) == null ? void 0 : _b2.call(_a2, step.id);
|
|
882
|
+
(_c2 = step.onEngage) == null ? void 0 : _c2.call(step);
|
|
883
|
+
setActiveIndex((index) => (index != null ? index : 0) + 1);
|
|
884
|
+
},
|
|
885
|
+
onDismiss: () => {
|
|
886
|
+
var _a2, _b2;
|
|
887
|
+
(_b2 = (_a2 = callbacksRef.current).onStepDismissed) == null ? void 0 : _b2.call(_a2, step.id);
|
|
888
|
+
if (activeIndex >= steps.length - 1) {
|
|
889
|
+
finish();
|
|
890
|
+
} else {
|
|
891
|
+
setActiveIndex((index) => (index != null ? index : 0) + 1);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
});
|
|
895
|
+
})();
|
|
896
|
+
return () => {
|
|
897
|
+
cancelled = true;
|
|
898
|
+
};
|
|
899
|
+
}, [activeIndex, steps, finish, enabled]);
|
|
900
|
+
React3.useEffect(
|
|
901
|
+
() => () => {
|
|
902
|
+
if (!finishedRef.current) coachmarkOverlay.hide();
|
|
903
|
+
},
|
|
904
|
+
[]
|
|
905
|
+
);
|
|
906
|
+
};
|
|
907
|
+
|
|
908
|
+
// src/coachmarks/selectTourSteps.ts
|
|
909
|
+
var selectTourSteps = (catalog, selection) => {
|
|
910
|
+
if (!selection) return catalog;
|
|
911
|
+
const byId = new Map(catalog.map((item) => [item.id, item]));
|
|
912
|
+
const seen = /* @__PURE__ */ new Set();
|
|
913
|
+
const ordered = [];
|
|
914
|
+
for (const id of selection) {
|
|
915
|
+
if (seen.has(id)) continue;
|
|
916
|
+
seen.add(id);
|
|
917
|
+
const item = byId.get(id);
|
|
918
|
+
if (item) ordered.push(item);
|
|
919
|
+
}
|
|
920
|
+
return ordered;
|
|
921
|
+
};
|
|
922
|
+
|
|
923
|
+
exports.CoachmarkOverlayHost = CoachmarkOverlayHost;
|
|
924
|
+
exports.CoachmarkProvider = CoachmarkProvider;
|
|
925
|
+
exports.GestureHint = GestureHint;
|
|
926
|
+
exports.SpotlightOverlay = SpotlightOverlay;
|
|
927
|
+
exports.areCoachmarksEnabled = areCoachmarksEnabled;
|
|
928
|
+
exports.coachmarkAnchors = coachmarkAnchors;
|
|
929
|
+
exports.coachmarkGateKey = coachmarkGateKey;
|
|
930
|
+
exports.coachmarkOverlay = coachmarkOverlay;
|
|
931
|
+
exports.getCoachmarkStorage = getCoachmarkStorage;
|
|
932
|
+
exports.hasSeenGate = hasSeenGate;
|
|
933
|
+
exports.isCoachmarkTesting = isCoachmarkTesting;
|
|
934
|
+
exports.markSeenGate = markSeenGate;
|
|
935
|
+
exports.selectTourSteps = selectTourSteps;
|
|
936
|
+
exports.setCoachmarkStorage = setCoachmarkStorage;
|
|
937
|
+
exports.setCoachmarkTesting = setCoachmarkTesting;
|
|
938
|
+
exports.setCoachmarksEnabled = setCoachmarksEnabled;
|
|
939
|
+
exports.showcaseGateKey = showcaseGateKey;
|
|
940
|
+
exports.useCoachmarkAnchor = useCoachmarkAnchor;
|
|
941
|
+
exports.useCoachmarkOverlay = useCoachmarkOverlay;
|
|
942
|
+
exports.useCoachmarkTour = useCoachmarkTour;
|
|
943
|
+
//# sourceMappingURL=index.js.map
|
|
944
|
+
//# sourceMappingURL=index.js.map
|