@mrrocket/sdk 0.15.0 → 0.17.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/CHANGELOG.md +29 -0
- package/README.md +47 -3
- package/dist/api-client.d.ts +12 -5
- package/dist/api-client.js +12 -8
- package/dist/api-client.js.map +1 -1
- package/dist/api.d.ts +4 -1
- package/dist/api.js +2 -1
- package/dist/api.js.map +1 -1
- package/dist/batch.d.ts +6 -4
- package/dist/batch.js +3 -3
- package/dist/batch.js.map +1 -1
- package/dist/browser/mrrocket-flows.js +9 -2
- package/dist/browser/mrrocket-forms.js +1 -1
- package/dist/browser/mrrocket-messenger.js +42 -43
- package/dist/browser/mrrocket-push.js +1 -1
- package/dist/browser/mrrocket-replay.js +3 -3
- package/dist/browser/mrrocket.js +1 -1
- package/dist/client.d.ts +10 -2
- package/dist/client.js +36 -15
- package/dist/client.js.map +1 -1
- package/dist/context.d.ts +5 -1
- package/dist/context.js.map +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/errors.d.ts +5 -1
- package/dist/errors.js +8 -2
- package/dist/errors.js.map +1 -1
- package/dist/flows-ui.d.ts +1 -1
- package/dist/flows-ui.js +64 -14
- package/dist/flows-ui.js.map +1 -1
- package/dist/flows-web.d.ts +2 -0
- package/dist/flows-web.js +4 -1
- package/dist/flows-web.js.map +1 -1
- package/dist/flows.d.ts +59 -3
- package/dist/flows.js +88 -7
- package/dist/flows.js.map +1 -1
- package/dist/forms.d.ts +4 -0
- package/dist/forms.js +4 -1
- package/dist/forms.js.map +1 -1
- package/dist/identity.d.ts +42 -0
- package/dist/identity.js +24 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/messenger-ui.d.ts +12 -2
- package/dist/messenger-ui.js +58 -47
- package/dist/messenger-ui.js.map +1 -1
- package/dist/messenger.js +3 -2
- package/dist/messenger.js.map +1 -1
- package/dist/push-web.d.ts +2 -13
- package/dist/push-web.js +4 -2
- package/dist/push-web.js.map +1 -1
- package/dist/react-native/index.d.cts +105 -14
- package/dist/react-native/index.d.ts +105 -14
- package/dist/react-native/index.js +238 -55
- package/dist/react-native/index.js.map +1 -1
- package/dist/replay.d.ts +4 -1
- package/dist/replay.js +8 -2
- package/dist/replay.js.map +1 -1
- package/dist/server.d.ts +2 -0
- package/dist/server.js +2 -0
- package/dist/server.js.map +1 -1
- package/dist/styles-css.d.ts +1 -1
- package/dist/styles-css.js +40 -41
- package/dist/styles-css.js.map +1 -1
- package/dist/user-hash.d.ts +27 -0
- package/dist/user-hash.js +24 -0
- package/dist/user-hash.js.map +1 -0
- package/package.json +5 -1
|
@@ -43,6 +43,7 @@ __export(native_exports, {
|
|
|
43
43
|
useOnboarding: () => useOnboarding,
|
|
44
44
|
usePaywall: () => usePaywall,
|
|
45
45
|
usePush: () => usePush,
|
|
46
|
+
useReview: () => useReview,
|
|
46
47
|
useText: () => useText,
|
|
47
48
|
useVariant: () => useVariant
|
|
48
49
|
});
|
|
@@ -50,7 +51,7 @@ module.exports = __toCommonJS(native_exports);
|
|
|
50
51
|
var import_react3 = require("react");
|
|
51
52
|
|
|
52
53
|
// src/core.ts
|
|
53
|
-
var SDK_VERSION = "0.
|
|
54
|
+
var SDK_VERSION = "0.17.0";
|
|
54
55
|
var propOf = (person, name) => {
|
|
55
56
|
const want = name.trim().toLowerCase();
|
|
56
57
|
for (const [k, v] of Object.entries(person)) if (k.toLowerCase() === want) return v;
|
|
@@ -172,6 +173,23 @@ var textChanges = (config, distinctId, preview) => {
|
|
|
172
173
|
};
|
|
173
174
|
var newDistinctId = () => typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `ck_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;
|
|
174
175
|
|
|
176
|
+
// src/identity.ts
|
|
177
|
+
var UNSIGNED_HELP = "401: sign the user id on your server with userHash(id) from @mrrocket/sdk/next/server and pass identify(id, props, { hash }).";
|
|
178
|
+
var warnUnsigned = (status, log = (m) => console.warn(m)) => {
|
|
179
|
+
const g = globalThis;
|
|
180
|
+
if (status !== 401 || g.__mrrocketUnsigned) return;
|
|
181
|
+
g.__mrrocketUnsigned = true;
|
|
182
|
+
log(`[MRRocket] ${UNSIGNED_HELP}`);
|
|
183
|
+
};
|
|
184
|
+
var nextSigned = (signed, id, hash, log = (m) => console.warn(m)) => {
|
|
185
|
+
const h3 = typeof hash === "string" ? hash.trim().toLowerCase() : "";
|
|
186
|
+
if (/^[0-9a-f]{64}$/.test(h3)) return { id, hash: h3 };
|
|
187
|
+
if (h3) log("[MRRocket] identify: that hash is no userHash(id); not sent.");
|
|
188
|
+
return signed && signed.id === id ? signed : null;
|
|
189
|
+
};
|
|
190
|
+
var hashFor = (signed, id) => signed && id && signed.id === id ? signed.hash : void 0;
|
|
191
|
+
var hashForAll = (signed, ids) => signed && ids.some((id) => id === signed.id) && ids.every((id) => !id || id === signed.id) ? signed.hash : void 0;
|
|
192
|
+
|
|
175
193
|
// src/api-client.ts
|
|
176
194
|
var CHAT_FILE_MAX_BYTES = 10 * 1024 * 1024;
|
|
177
195
|
var CHAT_FILES_PER_MESSAGE = 4;
|
|
@@ -212,6 +230,7 @@ var call = async (url, init, signal) => {
|
|
|
212
230
|
throw new ApiError("Could not reach the server. Check your connection and try again.", 0);
|
|
213
231
|
}
|
|
214
232
|
if (!res.ok) {
|
|
233
|
+
warnUnsigned(res.status);
|
|
215
234
|
let detail = "";
|
|
216
235
|
try {
|
|
217
236
|
detail = (await res.json()).error ?? "";
|
|
@@ -223,21 +242,22 @@ var call = async (url, init, signal) => {
|
|
|
223
242
|
};
|
|
224
243
|
var createApi = (c) => {
|
|
225
244
|
const base = `${c.host}/api/sdk`;
|
|
226
|
-
const
|
|
227
|
-
const
|
|
245
|
+
const who2 = { key: c.apiKey, distinctId: c.distinctId, user: c.user, userHash: c.userHash };
|
|
246
|
+
const me = { key: c.apiKey, distinctId: c.distinctId, userHash: c.userHash };
|
|
247
|
+
const post = (path, body, signal) => call(`${base}${path}`, { method: "POST", body: JSON.stringify({ ...who2, ...body }) }, signal);
|
|
228
248
|
return {
|
|
229
|
-
list: (o, signal) => call(`${base}/feedback?${qs({
|
|
230
|
-
post: (id, signal) => call(`${base}/feedback/${encodeURIComponent(id)}?${qs(
|
|
231
|
-
roadmap: (signal) => call(`${base}/feedback/roadmap?${qs(
|
|
249
|
+
list: (o, signal) => call(`${base}/feedback?${qs({ ...me, board: o.board, sort: o.sort, q: o.q })}`, void 0, signal),
|
|
250
|
+
post: (id, signal) => call(`${base}/feedback/${encodeURIComponent(id)}?${qs(me)}`, void 0, signal),
|
|
251
|
+
roadmap: (signal) => call(`${base}/feedback/roadmap?${qs(me)}`, void 0, signal),
|
|
232
252
|
changelog: (signal) => call(`${base}/feedback/changelog?${qs({ key: c.apiKey })}`, void 0, signal),
|
|
233
253
|
submit: (p) => post("/feedback", p),
|
|
234
254
|
vote: (id) => post(`/feedback/${encodeURIComponent(id)}/vote`, {}),
|
|
235
255
|
comments: (id, signal) => call(`${base}/feedback/${encodeURIComponent(id)}/comments?${qs({ key: c.apiKey })}`, void 0, signal),
|
|
236
256
|
comment: (id, body) => post(`/feedback/${encodeURIComponent(id)}/comments`, { body }),
|
|
237
|
-
survey: (url, signal) => call(`${base}/surveys?${qs({
|
|
257
|
+
survey: (url, signal) => call(`${base}/surveys?${qs({ ...me, url })}`, void 0, signal),
|
|
238
258
|
seen: (id) => post(`/surveys/${encodeURIComponent(id)}/seen`, { user: void 0 }),
|
|
239
259
|
respond: (id, r) => post(`/surveys/${encodeURIComponent(id)}/respond`, r),
|
|
240
|
-
chat: (signal) => call(`${base}/messages?${qs(
|
|
260
|
+
chat: (signal) => call(`${base}/messages?${qs(me)}`, void 0, signal),
|
|
241
261
|
/** A message; `attachments` are upload ids from `uploadChatFile` (then the body may be empty). */
|
|
242
262
|
sendChat: (m) => post("/messages", m),
|
|
243
263
|
/** Uploads one picked file for the next message (pictures and PDFs, at most 10 MB); answers its upload id. */
|
|
@@ -245,14 +265,15 @@ var createApi = (c) => {
|
|
|
245
265
|
const form = new FormData();
|
|
246
266
|
form.append("key", c.apiKey);
|
|
247
267
|
form.append("distinctId", c.distinctId);
|
|
268
|
+
if (c.userHash) form.append("userHash", c.userHash);
|
|
248
269
|
if ("uri" in file) form.append("file", file);
|
|
249
270
|
else form.append("file", file, file.name || "file");
|
|
250
271
|
return call(`${base}/messages/attachments`, { method: "POST", body: form }, signal);
|
|
251
272
|
},
|
|
252
273
|
/** Where a sent file of this visitor's chat is read (an image's src, a PDF's link). */
|
|
253
|
-
attachmentUrl: (id) => `${base}/messages/attachments/${encodeURIComponent(id)}?${qs(
|
|
274
|
+
attachmentUrl: (id) => `${base}/messages/attachments/${encodeURIComponent(id)}?${qs(me)}`,
|
|
254
275
|
articles: (q, signal) => call(`${base}/articles?${qs({ key: c.apiKey, q })}`, void 0, signal),
|
|
255
|
-
article: (id, signal) => call(`${base}/articles/${encodeURIComponent(id)}?${qs(
|
|
276
|
+
article: (id, signal) => call(`${base}/articles/${encodeURIComponent(id)}?${qs(me)}`, void 0, signal),
|
|
256
277
|
messengerLook: (signal) => call(`${base}/messenger?${qs({ key: c.apiKey })}`, void 0, signal),
|
|
257
278
|
rateArticle: (id, helpful) => post(`/articles/${encodeURIComponent(id)}/rate`, { helpful })
|
|
258
279
|
};
|
|
@@ -274,8 +295,8 @@ var byteLength = (s) => {
|
|
|
274
295
|
}
|
|
275
296
|
return n;
|
|
276
297
|
};
|
|
277
|
-
var batchRequests = (key, events, sdk) => {
|
|
278
|
-
const head = `{"key":${JSON.stringify(key)}
|
|
298
|
+
var batchRequests = (key, events, sdk, userHash) => {
|
|
299
|
+
const head = `{"key":${JSON.stringify(key)},${userHash ? `"userHash":${JSON.stringify(userHash)},` : ""}"sdk":${JSON.stringify(sdk)},"events":[`;
|
|
279
300
|
const empty = byteLength(head) + 2;
|
|
280
301
|
const out = [];
|
|
281
302
|
let chunk = [];
|
|
@@ -317,6 +338,7 @@ var describeError = (error) => {
|
|
|
317
338
|
var CANCEL_EVENT = "$cancel_flow";
|
|
318
339
|
var ONBOARDING_EVENT = "$onboarding";
|
|
319
340
|
var INAPP_EVENT = "$inapp";
|
|
341
|
+
var REVIEW_EVENT = "$review_prompt";
|
|
320
342
|
var PLAIN_CANCEL = {
|
|
321
343
|
version: 0,
|
|
322
344
|
question: "",
|
|
@@ -336,6 +358,25 @@ var matchesTrigger = (trigger, event) => {
|
|
|
336
358
|
return t === e || SIGNUP.has(t) && SIGNUP.has(e);
|
|
337
359
|
};
|
|
338
360
|
var startsOnLoad = (trigger) => /^(first (launch|visit|open|session|login|sign ?in|app open)|app (opened|launched)|on load|every visit|always)?$/i.test(trigger.trim());
|
|
361
|
+
var isPageTrigger = (trigger) => trigger.trim().startsWith("/");
|
|
362
|
+
var manual = (trigger) => eventSlug(trigger) === "from_a_checklist";
|
|
363
|
+
var videoEmbed = (url) => {
|
|
364
|
+
const u = (url ?? "").trim();
|
|
365
|
+
if (!/^https?:\/\//i.test(u)) return null;
|
|
366
|
+
let m = u.match(/^https?:\/\/(?:www\.|m\.)?(?:youtube\.com\/(?:watch\?(?:.*&)?v=|embed\/|shorts\/|live\/)|youtu\.be\/|youtube-nocookie\.com\/embed\/)([\w-]{6,20})/i);
|
|
367
|
+
if (m) return { kind: "iframe", src: `https://www.youtube-nocookie.com/embed/${m[1]}?rel=0` };
|
|
368
|
+
m = u.match(/^https?:\/\/(?:www\.|player\.)?vimeo\.com\/(?:video\/)?(\d{4,12})/i);
|
|
369
|
+
if (m) return { kind: "iframe", src: `https://player.vimeo.com/video/${m[1]}?dnt=1` };
|
|
370
|
+
m = u.match(/^https?:\/\/(?:www\.)?loom\.com\/(?:share|embed)\/([\w-]{8,64})/i);
|
|
371
|
+
if (m) return { kind: "iframe", src: `https://www.loom.com/embed/${m[1]}` };
|
|
372
|
+
if (/\.(mp4|webm|mov|m4v)(\?|#|$)/i.test(u) || /\/media\/uploads\//.test(u)) return { kind: "file", src: u };
|
|
373
|
+
return null;
|
|
374
|
+
};
|
|
375
|
+
var mediaOf = (step) => {
|
|
376
|
+
const m = step.media;
|
|
377
|
+
if (!m || m.kind !== "image" && m.kind !== "video" || !/^https?:\/\//i.test(m.url ?? "")) return void 0;
|
|
378
|
+
return m.kind === "video" && !videoEmbed(m.url) ? void 0 : m;
|
|
379
|
+
};
|
|
339
380
|
var runId = () => `cf_${Math.random().toString(36).slice(2, 10)}${Date.now().toString(36)}`;
|
|
340
381
|
var discounted = (amount, percent) => Math.round(amount * (1 - percent / 100) * 100) / 100;
|
|
341
382
|
var money = (amount, currency) => {
|
|
@@ -383,6 +424,7 @@ var createFlows = (env) => {
|
|
|
383
424
|
const seenEvents = /* @__PURE__ */ new Set();
|
|
384
425
|
const now = () => env.now?.() ?? Date.now();
|
|
385
426
|
let timer = null;
|
|
427
|
+
let returnTo = null;
|
|
386
428
|
const flows = () => config?.onboarding ?? [];
|
|
387
429
|
const flowById = (id) => id ? flows().find((f) => f.id === id) : void 0;
|
|
388
430
|
const prog = (id) => {
|
|
@@ -520,7 +562,14 @@ var createFlows = (env) => {
|
|
|
520
562
|
shown.add(`s:${i.id}`);
|
|
521
563
|
env.track(ONBOARDING_EVENT, { flow: flow.id, step: i.id, kind: "checklist", action: "shown" });
|
|
522
564
|
}
|
|
523
|
-
return {
|
|
565
|
+
return {
|
|
566
|
+
type: "checklist",
|
|
567
|
+
flow,
|
|
568
|
+
items: items.map((s) => {
|
|
569
|
+
const tour = s.startsTour && s.startsTour !== flow.id ? flowById(s.startsTour) : void 0;
|
|
570
|
+
return { step: s, done: p.done.has(s.id), ...tour && nodesOf(tour).length ? { tour } : {} };
|
|
571
|
+
})
|
|
572
|
+
};
|
|
524
573
|
}
|
|
525
574
|
if (!shown.has(`s:${step.id}`)) {
|
|
526
575
|
shown.add(`s:${step.id}`);
|
|
@@ -554,7 +603,28 @@ var createFlows = (env) => {
|
|
|
554
603
|
}
|
|
555
604
|
const ob = onboardingView();
|
|
556
605
|
if (ob) return ob;
|
|
557
|
-
if (active && !holding)
|
|
606
|
+
if (active && !holding) {
|
|
607
|
+
const back = returnTo;
|
|
608
|
+
const ended = active;
|
|
609
|
+
returnTo = null;
|
|
610
|
+
active = null;
|
|
611
|
+
const list = back ? flowById(back.flow) : void 0;
|
|
612
|
+
if (back && list) {
|
|
613
|
+
const item = stepsIn(nodesOf(list)).find((s) => s.id === back.step);
|
|
614
|
+
const p = prog(list.id);
|
|
615
|
+
if (item && !item.event && !progress.get(ended)?.dismissed && !p.done.has(item.id)) {
|
|
616
|
+
markDone(p, item.id);
|
|
617
|
+
record({ flow: list.id, step: item.id, kind: "checklist", action: "completed" });
|
|
618
|
+
}
|
|
619
|
+
active = list.id;
|
|
620
|
+
const again = onboardingView();
|
|
621
|
+
if (again) return again;
|
|
622
|
+
if (!holding) active = null;
|
|
623
|
+
} else if (known) {
|
|
624
|
+
const after = flowById(ended)?.trigger;
|
|
625
|
+
Promise.resolve().then(() => autoStart(after));
|
|
626
|
+
}
|
|
627
|
+
}
|
|
558
628
|
const m = messages.find((x) => !handled.has(x.id));
|
|
559
629
|
return m ? { type: "message", message: m } : null;
|
|
560
630
|
};
|
|
@@ -579,10 +649,15 @@ var createFlows = (env) => {
|
|
|
579
649
|
emit();
|
|
580
650
|
return true;
|
|
581
651
|
};
|
|
582
|
-
const
|
|
652
|
+
const allowed = (f) => f.audience !== "signed_in" || !!env.personId();
|
|
653
|
+
const autoStart = (after) => {
|
|
583
654
|
if (!auto || active || !known) return;
|
|
584
655
|
if (!env.personId() && !env.storage?.()) return;
|
|
585
|
-
const
|
|
656
|
+
const path = env.path?.();
|
|
657
|
+
const same2 = after && !manual(after) ? eventSlug(after) : "";
|
|
658
|
+
const next = flows().find(
|
|
659
|
+
(f) => (isPageTrigger(f.trigger) ? path !== void 0 && pageMatches(f.trigger, path) : startsOnLoad(f.trigger) || !!same2 && eventSlug(f.trigger) === same2) && allowed(f) && nodesOf(f).length && !finished(f)
|
|
660
|
+
);
|
|
586
661
|
if (next) start(next);
|
|
587
662
|
};
|
|
588
663
|
const refresh = () => {
|
|
@@ -592,6 +667,7 @@ var createFlows = (env) => {
|
|
|
592
667
|
seenEvents.clear();
|
|
593
668
|
handled.clear();
|
|
594
669
|
active = null;
|
|
670
|
+
returnTo = null;
|
|
595
671
|
loadedFor = void 0;
|
|
596
672
|
}
|
|
597
673
|
progressFor = pid;
|
|
@@ -630,9 +706,9 @@ var createFlows = (env) => {
|
|
|
630
706
|
};
|
|
631
707
|
const offerAt = (c) => c.round > 1 ? { round: 2 } : c.rule >= 0 ? { rule: c.rule } : {};
|
|
632
708
|
const toOffer = (c) => {
|
|
633
|
-
const
|
|
709
|
+
const who2 = { answer: c.answer, plan: c.options.plan, months: monthsSince(c.options.customerSince, env.now?.()) };
|
|
634
710
|
const rules = c.flow.rules?.[c.reason] ?? [];
|
|
635
|
-
c.rule = rules.findIndex((r) => ruleMatches(r,
|
|
711
|
+
c.rule = rules.findIndex((r) => ruleMatches(r, who2));
|
|
636
712
|
c.offer = c.rule < 0 ? c.flow.offers[c.reason] : rules[c.rule].offer ?? void 0;
|
|
637
713
|
c.round = 1;
|
|
638
714
|
c.screen = c.offer ? "offer" : "confirm";
|
|
@@ -701,6 +777,7 @@ var createFlows = (env) => {
|
|
|
701
777
|
},
|
|
702
778
|
refresh,
|
|
703
779
|
onEvent: (name) => {
|
|
780
|
+
if (name === "$pageview") autoStart();
|
|
704
781
|
if (!name || name.startsWith("$")) return;
|
|
705
782
|
const slug = eventSlug(name);
|
|
706
783
|
const fresh = !!slug && !seenEvents.has(slug);
|
|
@@ -721,12 +798,12 @@ var createFlows = (env) => {
|
|
|
721
798
|
emit();
|
|
722
799
|
} else if (fresh) save();
|
|
723
800
|
if (!auto || !env.personId() && !env.storage?.()) return;
|
|
724
|
-
const next = flows().find((f) => nodesOf(f).length && matchesTrigger(f.trigger, name) && !finished(f));
|
|
801
|
+
const next = flows().find((f) => nodesOf(f).length && !manual(f.trigger) && matchesTrigger(f.trigger, name) && allowed(f) && !finished(f));
|
|
725
802
|
if (next) start(next);
|
|
726
803
|
},
|
|
727
804
|
openCancelFlow,
|
|
728
805
|
startOnboarding: (id) => {
|
|
729
|
-
const f = id ? flowById(id) : flows().find((x) => nodesOf(x).length && !finished(x));
|
|
806
|
+
const f = id ? flowById(id) : flows().find((x) => nodesOf(x).length && !manual(x.trigger) && !finished(x));
|
|
730
807
|
if (!f || !nodesOf(f).length) return false;
|
|
731
808
|
const p = prog(f.id);
|
|
732
809
|
p.dismissed = false;
|
|
@@ -822,6 +899,8 @@ var createFlows = (env) => {
|
|
|
822
899
|
env.track(ONBOARDING_EVENT, { flow: v.flow.id, step: v.step.id, kind: v.step.kind, action: "completed" });
|
|
823
900
|
save();
|
|
824
901
|
emit();
|
|
902
|
+
const url = v.step.kind === "modal" ? safeUrl(v.step.url) : void 0;
|
|
903
|
+
if (url) env.openUrl?.(url);
|
|
825
904
|
},
|
|
826
905
|
check: (stepId) => {
|
|
827
906
|
const v = onboardingView();
|
|
@@ -838,9 +917,26 @@ var createFlows = (env) => {
|
|
|
838
917
|
if (!v || v.type !== "step" && v.type !== "checklist") return;
|
|
839
918
|
prog(v.flow.id).dismissed = true;
|
|
840
919
|
env.track(ONBOARDING_EVENT, { flow: v.flow.id, ...v.type === "step" ? { step: v.step.id } : {}, action: "dismissed" });
|
|
841
|
-
active = null;
|
|
920
|
+
active = returnTo && returnTo.flow !== v.flow.id && flowById(returnTo.flow) ? returnTo.flow : null;
|
|
921
|
+
returnTo = null;
|
|
842
922
|
save();
|
|
843
923
|
emit();
|
|
924
|
+
},
|
|
925
|
+
startTour: (stepId) => {
|
|
926
|
+
const v = onboardingView();
|
|
927
|
+
if (v?.type !== "checklist") return false;
|
|
928
|
+
const tour = v.items.find((i) => i.step.id === stepId)?.tour;
|
|
929
|
+
if (!tour) return false;
|
|
930
|
+
const p = prog(tour.id);
|
|
931
|
+
p.done.clear();
|
|
932
|
+
p.at.clear();
|
|
933
|
+
p.dismissed = false;
|
|
934
|
+
returnTo = { flow: v.flow.id, step: stepId };
|
|
935
|
+
active = tour.id;
|
|
936
|
+
env.track(ONBOARDING_EVENT, { flow: tour.id, action: "started", from: v.flow.id });
|
|
937
|
+
save();
|
|
938
|
+
emit();
|
|
939
|
+
return true;
|
|
844
940
|
}
|
|
845
941
|
},
|
|
846
942
|
message: {
|
|
@@ -1047,7 +1143,7 @@ var createRecorder = (deps, st) => {
|
|
|
1047
1143
|
const batch = frames.splice(0, frames.length);
|
|
1048
1144
|
const ms = marks.splice(0, marks.length);
|
|
1049
1145
|
const size = deps.screen();
|
|
1050
|
-
const { distinctId, identified } = st.who();
|
|
1146
|
+
const { distinctId, identified, userHash } = st.who();
|
|
1051
1147
|
const body = JSON.stringify({
|
|
1052
1148
|
key: st.apiKey,
|
|
1053
1149
|
consent: { replay: true },
|
|
@@ -1059,11 +1155,12 @@ var createRecorder = (deps, st) => {
|
|
|
1059
1155
|
meta: { first: batch[0].t, last: batch[batch.length - 1].t, vw: size ? Math.round(size.w) : null, vh: size ? Math.round(size.h) : null, marks: ms },
|
|
1060
1156
|
distinctId,
|
|
1061
1157
|
identified,
|
|
1158
|
+
...userHash ? { userHash } : {},
|
|
1062
1159
|
trigger: r.trigger,
|
|
1063
1160
|
app: st.app
|
|
1064
1161
|
});
|
|
1065
1162
|
const status = await deps.send(body);
|
|
1066
|
-
if (status === 403 || status === 409 || status === 413) r.stopped = true;
|
|
1163
|
+
if (status === 401 || status === 403 || status === 409 || status === 413) r.stopped = true;
|
|
1067
1164
|
};
|
|
1068
1165
|
const tick = async () => {
|
|
1069
1166
|
if (busy || !st.allowed() || !st.config()) return;
|
|
@@ -1223,6 +1320,7 @@ var createClient = (options, deps) => {
|
|
|
1223
1320
|
let installId = null;
|
|
1224
1321
|
let memoryId = newDistinctId();
|
|
1225
1322
|
let identified;
|
|
1323
|
+
let signed = null;
|
|
1226
1324
|
let person;
|
|
1227
1325
|
let config = null;
|
|
1228
1326
|
let queue = [];
|
|
@@ -1257,6 +1355,13 @@ var createClient = (options, deps) => {
|
|
|
1257
1355
|
const errorQueue = [];
|
|
1258
1356
|
const errorsSeen = /* @__PURE__ */ new Set();
|
|
1259
1357
|
let errorsSent = 0;
|
|
1358
|
+
const fetchInApp = async (id) => {
|
|
1359
|
+
if (!apiKey) return null;
|
|
1360
|
+
const hash = hashOf(id);
|
|
1361
|
+
const r = await deps.fetch(`${host}/api/sdk/inapp?key=${encodeURIComponent(apiKey)}&distinctId=${encodeURIComponent(id)}${hash ? `&userHash=${hash}` : ""}`, { method: "GET" });
|
|
1362
|
+
warnUnsigned(r.status, deps.log);
|
|
1363
|
+
return r.ok ? await r.json() : null;
|
|
1364
|
+
};
|
|
1260
1365
|
const paywall = createPaywallHost({ config: () => config, distinctId: () => distinctId(), track: (event, props) => record(event, props) });
|
|
1261
1366
|
const flows = createFlows({
|
|
1262
1367
|
track: (event, props) => record(event, props),
|
|
@@ -1267,11 +1372,7 @@ var createClient = (options, deps) => {
|
|
|
1267
1372
|
set: (k, v) => void set(k, v)
|
|
1268
1373
|
} : null,
|
|
1269
1374
|
openUrl: deps.openUrl,
|
|
1270
|
-
fetchInApp:
|
|
1271
|
-
if (!apiKey) return null;
|
|
1272
|
-
const r = await deps.fetch(`${host}/api/sdk/inapp?key=${encodeURIComponent(apiKey)}&distinctId=${encodeURIComponent(id)}`, { method: "GET" });
|
|
1273
|
-
return r.ok ? await r.json() : null;
|
|
1274
|
-
}
|
|
1375
|
+
fetchInApp: (id) => fetchInApp(id)
|
|
1275
1376
|
});
|
|
1276
1377
|
const refreshFlows = () => {
|
|
1277
1378
|
if (!isReady || closed) return;
|
|
@@ -1334,6 +1435,7 @@ var createClient = (options, deps) => {
|
|
|
1334
1435
|
};
|
|
1335
1436
|
const distinctId = () => identified ?? (consent === "persistent" && installId ? installId : memoryId);
|
|
1336
1437
|
const sendId = () => consent === "persistent" ? distinctId() : identified;
|
|
1438
|
+
const hashOf = (id) => hashFor(signed, id);
|
|
1337
1439
|
const touchSession = () => {
|
|
1338
1440
|
const t = now();
|
|
1339
1441
|
if (!session || t - session.last >= SESSION_TIMEOUT_MS) session = { id: newDistinctId(), last: t };
|
|
@@ -1371,6 +1473,7 @@ var createClient = (options, deps) => {
|
|
|
1371
1473
|
const timer = ctl ? setTimeoutFn(() => ctl.abort(), REQUEST_TIMEOUT_MS) : null;
|
|
1372
1474
|
try {
|
|
1373
1475
|
const r = await deps.fetch(`${host}${path}`, { method: "POST", headers: { "content-type": "application/json" }, body, signal: ctl?.signal });
|
|
1476
|
+
warnUnsigned(r.status, deps.log);
|
|
1374
1477
|
return r.status;
|
|
1375
1478
|
} catch {
|
|
1376
1479
|
return 0;
|
|
@@ -1382,7 +1485,7 @@ var createClient = (options, deps) => {
|
|
|
1382
1485
|
if (!queue.length || consent === "off" || !apiKey) return;
|
|
1383
1486
|
if (now() < backoffUntil) return;
|
|
1384
1487
|
const snapshot = queue.slice();
|
|
1385
|
-
const requests = batchRequests(apiKey, snapshot, sdk);
|
|
1488
|
+
const requests = batchRequests(apiKey, snapshot, sdk, hashOf(sendId()));
|
|
1386
1489
|
const included = new Set(requests.flatMap((r) => r.events));
|
|
1387
1490
|
const done = /* @__PURE__ */ new Set();
|
|
1388
1491
|
for (const r of requests) {
|
|
@@ -1393,7 +1496,7 @@ var createClient = (options, deps) => {
|
|
|
1393
1496
|
backoffUntil = 0;
|
|
1394
1497
|
} else if (status >= 400 && status < 500 && status !== 408 && status !== 429) {
|
|
1395
1498
|
r.events.forEach((e) => done.add(e));
|
|
1396
|
-
warn(`Events were rejected with ${status}. Check the public key (MRRocket \u203A Settings \u203A Products) and the host.`);
|
|
1499
|
+
if (status !== 401) warn(`Events were rejected with ${status}. Check the public key (MRRocket \u203A Settings \u203A Products) and the host.`);
|
|
1397
1500
|
} else {
|
|
1398
1501
|
failures++;
|
|
1399
1502
|
backoffUntil = now() + Math.min(MAX_BACKOFF_MS, 1e3 * 2 ** failures);
|
|
@@ -1432,8 +1535,9 @@ var createClient = (options, deps) => {
|
|
|
1432
1535
|
}
|
|
1433
1536
|
while (errorQueue.length) {
|
|
1434
1537
|
const batch = errorQueue.splice(0, 4);
|
|
1435
|
-
const
|
|
1436
|
-
|
|
1538
|
+
const userHash = hashForAll(signed, batch.map((r) => r.distinctId));
|
|
1539
|
+
const status = await post("/api/sdk/errors", JSON.stringify({ key: apiKey, ...userHash ? { userHash } : {}, sdk, errors: batch }));
|
|
1540
|
+
if (status >= 400 && status < 500 && status !== 429 && status !== 401) warn(`Error reports were rejected with ${status}. Check the public key and the host.`);
|
|
1437
1541
|
}
|
|
1438
1542
|
};
|
|
1439
1543
|
const capture = (error, mechanism, fatal = false) => {
|
|
@@ -1444,7 +1548,7 @@ var createClient = (options, deps) => {
|
|
|
1444
1548
|
if (errorsSeen.has(mark)) return;
|
|
1445
1549
|
errorsSeen.add(mark);
|
|
1446
1550
|
errorsSent++;
|
|
1447
|
-
const
|
|
1551
|
+
const who2 = sendId();
|
|
1448
1552
|
errorQueue.push({
|
|
1449
1553
|
...d,
|
|
1450
1554
|
mechanism,
|
|
@@ -1453,7 +1557,7 @@ var createClient = (options, deps) => {
|
|
|
1453
1557
|
ts: now(),
|
|
1454
1558
|
crumbs: crumbs.slice(),
|
|
1455
1559
|
...release ? { release } : {},
|
|
1456
|
-
...
|
|
1560
|
+
...who2 ? { distinctId: who2 } : {},
|
|
1457
1561
|
...session ? { sessionId: session.id } : {}
|
|
1458
1562
|
});
|
|
1459
1563
|
recorder?.mark({ kind: "error", path: currentScreen ?? "/", label: clip2(d.message, 120) });
|
|
@@ -1491,7 +1595,7 @@ var createClient = (options, deps) => {
|
|
|
1491
1595
|
allowed: () => replayOn && loaded && !closed && consent !== "off" && !!apiKey,
|
|
1492
1596
|
config: () => config?.replay,
|
|
1493
1597
|
session: () => session?.id ?? touchSession(),
|
|
1494
|
-
who: () => ({ distinctId: sendId(), identified: !!identified }),
|
|
1598
|
+
who: () => ({ distinctId: sendId(), identified: !!identified, userHash: hashOf(sendId()) }),
|
|
1495
1599
|
apiKey,
|
|
1496
1600
|
app: context
|
|
1497
1601
|
}
|
|
@@ -1541,6 +1645,7 @@ var createClient = (options, deps) => {
|
|
|
1541
1645
|
};
|
|
1542
1646
|
let pushToken = null;
|
|
1543
1647
|
let pushFor;
|
|
1648
|
+
let pushHash;
|
|
1544
1649
|
let pushOff = null;
|
|
1545
1650
|
const opened = /* @__PURE__ */ new Set();
|
|
1546
1651
|
const onPushOpen = (data) => {
|
|
@@ -1567,19 +1672,21 @@ var createClient = (options, deps) => {
|
|
|
1567
1672
|
await loadedNow();
|
|
1568
1673
|
if (!apiKey || consent === "off") return { status: "error", reason: !apiKey ? "No public key." : "The consent is off." };
|
|
1569
1674
|
if (!identified) return { status: "not-signed-in" };
|
|
1570
|
-
const
|
|
1675
|
+
const who2 = identified;
|
|
1676
|
+
const userHash = hashOf(who2);
|
|
1571
1677
|
try {
|
|
1572
1678
|
const permission = await deps.push.permission(o.ask !== false);
|
|
1573
1679
|
if (permission !== "granted") return { status: permission === "denied" ? "denied" : "not-asked" };
|
|
1574
1680
|
const token = await deps.push.token(o.projectId);
|
|
1575
1681
|
if (!token) return { status: "unavailable", reason: "This device has no push token (a simulator, or no EAS project id)." };
|
|
1576
|
-
const status = await post("/api/sdk/push", JSON.stringify({ key: apiKey, distinctId:
|
|
1682
|
+
const status = await post("/api/sdk/push", JSON.stringify({ key: apiKey, distinctId: who2, userHash, platform: "expo", token, app: context }));
|
|
1577
1683
|
if (status < 200 || status >= 300) {
|
|
1578
1684
|
warn(`Push registration was rejected with ${status || "no answer"}. Check the public key and the host.`);
|
|
1579
1685
|
return { status: "error", reason: `MRRocket answered ${status || "nothing"}.` };
|
|
1580
1686
|
}
|
|
1581
1687
|
pushToken = token;
|
|
1582
|
-
pushFor =
|
|
1688
|
+
pushFor = who2;
|
|
1689
|
+
pushHash = userHash;
|
|
1583
1690
|
listenForOpens();
|
|
1584
1691
|
debug("Registered for push.");
|
|
1585
1692
|
return { status: "registered", token };
|
|
@@ -1591,9 +1698,28 @@ var createClient = (options, deps) => {
|
|
|
1591
1698
|
const token = pushToken;
|
|
1592
1699
|
if (!token || !apiKey) return false;
|
|
1593
1700
|
pushToken = null;
|
|
1594
|
-
const status = await post("/api/sdk/push", JSON.stringify({ key: apiKey, distinctId: pushFor, platform: "expo", token, remove: true }));
|
|
1701
|
+
const status = await post("/api/sdk/push", JSON.stringify({ key: apiKey, distinctId: pushFor, userHash: pushHash, platform: "expo", token, remove: true }));
|
|
1595
1702
|
return status >= 200 && status < 300;
|
|
1596
1703
|
};
|
|
1704
|
+
let reviewing = false;
|
|
1705
|
+
const requestReview = async () => {
|
|
1706
|
+
if (!deps.storeReview) return "unavailable";
|
|
1707
|
+
await loadedNow();
|
|
1708
|
+
if (!apiKey || consent === "off") return "not-now";
|
|
1709
|
+
if (!identified) return "not-signed-in";
|
|
1710
|
+
if (reviewing) return "not-now";
|
|
1711
|
+
reviewing = true;
|
|
1712
|
+
try {
|
|
1713
|
+
const ask = (await fetchInApp(identified).catch(() => null))?.review;
|
|
1714
|
+
if (!ask) return "not-now";
|
|
1715
|
+
const asked = await deps.storeReview.request().catch(() => false);
|
|
1716
|
+
record(REVIEW_EVENT, { campaign: ask.campaign, store: ask.store, action: asked ? "requested" : "unavailable" });
|
|
1717
|
+
void flush();
|
|
1718
|
+
return asked ? "requested" : "unavailable";
|
|
1719
|
+
} finally {
|
|
1720
|
+
reviewing = false;
|
|
1721
|
+
}
|
|
1722
|
+
};
|
|
1597
1723
|
let resolveReady = () => {
|
|
1598
1724
|
};
|
|
1599
1725
|
const ready = new Promise((r) => {
|
|
@@ -1723,13 +1849,14 @@ var createClient = (options, deps) => {
|
|
|
1723
1849
|
record("$screen", { ...props, path, screen: name, ...previous ? { previous } : {} });
|
|
1724
1850
|
recorder?.mark({ kind: "pageview", path });
|
|
1725
1851
|
},
|
|
1726
|
-
identify: (id, props) => {
|
|
1852
|
+
identify: (id, props, options2) => {
|
|
1727
1853
|
if (typeof id !== "string" || !id) return;
|
|
1728
1854
|
if (!loaded) {
|
|
1729
|
-
early.push(() => client.identify(id, props));
|
|
1855
|
+
early.push(() => client.identify(id, props, options2));
|
|
1730
1856
|
return;
|
|
1731
1857
|
}
|
|
1732
1858
|
const previous = sendId();
|
|
1859
|
+
signed = nextSigned(signed, id, options2?.hash, deps.log);
|
|
1733
1860
|
identified = id;
|
|
1734
1861
|
person = { ...props, id };
|
|
1735
1862
|
record("$identify", { ...props, id, ...previous && previous !== id ? { previousId: previous } : {} });
|
|
@@ -1743,7 +1870,9 @@ var createClient = (options, deps) => {
|
|
|
1743
1870
|
return;
|
|
1744
1871
|
}
|
|
1745
1872
|
if (pushToken) void unregisterForPush();
|
|
1873
|
+
void flush();
|
|
1746
1874
|
identified = void 0;
|
|
1875
|
+
signed = null;
|
|
1747
1876
|
person = void 0;
|
|
1748
1877
|
session = null;
|
|
1749
1878
|
utm = null;
|
|
@@ -1822,6 +1951,7 @@ var createClient = (options, deps) => {
|
|
|
1822
1951
|
paywall,
|
|
1823
1952
|
registerForPush,
|
|
1824
1953
|
unregisterForPush,
|
|
1954
|
+
requestReview,
|
|
1825
1955
|
flows,
|
|
1826
1956
|
flush,
|
|
1827
1957
|
ready,
|
|
@@ -1837,7 +1967,7 @@ var createClient = (options, deps) => {
|
|
|
1837
1967
|
if (!apiKey || consent === "off") return null;
|
|
1838
1968
|
const known = client.getUser();
|
|
1839
1969
|
const user = as?.name || as?.email ? { ...known, ...as.name ? { name: as.name } : {}, ...as.email ? { email: as.email } : {} } : known;
|
|
1840
|
-
return createApi({ host, apiKey, distinctId: distinctId(), user });
|
|
1970
|
+
return createApi({ host, apiKey, distinctId: distinctId(), user, userHash: hashOf(distinctId()) });
|
|
1841
1971
|
},
|
|
1842
1972
|
isSurveyDone: (id) => surveysDone.has(id),
|
|
1843
1973
|
markSurveyDone: (id) => {
|
|
@@ -2109,6 +2239,22 @@ var MRRocketFlows = ({ flows, paywall, appearance = {} }) => {
|
|
|
2109
2239
|
];
|
|
2110
2240
|
return sheet([top, ...body, error], flows.cancel.close);
|
|
2111
2241
|
}
|
|
2242
|
+
const media = (s) => {
|
|
2243
|
+
const m2 = mediaOf(s);
|
|
2244
|
+
if (!m2) return null;
|
|
2245
|
+
if (m2.kind === "image")
|
|
2246
|
+
return RN.Image ? (0, import_react.createElement)(RN.Image, {
|
|
2247
|
+
key: "media",
|
|
2248
|
+
source: { uri: m2.url },
|
|
2249
|
+
accessibilityLabel: m2.alt,
|
|
2250
|
+
resizeMode: "cover",
|
|
2251
|
+
style: { width: "100%", aspectRatio: 16 / 9, borderRadius: 10, backgroundColor: p.soft }
|
|
2252
|
+
}) : null;
|
|
2253
|
+
const v2 = videoEmbed(m2.url);
|
|
2254
|
+
const open = v2?.kind === "file" ? v2.src : m2.url;
|
|
2255
|
+
return RN.Linking ? btn("\u25B6 Watch the video", "secondary", () => void RN.Linking?.openURL(open).catch(() => {
|
|
2256
|
+
})) : null;
|
|
2257
|
+
};
|
|
2112
2258
|
if (v.type === "step")
|
|
2113
2259
|
return sheet(
|
|
2114
2260
|
[
|
|
@@ -2118,6 +2264,7 @@ var MRRocketFlows = ({ flows, paywall, appearance = {} }) => {
|
|
|
2118
2264
|
v.total > 1 ? muted(`Step ${v.index + 1} of ${v.total}`) : null,
|
|
2119
2265
|
close("Close the tour", flows.onboarding.dismiss)
|
|
2120
2266
|
),
|
|
2267
|
+
media(v.step),
|
|
2121
2268
|
title(v.step.title),
|
|
2122
2269
|
v.step.body ? muted(v.step.body, { fontSize: 15 }) : null,
|
|
2123
2270
|
btn(v.step.button || (v.last ? "Done" : "Next"), "primary", flows.onboarding.next)
|
|
@@ -2138,13 +2285,14 @@ var MRRocketFlows = ({ flows, paywall, appearance = {} }) => {
|
|
|
2138
2285
|
),
|
|
2139
2286
|
muted(`${done} of ${v.items.length} done`),
|
|
2140
2287
|
...v.items.map(
|
|
2141
|
-
({ step, done: d }) => (0, import_react.createElement)(
|
|
2288
|
+
({ step, done: d, tour }) => (0, import_react.createElement)(
|
|
2142
2289
|
RN.Pressable,
|
|
2143
2290
|
{
|
|
2144
2291
|
key: step.id,
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2292
|
+
// 0.17.0: an item that starts a tour opens it; finishing the tour ticks it.
|
|
2293
|
+
disabled: d || !!step.event && !tour,
|
|
2294
|
+
onPress: () => tour ? flows.onboarding.startTour(step.id) : flows.onboarding.check(step.id),
|
|
2295
|
+
accessibilityRole: tour && !d ? "button" : "checkbox",
|
|
2148
2296
|
accessibilityState: { checked: d },
|
|
2149
2297
|
style: { flexDirection: "row", gap: 10, alignItems: "flex-start", paddingVertical: 6 }
|
|
2150
2298
|
},
|
|
@@ -2227,6 +2375,12 @@ try {
|
|
|
2227
2375
|
} catch {
|
|
2228
2376
|
expoLocalization = null;
|
|
2229
2377
|
}
|
|
2378
|
+
var expoStoreReview = null;
|
|
2379
|
+
try {
|
|
2380
|
+
expoStoreReview = require("expo-store-review");
|
|
2381
|
+
} catch {
|
|
2382
|
+
expoStoreReview = null;
|
|
2383
|
+
}
|
|
2230
2384
|
var viewShot = null;
|
|
2231
2385
|
try {
|
|
2232
2386
|
viewShot = require("react-native-view-shot");
|
|
@@ -2330,6 +2484,17 @@ var recorderDeps = () => {
|
|
|
2330
2484
|
masks: measureMasks
|
|
2331
2485
|
};
|
|
2332
2486
|
};
|
|
2487
|
+
var storeReview = () => {
|
|
2488
|
+
const m = expoStoreReview;
|
|
2489
|
+
if (!m?.requestReview) return null;
|
|
2490
|
+
return {
|
|
2491
|
+
request: async () => {
|
|
2492
|
+
if (m.isAvailableAsync && !await m.isAvailableAsync()) return false;
|
|
2493
|
+
await m.requestReview();
|
|
2494
|
+
return true;
|
|
2495
|
+
}
|
|
2496
|
+
};
|
|
2497
|
+
};
|
|
2333
2498
|
var nativeDeps = (overrides = {}) => ({
|
|
2334
2499
|
storage: overrides.storage !== void 0 ? overrides.storage : asyncStorage(),
|
|
2335
2500
|
fetch: ((url, init) => fetch(url, init)),
|
|
@@ -2344,6 +2509,7 @@ var nativeDeps = (overrides = {}) => ({
|
|
|
2344
2509
|
initialUrl: () => RN2.Linking.getInitialURL(),
|
|
2345
2510
|
openUrl: (url) => void RN2.Linking.openURL(url).catch(() => void 0),
|
|
2346
2511
|
push: expoPush(),
|
|
2512
|
+
storeReview: storeReview(),
|
|
2347
2513
|
recorder: recorderDeps()
|
|
2348
2514
|
});
|
|
2349
2515
|
var optionalModules = () => ({
|
|
@@ -2352,6 +2518,7 @@ var optionalModules = () => ({
|
|
|
2352
2518
|
expoApplication: !!expoApplication,
|
|
2353
2519
|
expoLocalization: !!expoLocalization,
|
|
2354
2520
|
expoNotifications: !!expoNotifications,
|
|
2521
|
+
expoStoreReview: !!expoStoreReview?.requestReview,
|
|
2355
2522
|
viewShot: !!viewShot?.captureScreen
|
|
2356
2523
|
});
|
|
2357
2524
|
|
|
@@ -2597,7 +2764,7 @@ var SurveySheet = ({ survey, api, p, onDone }) => {
|
|
|
2597
2764
|
var MRRocketSurveys = ({ client, appearance }) => {
|
|
2598
2765
|
const p = usePalette(appearance);
|
|
2599
2766
|
const [survey, setSurvey] = (0, import_react2.useState)(null);
|
|
2600
|
-
const [
|
|
2767
|
+
const [who2, setWho] = (0, import_react2.useState)(() => client.getDistinctId());
|
|
2601
2768
|
(0, import_react2.useEffect)(() => client.subscribe(() => setWho(client.getDistinctId())), [client]);
|
|
2602
2769
|
(0, import_react2.useEffect)(() => {
|
|
2603
2770
|
let stop = false;
|
|
@@ -2618,7 +2785,7 @@ var MRRocketSurveys = ({ client, appearance }) => {
|
|
|
2618
2785
|
clearTimeout(timer);
|
|
2619
2786
|
ctrl.abort();
|
|
2620
2787
|
};
|
|
2621
|
-
}, [client,
|
|
2788
|
+
}, [client, who2]);
|
|
2622
2789
|
const done = (0, import_react2.useCallback)(() => {
|
|
2623
2790
|
setSurvey((cur) => {
|
|
2624
2791
|
if (cur) client.markSurveyDone(cur.s.id);
|
|
@@ -2740,6 +2907,7 @@ var POLL_IDLE_MS = 3e4;
|
|
|
2740
2907
|
var WAITING_FOR_MS = 10 * 6e4;
|
|
2741
2908
|
var EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/;
|
|
2742
2909
|
var whoLabel = { visitor: "You", team: "Team", ai: "AI agent", auto: "Team" };
|
|
2910
|
+
var who = (from, agentName) => from === "ai" && agentName?.trim() || whoLabel[from];
|
|
2743
2911
|
var msgView = "home";
|
|
2744
2912
|
var messengerOpen = opener();
|
|
2745
2913
|
var openAt = (v = "home") => {
|
|
@@ -3124,7 +3292,7 @@ var MessengerSheet = ({
|
|
|
3124
3292
|
k.text(`\u{1F4C4} ${a.title}`, { fontSize: 13 })
|
|
3125
3293
|
)
|
|
3126
3294
|
),
|
|
3127
|
-
m.at ? k.muted(`${mine ? "" : `${
|
|
3295
|
+
m.at ? k.muted(`${mine ? "" : `${who(m.from, o.agentName)} \xB7 `}${isPending ? "Sending\u2026" : timeAgo(m.at)}`, { fontSize: 11, textAlign: mine ? "right" : "left", paddingHorizontal: 6 }) : null
|
|
3128
3296
|
);
|
|
3129
3297
|
};
|
|
3130
3298
|
const row = (a) => (0, import_react2.createElement)(
|
|
@@ -3182,7 +3350,7 @@ var MessengerSheet = ({
|
|
|
3182
3350
|
RN3.View,
|
|
3183
3351
|
{ key: "r", style: { flexDirection: "row", alignItems: "center", gap: 10 } },
|
|
3184
3352
|
title ? teamMark(title, alpha(p.accent, 0.16), p.accent, 26) : null,
|
|
3185
|
-
(0, import_react2.createElement)(RN3.View, { style: { flex: 1, gap: 1 } }, (0, import_react2.createElement)(RN3.Text, { numberOfLines: 1, style: { color: hu.fg, fontSize: 14, fontWeight: "500" } }, last.body || (last.attachments?.length ? `Sent ${last.attachments.length === 1 ? last.attachments[0].name : `${last.attachments.length} files`}` : "")), k.muted(`${last.from === "visitor" ? "You" :
|
|
3353
|
+
(0, import_react2.createElement)(RN3.View, { style: { flex: 1, gap: 1 } }, (0, import_react2.createElement)(RN3.Text, { numberOfLines: 1, style: { color: hu.fg, fontSize: 14, fontWeight: "500" } }, last.body || (last.attachments?.length ? `Sent ${last.attachments.length === 1 ? last.attachments[0].name : `${last.attachments.length} files`}` : "")), k.muted(`${last.from === "visitor" ? "You" : who(last.from, o.agentName)} \xB7 ${timeAgo(last.at)}`, { fontSize: 11.5 })),
|
|
3186
3354
|
chat.unseen.length ? (0, import_react2.createElement)(RN3.View, { style: { width: 8, height: 8, borderRadius: 4, backgroundColor: p.danger } }) : glyph("next", p.muted, 14)
|
|
3187
3355
|
)
|
|
3188
3356
|
], { paddingVertical: 10, paddingHorizontal: 12 })
|
|
@@ -3217,7 +3385,6 @@ var MessengerSheet = ({
|
|
|
3217
3385
|
RN3.View,
|
|
3218
3386
|
{ style: { paddingHorizontal: 12, paddingTop: 8, paddingBottom: 6, gap: 8 } },
|
|
3219
3387
|
chat.error ? k.text(chat.error, { color: p.danger, fontSize: 12 }) : null,
|
|
3220
|
-
needEmail ? k.field({ value: email, onChange: setEmail, placeholder: "Your email (optional), so we can follow up", email: true, max: 200, style: { borderWidth: emailOk ? 0 : 1, borderColor: p.danger, borderRadius: r.field, paddingHorizontal: 16, backgroundColor: hu.surface, ...surf } }) : null,
|
|
3221
3388
|
// Photos picked for the next message, each with a way to take it out again.
|
|
3222
3389
|
photos.length ? (0, import_react2.createElement)(
|
|
3223
3390
|
RN3.View,
|
|
@@ -3245,6 +3412,15 @@ var MessengerSheet = ({
|
|
|
3245
3412
|
(0, import_react2.createElement)(
|
|
3246
3413
|
RN3.View,
|
|
3247
3414
|
{ style: { gap: 2, borderRadius: 20, paddingTop: 10, paddingBottom: 6, paddingRight: 6, paddingLeft: 16, backgroundColor: hu.surface, ...surf } },
|
|
3415
|
+
// The optional email: the box's first line, under a hairline the message follows.
|
|
3416
|
+
needEmail ? k.field({
|
|
3417
|
+
value: email,
|
|
3418
|
+
onChange: setEmail,
|
|
3419
|
+
placeholder: "Email for our reply (optional)",
|
|
3420
|
+
email: true,
|
|
3421
|
+
max: 200,
|
|
3422
|
+
style: { minHeight: 0, marginRight: 10, marginBottom: 4, paddingHorizontal: 0, paddingTop: 0, paddingBottom: 8, borderWidth: 0, borderRadius: 0, borderBottomWidth: 1, borderBottomColor: emailOk ? p.line : p.danger, backgroundColor: "transparent", fontSize: 13.5 }
|
|
3423
|
+
}) : null,
|
|
3248
3424
|
k.field({ value: body, onChange: setBody, placeholder: "Write a message\u2026", multiline: true, max: 4e3, style: { minHeight: 28, maxHeight: 120, borderWidth: 0, paddingHorizontal: 0, paddingVertical: 4, backgroundColor: "transparent" } }),
|
|
3249
3425
|
(0, import_react2.createElement)(
|
|
3250
3426
|
RN3.View,
|
|
@@ -3296,11 +3472,13 @@ var MessengerSheet = ({
|
|
|
3296
3472
|
key: v,
|
|
3297
3473
|
onPress: () => setView(v),
|
|
3298
3474
|
accessibilityRole: "tab",
|
|
3475
|
+
accessibilityLabel: name,
|
|
3299
3476
|
accessibilityState: { selected: on },
|
|
3300
|
-
|
|
3477
|
+
// Only the open tab carries its name; the others are icons, so the bar fits a narrow phone.
|
|
3478
|
+
style: { ...on ? { flex: 1 } : { width: 48 }, flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 6, height: 36, borderRadius: 18, ...on ? { backgroundColor: hu.segment, ...segLift } : {} }
|
|
3301
3479
|
},
|
|
3302
3480
|
glyph(icon, on ? hu.fg : hu.muted, 17),
|
|
3303
|
-
k.text(name, { fontSize: 13.5, fontWeight: "500", color:
|
|
3481
|
+
on ? k.text(name, { fontSize: 13.5, fontWeight: "500", color: hu.fg }) : null,
|
|
3304
3482
|
badge ? (0, import_react2.createElement)(RN3.View, { style: { position: "absolute", top: 7, right: 12, width: 8, height: 8, borderRadius: 4, backgroundColor: p.danger } }) : null
|
|
3305
3483
|
);
|
|
3306
3484
|
};
|
|
@@ -3314,7 +3492,7 @@ var MessengerSheet = ({
|
|
|
3314
3492
|
const head = (0, import_react2.createElement)(
|
|
3315
3493
|
RN3.View,
|
|
3316
3494
|
{ key: "head", style: { flexDirection: "row", alignItems: "center", gap: 8, paddingLeft: 12, paddingRight: 12, paddingTop: 12, paddingBottom: 6, backgroundColor: ground } },
|
|
3317
|
-
typeof view !== "string" ? back("help", "All articles") : view === "messages" ? back("home", "Back to home") : view === "home" && (o.logo || title) ? o.logo ? (0, import_react2.createElement)(RN3.Image, { key: "logo", source: { uri: o.logo }, accessibilityIgnoresInvertColors: true, style: {
|
|
3495
|
+
typeof view !== "string" ? back("help", "All articles") : view === "messages" ? back("home", "Back to home") : view === "home" && (o.logo || title) ? o.logo ? (0, import_react2.createElement)(RN3.Image, { key: "logo", source: { uri: o.logo }, accessibilityIgnoresInvertColors: true, resizeMode: "contain", style: { height: 28, aspectRatio: 1, maxWidth: 132, borderRadius: 8, marginLeft: 4 } }) : teamMark(title, alpha(p.accent, 0.16), p.accent, 28) : null,
|
|
3318
3496
|
// In the chat: who answers, in the middle.
|
|
3319
3497
|
view === "messages" && title ? (0, import_react2.createElement)(
|
|
3320
3498
|
RN3.View,
|
|
@@ -3378,6 +3556,7 @@ var MRRocketMessenger = ({ client, appearance, options: given = {} }) => {
|
|
|
3378
3556
|
corners: look.corners,
|
|
3379
3557
|
header: look.header,
|
|
3380
3558
|
logo: look.logo,
|
|
3559
|
+
agentName: look.agentName,
|
|
3381
3560
|
...defined
|
|
3382
3561
|
} : given;
|
|
3383
3562
|
const p = usePalette({ theme: appearance?.theme ?? look?.theme, accent: appearance?.accent ?? look?.accent });
|
|
@@ -3467,7 +3646,7 @@ var MRRocketMessenger = ({ client, appearance, options: given = {} }) => {
|
|
|
3467
3646
|
elevation: 6
|
|
3468
3647
|
}
|
|
3469
3648
|
},
|
|
3470
|
-
k.muted(`${
|
|
3649
|
+
k.muted(`${who(banner.from, options.agentName)}${unseen > 1 ? ` \xB7 ${unseen} new` : ""}`, { fontWeight: "600", fontSize: 12 }),
|
|
3471
3650
|
(0, import_react2.createElement)(RN3.Text, { numberOfLines: 3, style: { color: p.fg, fontSize: 15, lineHeight: 20 } }, banner.body)
|
|
3472
3651
|
) : null;
|
|
3473
3652
|
if (!launcher && !toast && !open) return null;
|
|
@@ -3570,6 +3749,10 @@ var usePaywall = () => {
|
|
|
3570
3749
|
const client = useClient();
|
|
3571
3750
|
return { open: client.openPaywall };
|
|
3572
3751
|
};
|
|
3752
|
+
var useReview = () => {
|
|
3753
|
+
const client = useClient();
|
|
3754
|
+
return { request: client.requestReview };
|
|
3755
|
+
};
|
|
3573
3756
|
var useOnboarding = () => {
|
|
3574
3757
|
const client = useClient();
|
|
3575
3758
|
return { start: client.startOnboarding };
|