@mrrocket/sdk 0.18.0 → 0.21.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 +23 -0
- package/README.md +24 -1
- package/dist/api-client.d.ts +28 -4
- package/dist/api-client.js +30 -13
- package/dist/api-client.js.map +1 -1
- package/dist/browser/mrrocket-flows.js +8 -3
- package/dist/browser/mrrocket-messenger.js +38 -5
- package/dist/browser/mrrocket.js +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js.map +1 -1
- package/dist/client.d.ts +6 -1
- package/dist/client.js +22 -4
- package/dist/client.js.map +1 -1
- package/dist/core.d.ts +5 -1
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/feedback.js +161 -119
- package/dist/feedback.js.map +1 -1
- package/dist/flows-ui.d.ts +1 -1
- package/dist/flows-ui.js +100 -72
- package/dist/flows-ui.js.map +1 -1
- package/dist/flows-web.js +2 -1
- package/dist/flows-web.js.map +1 -1
- package/dist/flows.d.ts +38 -6
- package/dist/flows.js +51 -17
- package/dist/flows.js.map +1 -1
- package/dist/i18n.d.ts +22 -0
- package/dist/i18n.js +74 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/messenger-ui.d.ts +9 -3
- package/dist/messenger-ui.js +324 -152
- package/dist/messenger-ui.js.map +1 -1
- package/dist/messenger.d.ts +3 -1
- package/dist/messenger.js +3 -2
- package/dist/messenger.js.map +1 -1
- package/dist/paywall.js +2 -1
- package/dist/paywall.js.map +1 -1
- package/dist/react-native/index.d.cts +66 -8
- package/dist/react-native/index.d.ts +66 -8
- package/dist/react-native/index.js +615 -334
- package/dist/react-native/index.js.map +1 -1
- package/dist/server.d.ts +4 -2
- package/dist/server.js +2 -2
- package/dist/server.js.map +1 -1
- package/dist/styles-css.d.ts +1 -1
- package/dist/styles-css.js +35 -2
- package/dist/styles-css.js.map +1 -1
- package/dist/survey.js +18 -14
- package/dist/survey.js.map +1 -1
- package/dist/ui.d.ts +2 -0
- package/dist/ui.js +15 -12
- package/dist/ui.js.map +1 -1
- package/package.json +6 -3
|
@@ -31,7 +31,9 @@ __export(native_exports, {
|
|
|
31
31
|
SDK_VERSION: () => SDK_VERSION,
|
|
32
32
|
activeRouteName: () => activeRouteName,
|
|
33
33
|
createMRRocket: () => createMRRocket,
|
|
34
|
+
currentLocale: () => currentLocale,
|
|
34
35
|
getMRRocket: () => getMRRocket,
|
|
36
|
+
setLocale: () => setLocale,
|
|
35
37
|
useCancelFlow: () => useCancelFlow,
|
|
36
38
|
useExpoRouterTracking: () => useExpoRouterTracking,
|
|
37
39
|
useFeedback: () => useFeedback,
|
|
@@ -50,8 +52,68 @@ __export(native_exports, {
|
|
|
50
52
|
module.exports = __toCommonJS(native_exports);
|
|
51
53
|
var import_react4 = require("react");
|
|
52
54
|
|
|
55
|
+
// src/i18n.ts
|
|
56
|
+
var g = globalThis;
|
|
57
|
+
var s = g.__mrrocketI18n ?? (g.__mrrocketI18n = { version: 0, onWords: /* @__PURE__ */ new Set(), onLocale: /* @__PURE__ */ new Set() });
|
|
58
|
+
var tell = (listeners) => {
|
|
59
|
+
for (const fn of [...listeners]) {
|
|
60
|
+
try {
|
|
61
|
+
fn();
|
|
62
|
+
} catch {
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
var t = (word) => s.words?.[word] || word;
|
|
67
|
+
var tf = (word, vars) => t(word).replace(/\{(\w+)\}/g, (all, k) => k in vars ? String(vars[k]) : all);
|
|
68
|
+
var setWords = (words, lang) => {
|
|
69
|
+
const same2 = s.lang === lang && JSON.stringify(s.words ?? {}) === JSON.stringify(words ?? {});
|
|
70
|
+
s.words = words;
|
|
71
|
+
s.lang = lang;
|
|
72
|
+
if (same2) return;
|
|
73
|
+
s.version++;
|
|
74
|
+
tell(s.onWords);
|
|
75
|
+
};
|
|
76
|
+
var tidy = (locale) => {
|
|
77
|
+
const tag = locale?.trim().replace(/_/g, "-");
|
|
78
|
+
if (!tag) return void 0;
|
|
79
|
+
try {
|
|
80
|
+
return Intl.getCanonicalLocales(tag)[0];
|
|
81
|
+
} catch (e) {
|
|
82
|
+
return e instanceof RangeError ? void 0 : tag;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
var setLocale = (locale) => {
|
|
86
|
+
const next = tidy(locale);
|
|
87
|
+
if (next === s.locale) return;
|
|
88
|
+
s.locale = next;
|
|
89
|
+
s.version++;
|
|
90
|
+
tell(s.onWords);
|
|
91
|
+
tell(s.onLocale);
|
|
92
|
+
};
|
|
93
|
+
var setDeviceLocale = (locale) => {
|
|
94
|
+
s.device = tidy(locale);
|
|
95
|
+
};
|
|
96
|
+
var currentLocale = () => s.locale ?? s.device ?? (typeof navigator !== "undefined" && navigator.language || void 0);
|
|
97
|
+
var langParam = () => {
|
|
98
|
+
const l = currentLocale();
|
|
99
|
+
return l ? `&lang=${encodeURIComponent(l)}` : "";
|
|
100
|
+
};
|
|
101
|
+
var wordsVersion = () => s.version;
|
|
102
|
+
var onWords = (fn) => {
|
|
103
|
+
s.onWords.add(fn);
|
|
104
|
+
return () => {
|
|
105
|
+
s.onWords.delete(fn);
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
var onLocale = (fn) => {
|
|
109
|
+
s.onLocale.add(fn);
|
|
110
|
+
return () => {
|
|
111
|
+
s.onLocale.delete(fn);
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
|
|
53
115
|
// src/core.ts
|
|
54
|
-
var SDK_VERSION = "0.
|
|
116
|
+
var SDK_VERSION = "0.21.0";
|
|
55
117
|
var propOf = (person, name) => {
|
|
56
118
|
const want = name.trim().toLowerCase();
|
|
57
119
|
for (const [k, v] of Object.entries(person)) if (k.toLowerCase() === want) return v;
|
|
@@ -77,7 +139,7 @@ var matchesCondition = (c, person) => {
|
|
|
77
139
|
var evaluateFlag = (flag, distinctId, person = {}) => {
|
|
78
140
|
if (!flag.on) return false;
|
|
79
141
|
const subject = { id: distinctId, ...person };
|
|
80
|
-
const set = flag.sets.length ? flag.sets.find((
|
|
142
|
+
const set = flag.sets.length ? flag.sets.find((s2) => s2.conditions.every((c) => matchesCondition(c, subject))) : { conditions: [], rollout: 100 };
|
|
81
143
|
if (!set) return false;
|
|
82
144
|
if (flagUnit(`${flag.key}:rollout:${distinctId}`) * 100 >= set.rollout) return false;
|
|
83
145
|
if (!flag.variants?.length) return true;
|
|
@@ -108,11 +170,11 @@ var parseQuery = (url) => {
|
|
|
108
170
|
const q = url.split("#")[0];
|
|
109
171
|
const at = q.indexOf("?");
|
|
110
172
|
if (at < 0) return out;
|
|
111
|
-
const dec = (
|
|
173
|
+
const dec = (s2) => {
|
|
112
174
|
try {
|
|
113
|
-
return decodeURIComponent(
|
|
175
|
+
return decodeURIComponent(s2.replace(/\+/g, " "));
|
|
114
176
|
} catch {
|
|
115
|
-
return
|
|
177
|
+
return s2;
|
|
116
178
|
}
|
|
117
179
|
};
|
|
118
180
|
for (const pair of q.slice(at + 1).split("&")) {
|
|
@@ -184,9 +246,9 @@ var useOnboardingItem = (flows, id, start) => {
|
|
|
184
246
|
return;
|
|
185
247
|
}
|
|
186
248
|
const up = () => {
|
|
187
|
-
const
|
|
188
|
-
setState(
|
|
189
|
-
if (
|
|
249
|
+
const s2 = flows.onboarding.item(id);
|
|
250
|
+
setState(s2);
|
|
251
|
+
if (s2 && !s2.done && !s2.dismissed) flows.onboarding.seen(id);
|
|
190
252
|
};
|
|
191
253
|
up();
|
|
192
254
|
return flows.subscribe(up);
|
|
@@ -216,9 +278,9 @@ var useOnboardingItem = (flows, id, start) => {
|
|
|
216
278
|
// src/identity.ts
|
|
217
279
|
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 }).";
|
|
218
280
|
var warnUnsigned = (status, log = (m) => console.warn(m)) => {
|
|
219
|
-
const
|
|
220
|
-
if (status !== 401 ||
|
|
221
|
-
|
|
281
|
+
const g2 = globalThis;
|
|
282
|
+
if (status !== 401 || g2.__mrrocketUnsigned) return;
|
|
283
|
+
g2.__mrrocketUnsigned = true;
|
|
222
284
|
log(`[MRRocket] ${UNSIGNED_HELP}`);
|
|
223
285
|
};
|
|
224
286
|
var nextSigned = (signed, id, hash, log = (m) => console.warn(m)) => {
|
|
@@ -242,11 +304,11 @@ var BRAND_URL = "https://mrrocket.dev/?utm_source=messenger&utm_medium=powered-b
|
|
|
242
304
|
var inkOn = (hex) => {
|
|
243
305
|
const m = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(hex);
|
|
244
306
|
if (!m) return "#ffffff";
|
|
245
|
-
const [r,
|
|
307
|
+
const [r, g2, b] = [m[1], m[2], m[3]].map((x) => {
|
|
246
308
|
const c = parseInt(x, 16) / 255;
|
|
247
309
|
return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
248
310
|
});
|
|
249
|
-
return 0.2126 * r + 0.7152 *
|
|
311
|
+
return 0.2126 * r + 0.7152 * g2 + 0.0722 * b > 0.45 ? "#111214" : "#ffffff";
|
|
250
312
|
};
|
|
251
313
|
var ApiError = class extends Error {
|
|
252
314
|
constructor(message, status) {
|
|
@@ -255,7 +317,11 @@ var ApiError = class extends Error {
|
|
|
255
317
|
this.status = status;
|
|
256
318
|
}
|
|
257
319
|
};
|
|
258
|
-
var qs = (params) => Object.entries(params).filter((e) => !!e[1]).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
|
|
320
|
+
var qs = (params, lang = currentLocale()) => Object.entries({ ...params, lang }).filter((e) => !!e[1]).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
|
|
321
|
+
var withLang = (url) => {
|
|
322
|
+
const q = qs({});
|
|
323
|
+
return q ? `${url}?${q}` : url;
|
|
324
|
+
};
|
|
259
325
|
var call = async (url, init, signal) => {
|
|
260
326
|
let res;
|
|
261
327
|
try {
|
|
@@ -267,7 +333,7 @@ var call = async (url, init, signal) => {
|
|
|
267
333
|
});
|
|
268
334
|
} catch (e) {
|
|
269
335
|
if (e.name === "AbortError") throw e;
|
|
270
|
-
throw new ApiError("Could not reach the server. Check your connection and try again.", 0);
|
|
336
|
+
throw new ApiError(t("Could not reach the server. Check your connection and try again."), 0);
|
|
271
337
|
}
|
|
272
338
|
if (!res.ok) {
|
|
273
339
|
warnUnsigned(res.status);
|
|
@@ -276,7 +342,7 @@ var call = async (url, init, signal) => {
|
|
|
276
342
|
detail = (await res.json()).error ?? "";
|
|
277
343
|
} catch {
|
|
278
344
|
}
|
|
279
|
-
throw new ApiError(detail || (res.status === 429 ? "Too many requests. Try again in a minute." : "Something went wrong. Please try again."), res.status);
|
|
345
|
+
throw new ApiError(detail || (res.status === 429 ? t("Too many requests. Try again in a minute.") : t("Something went wrong. Please try again.")), res.status);
|
|
280
346
|
}
|
|
281
347
|
return await res.json();
|
|
282
348
|
};
|
|
@@ -284,7 +350,7 @@ var createApi = (c) => {
|
|
|
284
350
|
const base = `${c.host}/api/sdk`;
|
|
285
351
|
const who2 = { key: c.apiKey, distinctId: c.distinctId, user: c.user, userHash: c.userHash };
|
|
286
352
|
const me = { key: c.apiKey, distinctId: c.distinctId, userHash: c.userHash };
|
|
287
|
-
const post = (path, body, signal) => call(`${base}${path}
|
|
353
|
+
const post = (path, body, signal) => call(withLang(`${base}${path}`), { method: "POST", body: JSON.stringify({ ...who2, ...body }) }, signal);
|
|
288
354
|
return {
|
|
289
355
|
list: (o, signal) => call(`${base}/feedback?${qs({ ...me, board: o.board, sort: o.sort, q: o.q })}`, void 0, signal),
|
|
290
356
|
post: (id, signal) => call(`${base}/feedback/${encodeURIComponent(id)}?${qs(me)}`, void 0, signal),
|
|
@@ -297,8 +363,12 @@ var createApi = (c) => {
|
|
|
297
363
|
survey: (url, signal) => call(`${base}/surveys?${qs({ ...me, url })}`, void 0, signal),
|
|
298
364
|
seen: (id) => post(`/surveys/${encodeURIComponent(id)}/seen`, { user: void 0 }),
|
|
299
365
|
respond: (id, r) => post(`/surveys/${encodeURIComponent(id)}/respond`, r),
|
|
300
|
-
chat
|
|
301
|
-
|
|
366
|
+
/** One chat by id, else the visitor's newest; `conversations` lists them all. */
|
|
367
|
+
chat: (signal, conversation) => call(`${base}/messages?${qs({ ...me, conversation })}`, void 0, signal),
|
|
368
|
+
/**
|
|
369
|
+
* A message; `attachments` are upload ids from `uploadChatFile` (then the body may be empty). `conversation` is
|
|
370
|
+
* the chat it answers, or "new" to start one; without it, the running chat or else a new one.
|
|
371
|
+
*/
|
|
302
372
|
sendChat: (m) => post("/messages", m),
|
|
303
373
|
/** Uploads one picked file for the next message (pictures and PDFs, at most 10 MB); answers its upload id. */
|
|
304
374
|
uploadChatFile: (file, signal) => {
|
|
@@ -308,10 +378,10 @@ var createApi = (c) => {
|
|
|
308
378
|
if (c.userHash) form.append("userHash", c.userHash);
|
|
309
379
|
if ("uri" in file) form.append("file", file);
|
|
310
380
|
else form.append("file", file, file.name || "file");
|
|
311
|
-
return call(`${base}/messages/attachments
|
|
381
|
+
return call(withLang(`${base}/messages/attachments`), { method: "POST", body: form }, signal);
|
|
312
382
|
},
|
|
313
383
|
/** Where a sent file of this visitor's chat is read (an image's src, a PDF's link). */
|
|
314
|
-
attachmentUrl: (id) => `${base}/messages/attachments/${encodeURIComponent(id)}?${qs(me)}`,
|
|
384
|
+
attachmentUrl: (id) => `${base}/messages/attachments/${encodeURIComponent(id)}?${qs(me, "")}`,
|
|
315
385
|
articles: (q, signal) => call(`${base}/articles?${qs({ key: c.apiKey, q })}`, void 0, signal),
|
|
316
386
|
article: (id, signal) => call(`${base}/articles/${encodeURIComponent(id)}?${qs(me)}`, void 0, signal),
|
|
317
387
|
messengerLook: (signal) => call(`${base}/messenger?${qs({ key: c.apiKey })}`, void 0, signal),
|
|
@@ -322,13 +392,13 @@ var createApi = (c) => {
|
|
|
322
392
|
// src/batch.ts
|
|
323
393
|
var MAX_BATCH = 100;
|
|
324
394
|
var MAX_BYTES = 6e4;
|
|
325
|
-
var byteLength = (
|
|
395
|
+
var byteLength = (s2) => {
|
|
326
396
|
let n = 0;
|
|
327
|
-
for (let i = 0; i <
|
|
328
|
-
const c =
|
|
397
|
+
for (let i = 0; i < s2.length; i++) {
|
|
398
|
+
const c = s2.charCodeAt(i);
|
|
329
399
|
if (c < 128) n += 1;
|
|
330
400
|
else if (c < 2048) n += 2;
|
|
331
|
-
else if (c >= 55296 && c <= 56319 && i + 1 <
|
|
401
|
+
else if (c >= 55296 && c <= 56319 && i + 1 < s2.length && (s2.charCodeAt(i + 1) & 64512) === 56320) {
|
|
332
402
|
n += 4;
|
|
333
403
|
i++;
|
|
334
404
|
} else n += 3;
|
|
@@ -363,7 +433,7 @@ var batchRequests = (key, events, sdk, userHash) => {
|
|
|
363
433
|
|
|
364
434
|
// src/errors.ts
|
|
365
435
|
var MAX_STACK = 8 * 1024;
|
|
366
|
-
var clip = (
|
|
436
|
+
var clip = (s2, n) => s2.length > n ? `${s2.slice(0, n - 1)}\u2026` : s2;
|
|
367
437
|
var describeError = (error) => {
|
|
368
438
|
if (error instanceof Error) return { type: error.name || "Error", message: clip(error.message || String(error), 1e3), stack: error.stack ? clip(error.stack, MAX_STACK) : void 0 };
|
|
369
439
|
if (typeof error === "string") return { type: "Error", message: clip(error, 1e3) };
|
|
@@ -384,18 +454,20 @@ var PLAIN_CANCEL = {
|
|
|
384
454
|
question: "",
|
|
385
455
|
reasons: [],
|
|
386
456
|
offers: {},
|
|
387
|
-
|
|
457
|
+
// Read when it is shown, so it follows the visitor's language.
|
|
458
|
+
get confirm() {
|
|
459
|
+
return { title: t("Cancel your subscription?"), body: t("You keep access until the end of the billing period."), button: t("Cancel subscription") };
|
|
460
|
+
},
|
|
388
461
|
offersFor: "business"
|
|
389
462
|
};
|
|
390
|
-
var FAILED = "Something went wrong. Please try again.";
|
|
391
463
|
var PROGRESS_KEY = "mrrocket:onboarding";
|
|
392
|
-
var eventSlug = (
|
|
464
|
+
var eventSlug = (s2) => s2.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
393
465
|
var SIGNUP = /* @__PURE__ */ new Set(["signed_up", "signup", "sign_up", "registered", "account_created", "user_signed_up"]);
|
|
394
466
|
var matchesTrigger = (trigger, event) => {
|
|
395
|
-
const
|
|
467
|
+
const t2 = eventSlug(trigger);
|
|
396
468
|
const e = eventSlug(event);
|
|
397
|
-
if (!
|
|
398
|
-
return
|
|
469
|
+
if (!t2 || !e) return false;
|
|
470
|
+
return t2 === e || SIGNUP.has(t2) && SIGNUP.has(e);
|
|
399
471
|
};
|
|
400
472
|
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());
|
|
401
473
|
var isPageTrigger = (trigger) => trigger.trim().startsWith("/");
|
|
@@ -404,16 +476,30 @@ var momentOf = (f) => f.when ?? f.trigger;
|
|
|
404
476
|
var manual = (trigger) => eventSlug(trigger) === "from_a_checklist" || eventSlug(trigger) === "from_your_code" || eventSlug(trigger) === eventSlug(NEEDS_NEWER);
|
|
405
477
|
var AFTER_PREFIX = "after:";
|
|
406
478
|
var afterOf = (trigger) => {
|
|
407
|
-
const
|
|
408
|
-
return
|
|
479
|
+
const t2 = (trigger ?? "").trim();
|
|
480
|
+
return t2.toLowerCase().startsWith(AFTER_PREFIX) ? t2.slice(AFTER_PREFIX.length).trim() || null : null;
|
|
409
481
|
};
|
|
410
482
|
var opensOf = (step) => step.opens ?? (step.startsTour ? { kind: "tour", id: step.startsTour } : void 0);
|
|
483
|
+
var OTHER_PREFIX = "other:";
|
|
484
|
+
var scaleOf = (q) => {
|
|
485
|
+
const min = q?.min ?? 0;
|
|
486
|
+
const max = q?.max ?? 10;
|
|
487
|
+
return Array.from({ length: Math.max(0, Math.min(max - min, 20) + 1) }, (_, i) => min + i);
|
|
488
|
+
};
|
|
411
489
|
var answerOk = (q, value) => {
|
|
412
490
|
if (!q || value == null) return false;
|
|
413
|
-
if (q.type === "choice")
|
|
491
|
+
if (q.type === "choice") {
|
|
492
|
+
if (q.other && value.startsWith(OTHER_PREFIX)) return value.slice(OTHER_PREFIX.length).trim().length > 0;
|
|
493
|
+
return (q.options ?? []).some((o) => o.id === value);
|
|
494
|
+
}
|
|
495
|
+
if (q.type === "scale") return scaleOf(q).some((n) => String(n) === value);
|
|
414
496
|
return value.trim().length > 0 && value.length <= 500;
|
|
415
497
|
};
|
|
416
|
-
var answerLabel = (q, value) =>
|
|
498
|
+
var answerLabel = (q, value) => {
|
|
499
|
+
if (q?.type !== "choice") return value.trim();
|
|
500
|
+
if (q.other && value.startsWith(OTHER_PREFIX)) return value.slice(OTHER_PREFIX.length).trim();
|
|
501
|
+
return q.options?.find((o) => o.id === value)?.label ?? value;
|
|
502
|
+
};
|
|
417
503
|
var videoEmbed = (url) => {
|
|
418
504
|
const u = (url ?? "").trim();
|
|
419
505
|
if (!/^https?:\/\//i.test(u)) return null;
|
|
@@ -435,12 +521,14 @@ var runId = () => `cf_${Math.random().toString(36).slice(2, 10)}${Date.now().toS
|
|
|
435
521
|
var discounted = (amount, percent) => Math.round(amount * (1 - percent / 100) * 100) / 100;
|
|
436
522
|
var money = (amount, currency) => {
|
|
437
523
|
try {
|
|
438
|
-
return new Intl.NumberFormat(
|
|
524
|
+
return new Intl.NumberFormat(currentLocale(), { style: "currency", currency }).format(amount);
|
|
439
525
|
} catch {
|
|
440
526
|
return `${amount.toFixed(2)} ${currency}`;
|
|
441
527
|
}
|
|
442
528
|
};
|
|
443
|
-
var offerButton = (o) => o.kind === "contact" ? "Send to the team" : o.kind === "pause" ? "Pause instead" : o.kind === "downgrade" ? "Switch plan" : "Accept offer";
|
|
529
|
+
var offerButton = (o) => o.kind === "contact" ? t("Send to the team") : o.kind === "pause" ? t("Pause instead") : o.kind === "downgrade" ? t("Switch plan") : t("Accept offer");
|
|
530
|
+
var monthsWord = (n) => n === 1 ? t("1 month") : tf("{n} months", { n });
|
|
531
|
+
var forMonths = (n) => tf("for {months}", { months: monthsWord(n) });
|
|
444
532
|
var safeUrl = (url) => {
|
|
445
533
|
const u = (url ?? "").trim();
|
|
446
534
|
return /^https?:\/\//i.test(u) || /^\/(?!\/)/.test(u) ? u : void 0;
|
|
@@ -587,13 +675,13 @@ var createFlows = (env) => {
|
|
|
587
675
|
}
|
|
588
676
|
};
|
|
589
677
|
const merge = (state) => {
|
|
590
|
-
for (const [id,
|
|
678
|
+
for (const [id, s2] of Object.entries(state ?? {})) {
|
|
591
679
|
const p = prog(id);
|
|
592
|
-
for (const d of
|
|
593
|
-
if (
|
|
594
|
-
for (const [k, v] of Object.entries(
|
|
595
|
-
for (const [k, v] of Object.entries(
|
|
596
|
-
for (const [k, v] of Object.entries(
|
|
680
|
+
for (const d of s2.done ?? []) p.done.add(d);
|
|
681
|
+
if (s2.dismissed) p.dismissed = true;
|
|
682
|
+
for (const [k, v] of Object.entries(s2.answers ?? {})) if (!p.answers.has(k)) p.answers.set(k, v === "yes");
|
|
683
|
+
for (const [k, v] of Object.entries(s2.at ?? {})) if (typeof v === "number" && !p.at.has(k)) p.at.set(k, v);
|
|
684
|
+
for (const [k, v] of Object.entries(s2.reached ?? {})) if (typeof v === "number" && (!p.reached.has(k) || v < p.reached.get(k))) p.reached.set(k, v);
|
|
597
685
|
}
|
|
598
686
|
};
|
|
599
687
|
const wakeAt = (until) => {
|
|
@@ -633,14 +721,14 @@ var createFlows = (env) => {
|
|
|
633
721
|
return {
|
|
634
722
|
type: "checklist",
|
|
635
723
|
flow,
|
|
636
|
-
items: items.map((
|
|
637
|
-
const o = opensOf(
|
|
638
|
-
const
|
|
639
|
-
const target =
|
|
724
|
+
items: items.map((s2) => {
|
|
725
|
+
const o = opensOf(s2);
|
|
726
|
+
const t2 = o && (o.kind === "tour" || o.kind === "modal") && o.id !== flow.id ? flowById(o.id) : void 0;
|
|
727
|
+
const target = t2 && t2.shownBy !== "code" && nodesOf(t2).length ? t2 : void 0;
|
|
640
728
|
const opens = o && (o.kind === "page" ? !!o.target.trim() : o.kind === "code" || !!target) ? o : void 0;
|
|
641
729
|
return {
|
|
642
|
-
step:
|
|
643
|
-
done: p.done.has(
|
|
730
|
+
step: s2,
|
|
731
|
+
done: p.done.has(s2.id),
|
|
644
732
|
...target && o?.kind === "tour" ? { tour: target } : {},
|
|
645
733
|
...opens ? { opens } : {},
|
|
646
734
|
...target ? { target } : {}
|
|
@@ -696,7 +784,7 @@ var createFlows = (env) => {
|
|
|
696
784
|
active = null;
|
|
697
785
|
const list = back ? flowById(back.flow) : void 0;
|
|
698
786
|
if (back && list) {
|
|
699
|
-
const item = stepsIn(nodesOf(list)).find((
|
|
787
|
+
const item = stepsIn(nodesOf(list)).find((s2) => s2.id === back.step);
|
|
700
788
|
const p = prog(list.id);
|
|
701
789
|
if (item && !item.event && !progress.get(ended2)?.dismissed && !p.done.has(item.id)) {
|
|
702
790
|
markDone(p, item.id);
|
|
@@ -739,8 +827,8 @@ var createFlows = (env) => {
|
|
|
739
827
|
};
|
|
740
828
|
const allowed = (f) => f.audience === "answered" ? !!f.answered && answers.get(f.answered.question) === f.answered.option : f.audience !== "signed_in" || !!env.personId();
|
|
741
829
|
const predecessorEnded = (id) => {
|
|
742
|
-
const
|
|
743
|
-
return !
|
|
830
|
+
const g2 = flowById(id);
|
|
831
|
+
return !g2 || finished(g2);
|
|
744
832
|
};
|
|
745
833
|
const due = (f, trigger = momentOf(f)) => {
|
|
746
834
|
const path = env.path?.();
|
|
@@ -775,7 +863,7 @@ var createFlows = (env) => {
|
|
|
775
863
|
};
|
|
776
864
|
const answerStep = (flow, step, raw) => {
|
|
777
865
|
const q = step.question;
|
|
778
|
-
const value = q.type === "text" ? raw.trim().slice(0, 500) : raw;
|
|
866
|
+
const value = q.type === "text" ? raw.trim().slice(0, 500) : raw.startsWith(OTHER_PREFIX) ? OTHER_PREFIX + raw.slice(OTHER_PREFIX.length).trim().slice(0, 490) : raw;
|
|
779
867
|
answers.set(q.key, value);
|
|
780
868
|
drafts.delete(step.id);
|
|
781
869
|
markDone(prog(flow.id), step.id);
|
|
@@ -886,7 +974,7 @@ var createFlows = (env) => {
|
|
|
886
974
|
() => finish(c, r),
|
|
887
975
|
() => {
|
|
888
976
|
c.busy = false;
|
|
889
|
-
c.error =
|
|
977
|
+
c.error = t("Something went wrong. Please try again.");
|
|
890
978
|
emit();
|
|
891
979
|
}
|
|
892
980
|
);
|
|
@@ -923,11 +1011,11 @@ var createFlows = (env) => {
|
|
|
923
1011
|
for (const l of lists) {
|
|
924
1012
|
const p = prog(l.id);
|
|
925
1013
|
let here = false;
|
|
926
|
-
for (const
|
|
927
|
-
if (
|
|
928
|
-
markDone(p,
|
|
1014
|
+
for (const s2 of stepsIn(nodesOf(l)))
|
|
1015
|
+
if (s2.kind === "checklist" && s2.event && !p.done.has(s2.id) && matchesTrigger(s2.event, name)) {
|
|
1016
|
+
markDone(p, s2.id);
|
|
929
1017
|
here = true;
|
|
930
|
-
env.track(ONBOARDING_EVENT, { flow: l.id, step:
|
|
1018
|
+
env.track(ONBOARDING_EVENT, { flow: l.id, step: s2.id, kind: s2.kind, action: "completed" });
|
|
931
1019
|
}
|
|
932
1020
|
if (here) {
|
|
933
1021
|
ticked = true;
|
|
@@ -966,8 +1054,8 @@ var createFlows = (env) => {
|
|
|
966
1054
|
answer: (text2) => {
|
|
967
1055
|
const c = cancel;
|
|
968
1056
|
if (!c || c.busy || c.screen !== "followup") return;
|
|
969
|
-
const
|
|
970
|
-
c.answer =
|
|
1057
|
+
const t2 = text2.slice(0, 500);
|
|
1058
|
+
c.answer = t2.trim() ? t2 : void 0;
|
|
971
1059
|
emit();
|
|
972
1060
|
},
|
|
973
1061
|
next: () => {
|
|
@@ -1109,11 +1197,22 @@ var createFlows = (env) => {
|
|
|
1109
1197
|
drafts.set(v.step.id, String(value).slice(0, 500));
|
|
1110
1198
|
emit();
|
|
1111
1199
|
},
|
|
1200
|
+
skip: () => {
|
|
1201
|
+
const v = onboardingView();
|
|
1202
|
+
const q = v?.type === "step" && v.step.kind === "question" ? v.step.question : void 0;
|
|
1203
|
+
if (v?.type !== "step" || !q?.optional) return;
|
|
1204
|
+
drafts.delete(v.step.id);
|
|
1205
|
+
markDone(prog(v.flow.id), v.step.id);
|
|
1206
|
+
env.track(ONBOARDING_EVENT, { flow: v.flow.id, step: v.step.id, kind: "question", action: "completed", question: q.key, skipped: true });
|
|
1207
|
+
celebrate(v.flow);
|
|
1208
|
+
save();
|
|
1209
|
+
emit();
|
|
1210
|
+
},
|
|
1112
1211
|
back: () => {
|
|
1113
1212
|
const v = onboardingView();
|
|
1114
1213
|
if (v?.type !== "step" || v.step.kind !== "question" || !v.back) return;
|
|
1115
1214
|
const line = stepsIn(nodesOf(v.flow));
|
|
1116
|
-
const at = line.findIndex((
|
|
1215
|
+
const at = line.findIndex((s2) => s2.id === v.step.id);
|
|
1117
1216
|
const p = prog(v.flow.id);
|
|
1118
1217
|
for (let i = at - 1; i >= 0; i--)
|
|
1119
1218
|
if (p.done.has(line[i].id)) {
|
|
@@ -1128,11 +1227,11 @@ var createFlows = (env) => {
|
|
|
1128
1227
|
const f = flowById(id);
|
|
1129
1228
|
if (!f) return null;
|
|
1130
1229
|
const p = progress.get(f.id);
|
|
1131
|
-
const steps = stepsIn(nodesOf(f)).map((
|
|
1132
|
-
const a =
|
|
1133
|
-
return { ...
|
|
1230
|
+
const steps = stepsIn(nodesOf(f)).map((s2) => {
|
|
1231
|
+
const a = s2.question ? answers.get(s2.question.key) : void 0;
|
|
1232
|
+
return { ...s2, done: !!p?.done.has(s2.id), ...a !== void 0 ? { answer: a } : {} };
|
|
1134
1233
|
});
|
|
1135
|
-
const done = steps.length > 0 && steps.every((
|
|
1234
|
+
const done = steps.length > 0 && steps.every((s2) => s2.done);
|
|
1136
1235
|
const dismissed = !!p?.dismissed;
|
|
1137
1236
|
return { item: f, steps, done, dismissed, eligible: !done && !dismissed && allowed(f) && due(f) };
|
|
1138
1237
|
},
|
|
@@ -1144,21 +1243,21 @@ var createFlows = (env) => {
|
|
|
1144
1243
|
env.track(ONBOARDING_EVENT, { flow: f.id, action: "started" });
|
|
1145
1244
|
}
|
|
1146
1245
|
const p = progress.get(f.id);
|
|
1147
|
-
for (const
|
|
1148
|
-
if (!p?.done.has(
|
|
1149
|
-
shown.add(`s:${
|
|
1150
|
-
env.track(ONBOARDING_EVENT, { flow: f.id, step:
|
|
1151
|
-
if (
|
|
1246
|
+
for (const s2 of stepsIn(nodesOf(f)))
|
|
1247
|
+
if (!p?.done.has(s2.id) && !shown.has(`s:${s2.id}`)) {
|
|
1248
|
+
shown.add(`s:${s2.id}`);
|
|
1249
|
+
env.track(ONBOARDING_EVENT, { flow: f.id, step: s2.id, kind: s2.kind, action: "shown" });
|
|
1250
|
+
if (s2.kind !== "checklist") break;
|
|
1152
1251
|
}
|
|
1153
1252
|
},
|
|
1154
1253
|
complete: (id, stepId) => {
|
|
1155
1254
|
const f = flowById(id);
|
|
1156
|
-
const
|
|
1157
|
-
if (!f || !
|
|
1255
|
+
const s2 = f && stepsIn(nodesOf(f)).find((x) => x.id === stepId);
|
|
1256
|
+
if (!f || !s2 || s2.kind === "question") return;
|
|
1158
1257
|
const p = prog(f.id);
|
|
1159
|
-
if (p.done.has(
|
|
1160
|
-
markDone(p,
|
|
1161
|
-
env.track(ONBOARDING_EVENT, { flow: f.id, step:
|
|
1258
|
+
if (p.done.has(s2.id)) return;
|
|
1259
|
+
markDone(p, s2.id);
|
|
1260
|
+
env.track(ONBOARDING_EVENT, { flow: f.id, step: s2.id, kind: s2.kind, action: "completed" });
|
|
1162
1261
|
ended(f);
|
|
1163
1262
|
},
|
|
1164
1263
|
dismissItem: (id) => {
|
|
@@ -1173,9 +1272,9 @@ var createFlows = (env) => {
|
|
|
1173
1272
|
},
|
|
1174
1273
|
answerItem: (id, question, value) => {
|
|
1175
1274
|
const f = flowById(id);
|
|
1176
|
-
const
|
|
1177
|
-
if (!f || !
|
|
1178
|
-
answerStep(f,
|
|
1275
|
+
const s2 = f && stepsIn(nodesOf(f)).find((x) => x.kind === "question" && (x.id === question || x.question?.key === question));
|
|
1276
|
+
if (!f || !s2?.question || !answerOk(s2.question, value)) return;
|
|
1277
|
+
answerStep(f, s2, value);
|
|
1179
1278
|
ended(f);
|
|
1180
1279
|
}
|
|
1181
1280
|
},
|
|
@@ -1263,7 +1362,7 @@ var createPaywallHost = (env) => {
|
|
|
1263
1362
|
},
|
|
1264
1363
|
() => {
|
|
1265
1364
|
c.busy = false;
|
|
1266
|
-
c.error = "The purchase did not go through. Please try again.";
|
|
1365
|
+
c.error = t("The purchase did not go through. Please try again.");
|
|
1267
1366
|
emit();
|
|
1268
1367
|
}
|
|
1269
1368
|
);
|
|
@@ -1338,10 +1437,10 @@ var RageDetector = class {
|
|
|
1338
1437
|
__publicField(this, "clicks", []);
|
|
1339
1438
|
__publicField(this, "fired", false);
|
|
1340
1439
|
}
|
|
1341
|
-
push(x, y,
|
|
1342
|
-
const near = this.clicks.filter((c) =>
|
|
1440
|
+
push(x, y, t2) {
|
|
1441
|
+
const near = this.clicks.filter((c) => t2 - c.t <= this.windowMs && Math.hypot(c.x - x, c.y - y) <= this.radius);
|
|
1343
1442
|
if (near.length !== this.clicks.length) this.fired = false;
|
|
1344
|
-
this.clicks = [...near, { x, y, t }];
|
|
1443
|
+
this.clicks = [...near, { x, y, t: t2 }];
|
|
1345
1444
|
if (this.clicks.length >= this.count && !this.fired) {
|
|
1346
1445
|
this.fired = true;
|
|
1347
1446
|
return true;
|
|
@@ -1357,9 +1456,9 @@ var BUFFER_MS = 6e4;
|
|
|
1357
1456
|
var CHUNK_FRAMES = 12;
|
|
1358
1457
|
var CHUNK_CHARS = 7e5;
|
|
1359
1458
|
var MAX_MS = 30 * 6e4;
|
|
1360
|
-
var shareOf = (
|
|
1459
|
+
var shareOf = (s2) => {
|
|
1361
1460
|
let h3 = 2166136261;
|
|
1362
|
-
for (let i = 0; i <
|
|
1461
|
+
for (let i = 0; i < s2.length; i++) h3 = Math.imul(h3 ^ s2.charCodeAt(i), 16777619);
|
|
1363
1462
|
return (h3 >>> 0) / 4294967296;
|
|
1364
1463
|
};
|
|
1365
1464
|
var createRecorder = (deps, st) => {
|
|
@@ -1484,14 +1583,14 @@ var measureMasks = () => Promise.all(
|
|
|
1484
1583
|
(ref) => new Promise((resolve) => {
|
|
1485
1584
|
const v = ref.current;
|
|
1486
1585
|
if (!v) return resolve("none");
|
|
1487
|
-
const
|
|
1586
|
+
const t2 = setTimeout(() => resolve("failed"), 100);
|
|
1488
1587
|
try {
|
|
1489
1588
|
v.measureInWindow((x, y, w, h3) => {
|
|
1490
|
-
clearTimeout(
|
|
1589
|
+
clearTimeout(t2);
|
|
1491
1590
|
resolve(w > 0 && h3 > 0 ? [x, y, w, h3] : "none");
|
|
1492
1591
|
});
|
|
1493
1592
|
} catch {
|
|
1494
|
-
clearTimeout(
|
|
1593
|
+
clearTimeout(t2);
|
|
1495
1594
|
resolve("failed");
|
|
1496
1595
|
}
|
|
1497
1596
|
})
|
|
@@ -1528,7 +1627,7 @@ var MAX_CRUMBS = 20;
|
|
|
1528
1627
|
var MAX_BACKOFF_MS = 5 * 60 * 1e3;
|
|
1529
1628
|
var REQUEST_TIMEOUT_MS = 15e3;
|
|
1530
1629
|
var EAGER_COUNT = 20;
|
|
1531
|
-
var clip2 = (
|
|
1630
|
+
var clip2 = (s2, n) => s2.length > n ? `${s2.slice(0, n - 1)}\u2026` : s2;
|
|
1532
1631
|
var createClient = (options, deps) => {
|
|
1533
1632
|
const apiKey = (options.apiKey ?? "").trim();
|
|
1534
1633
|
const host = (options.host ?? DEFAULT_HOST).trim().replace(/\/+$/, "");
|
|
@@ -1556,6 +1655,8 @@ var createClient = (options, deps) => {
|
|
|
1556
1655
|
context = deps.context();
|
|
1557
1656
|
} catch {
|
|
1558
1657
|
}
|
|
1658
|
+
setDeviceLocale(context.locale);
|
|
1659
|
+
if (options.locale) setLocale(options.locale);
|
|
1559
1660
|
const appId = (deps.appId || "app").replace(/[^A-Za-z0-9._-]/g, "") || "app";
|
|
1560
1661
|
const release = options.release ?? (context.appVersion ? `${context.appVersion}${context.build ? ` (${context.build})` : ""}` : void 0);
|
|
1561
1662
|
const sdk = { v: SDK_VERSION, errors: errorsOn, platform: context.platform };
|
|
@@ -1601,7 +1702,7 @@ var createClient = (options, deps) => {
|
|
|
1601
1702
|
const fetchInApp = async (id) => {
|
|
1602
1703
|
if (!apiKey) return null;
|
|
1603
1704
|
const hash = hashOf(id);
|
|
1604
|
-
const r = await deps.fetch(`${host}/api/sdk/inapp?key=${encodeURIComponent(apiKey)}&distinctId=${encodeURIComponent(id)}${hash ? `&userHash=${hash}` : ""}`, { method: "GET" });
|
|
1705
|
+
const r = await deps.fetch(`${host}/api/sdk/inapp?key=${encodeURIComponent(apiKey)}&distinctId=${encodeURIComponent(id)}${hash ? `&userHash=${hash}` : ""}${langParam()}`, { method: "GET" });
|
|
1605
1706
|
warnUnsigned(r.status, deps.log);
|
|
1606
1707
|
return r.ok ? await r.json() : null;
|
|
1607
1708
|
};
|
|
@@ -1687,9 +1788,9 @@ var createClient = (options, deps) => {
|
|
|
1687
1788
|
const sendId = () => consent === "persistent" ? distinctId() : identified;
|
|
1688
1789
|
const hashOf = (id) => hashFor(signed, id);
|
|
1689
1790
|
const touchSession = () => {
|
|
1690
|
-
const
|
|
1691
|
-
if (!session ||
|
|
1692
|
-
else session.last =
|
|
1791
|
+
const t2 = now();
|
|
1792
|
+
if (!session || t2 - session.last >= SESSION_TIMEOUT_MS) session = { id: newDistinctId(), last: t2 };
|
|
1793
|
+
else session.last = t2;
|
|
1693
1794
|
return session.id;
|
|
1694
1795
|
};
|
|
1695
1796
|
const urlOf = (path = currentScreen) => `app://${appId}${path ?? "/"}`;
|
|
@@ -1878,7 +1979,7 @@ var createClient = (options, deps) => {
|
|
|
1878
1979
|
const fetchConfig = async () => {
|
|
1879
1980
|
if (!apiKey) return;
|
|
1880
1981
|
try {
|
|
1881
|
-
const r = await deps.fetch(`${host}/api/sdk/config?key=${encodeURIComponent(apiKey)}`, { method: "GET" });
|
|
1982
|
+
const r = await deps.fetch(`${host}/api/sdk/config?key=${encodeURIComponent(apiKey)}${langParam()}`, { method: "GET" });
|
|
1882
1983
|
if (!r.ok) {
|
|
1883
1984
|
warn(`Config request answered ${r.status}. Check the public key and the host.`);
|
|
1884
1985
|
return;
|
|
@@ -1886,6 +1987,7 @@ var createClient = (options, deps) => {
|
|
|
1886
1987
|
const c = await r.json();
|
|
1887
1988
|
if (c && Array.isArray(c.experiments)) {
|
|
1888
1989
|
config = c;
|
|
1990
|
+
setWords(c.words, c.lang);
|
|
1889
1991
|
if (consent !== "off") await set(KEYS.config, JSON.stringify(c));
|
|
1890
1992
|
changed();
|
|
1891
1993
|
}
|
|
@@ -2004,7 +2106,10 @@ var createClient = (options, deps) => {
|
|
|
2004
2106
|
}
|
|
2005
2107
|
try {
|
|
2006
2108
|
const c = cached ? JSON.parse(cached) : null;
|
|
2007
|
-
if (c && Array.isArray(c.experiments) && !config)
|
|
2109
|
+
if (c && Array.isArray(c.experiments) && !config) {
|
|
2110
|
+
config = c;
|
|
2111
|
+
setWords(c.words, c.lang);
|
|
2112
|
+
}
|
|
2008
2113
|
} catch {
|
|
2009
2114
|
await remove(KEYS.config);
|
|
2010
2115
|
}
|
|
@@ -2060,6 +2165,9 @@ var createClient = (options, deps) => {
|
|
|
2060
2165
|
await fetchConfig();
|
|
2061
2166
|
markReady();
|
|
2062
2167
|
refreshFlows();
|
|
2168
|
+
onLocale(() => {
|
|
2169
|
+
void fetchConfig().then(refreshFlows);
|
|
2170
|
+
});
|
|
2063
2171
|
};
|
|
2064
2172
|
void start().catch(() => {
|
|
2065
2173
|
loaded = true;
|
|
@@ -2248,13 +2356,13 @@ var createClient = (options, deps) => {
|
|
|
2248
2356
|
return client;
|
|
2249
2357
|
};
|
|
2250
2358
|
var activeRouteName = (state) => {
|
|
2251
|
-
let
|
|
2359
|
+
let s2 = state;
|
|
2252
2360
|
let name;
|
|
2253
|
-
for (let depth = 0;
|
|
2254
|
-
const route =
|
|
2361
|
+
for (let depth = 0; s2 && Array.isArray(s2.routes) && s2.routes.length && depth < 20; depth++) {
|
|
2362
|
+
const route = s2.routes[typeof s2.index === "number" ? s2.index : s2.routes.length - 1];
|
|
2255
2363
|
if (!route) break;
|
|
2256
2364
|
name = route.name ?? name;
|
|
2257
|
-
|
|
2365
|
+
s2 = route.state;
|
|
2258
2366
|
}
|
|
2259
2367
|
return name;
|
|
2260
2368
|
};
|
|
@@ -2410,9 +2518,9 @@ var Finale = ({ kind, accent }) => {
|
|
|
2410
2518
|
RN.View,
|
|
2411
2519
|
{ pointerEvents: "none", accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", style: { position: "absolute", left: 0, right: 0, top: 0, bottom: 0 } },
|
|
2412
2520
|
...bits.map((b, i) => {
|
|
2413
|
-
const
|
|
2414
|
-
const fade =
|
|
2415
|
-
const scale = b.shape === "star" ?
|
|
2521
|
+
const t2 = values[i];
|
|
2522
|
+
const fade = t2.interpolate({ inputRange: [0, 0.7, 1], outputRange: [1, 1, 0] });
|
|
2523
|
+
const scale = b.shape === "star" ? t2.interpolate({ inputRange: [0, 0.25, 1], outputRange: [0.2, 1.15, 0.6] }) : 1;
|
|
2416
2524
|
return (0, import_react2.createElement)(
|
|
2417
2525
|
A.View,
|
|
2418
2526
|
{
|
|
@@ -2429,9 +2537,9 @@ var Finale = ({ kind, accent }) => {
|
|
|
2429
2537
|
justifyContent: "center",
|
|
2430
2538
|
opacity: fade,
|
|
2431
2539
|
transform: [
|
|
2432
|
-
{ translateX:
|
|
2433
|
-
{ translateY:
|
|
2434
|
-
{ rotate:
|
|
2540
|
+
{ translateX: t2.interpolate({ inputRange: [0, 1], outputRange: [0, b.dx] }) },
|
|
2541
|
+
{ translateY: t2.interpolate({ inputRange: [0, 0.5, 1], outputRange: [0, b.mid, b.dy] }) },
|
|
2542
|
+
{ rotate: t2.interpolate({ inputRange: [0, 1], outputRange: ["0deg", `${b.rot}deg`] }) },
|
|
2435
2543
|
{ scale }
|
|
2436
2544
|
]
|
|
2437
2545
|
}
|
|
@@ -2443,10 +2551,9 @@ var Finale = ({ kind, accent }) => {
|
|
|
2443
2551
|
};
|
|
2444
2552
|
var LIGHT = { bg: "#ffffff", fg: "#1c1d20", muted: "#5d6068", soft: "#f4f5f7", line: "#e4e5e8" };
|
|
2445
2553
|
var DARK = { bg: "#17181b", fg: "#f1f2f4", muted: "#a9acb3", soft: "#232428", line: "#2e2f35" };
|
|
2446
|
-
var months = (n) => `${n} ${n === 1 ? "month" : "months"}`;
|
|
2447
2554
|
var MRRocketFlows = (props) => {
|
|
2448
2555
|
const [, setTick] = (0, import_react2.useState)(0);
|
|
2449
|
-
(0, import_react2.useEffect)(() => props.flows.subscribe(() => setTick((
|
|
2556
|
+
(0, import_react2.useEffect)(() => props.flows.subscribe(() => setTick((t2) => t2 + 1)), [props.flows]);
|
|
2450
2557
|
const fin = props.flows.finale?.() ?? null;
|
|
2451
2558
|
const accent = props.appearance?.accent ?? props.flows.accent?.() ?? "#3f76ff";
|
|
2452
2559
|
return (0, import_react2.createElement)(import_react2.Fragment, null, (0, import_react2.createElement)(FlowsBody, props), fin ? (0, import_react2.createElement)(Finale, { key: fin.key, kind: fin.kind, accent }) : null);
|
|
@@ -2456,8 +2563,8 @@ var FlowsBody = ({ flows, paywall, appearance = {} }) => {
|
|
|
2456
2563
|
const dark = appearance.theme === "dark" || (appearance.theme ?? "auto") === "auto" && scheme === "dark";
|
|
2457
2564
|
const p = { ...dark ? DARK : LIGHT, accent: appearance.accent ?? flows.accent?.() ?? "#3f76ff", danger: "#d92d20" };
|
|
2458
2565
|
const [, setTick] = (0, import_react2.useState)(0);
|
|
2459
|
-
(0, import_react2.useEffect)(() => flows.subscribe(() => setTick((
|
|
2460
|
-
(0, import_react2.useEffect)(() => paywall?.subscribe(() => setTick((
|
|
2566
|
+
(0, import_react2.useEffect)(() => flows.subscribe(() => setTick((t2) => t2 + 1)), [flows]);
|
|
2567
|
+
(0, import_react2.useEffect)(() => paywall?.subscribe(() => setTick((t2) => t2 + 1)), [paywall]);
|
|
2461
2568
|
const pw = paywall?.view() ?? null;
|
|
2462
2569
|
const v = flows.view();
|
|
2463
2570
|
const [comment, setComment] = (0, import_react2.useState)("");
|
|
@@ -2546,7 +2653,7 @@ var FlowsBody = ({ flows, paywall, appearance = {} }) => {
|
|
|
2546
2653
|
{ length: w.total },
|
|
2547
2654
|
(_, i) => (0, import_react2.createElement)(RN.View, { key: i, style: { width: 16, height: 4, borderRadius: 4, backgroundColor: i <= w.index ? p.accent : p.line } })
|
|
2548
2655
|
) : [],
|
|
2549
|
-
close("Close", host.close)
|
|
2656
|
+
close(t("Close"), host.close)
|
|
2550
2657
|
),
|
|
2551
2658
|
title(pg.title),
|
|
2552
2659
|
pg.body ? muted(pg.body, { fontSize: 15 }) : null,
|
|
@@ -2591,8 +2698,8 @@ var FlowsBody = ({ flows, paywall, appearance = {} }) => {
|
|
|
2591
2698
|
)
|
|
2592
2699
|
) : null,
|
|
2593
2700
|
w.error ? text2(w.error, { color: p.danger, fontSize: 13 }) : null,
|
|
2594
|
-
btn(w.busy ? "One moment\u2026" : pg.button || "Continue", "primary", host.next, w.busy || pg.kind === "plans" && !w.plan),
|
|
2595
|
-
w.index ? btn("Back", "quiet", host.back, w.busy) : null
|
|
2701
|
+
btn(w.busy ? t("One moment\u2026") : pg.button || t("Continue"), "primary", host.next, w.busy || pg.kind === "plans" && !w.plan),
|
|
2702
|
+
w.index ? btn(t("Back"), "quiet", host.back, w.busy) : null
|
|
2596
2703
|
],
|
|
2597
2704
|
host.close
|
|
2598
2705
|
);
|
|
@@ -2607,7 +2714,7 @@ var FlowsBody = ({ flows, paywall, appearance = {} }) => {
|
|
|
2607
2714
|
{ length: v.steps },
|
|
2608
2715
|
(_, i) => (0, import_react2.createElement)(RN.View, { key: i, style: { width: 16, height: 4, borderRadius: 4, backgroundColor: i < v.step ? p.accent : p.line } })
|
|
2609
2716
|
) : [],
|
|
2610
|
-
close("Close", flows.cancel.close)
|
|
2717
|
+
close(t("Close"), flows.cancel.close)
|
|
2611
2718
|
);
|
|
2612
2719
|
const error = v.error ? text2(v.error, { color: p.danger, fontSize: 13 }) : null;
|
|
2613
2720
|
let body = [];
|
|
@@ -2638,45 +2745,45 @@ var FlowsBody = ({ flows, paywall, appearance = {} }) => {
|
|
|
2638
2745
|
);
|
|
2639
2746
|
if (v.screen === "survey")
|
|
2640
2747
|
body = [
|
|
2641
|
-
title(v.flow.question || "Why are you leaving?"),
|
|
2748
|
+
title(v.flow.question || t("Why are you leaving?")),
|
|
2642
2749
|
choices(v.flow.reasons.map((r) => ({ key: r.id, label: r.label, on: v.reason === r.id, press: () => flows.cancel.pick(r.id) }))),
|
|
2643
|
-
btn("Continue", "primary", flows.cancel.next, !v.reason),
|
|
2644
|
-
btn("Never mind, keep my plan", "quiet", flows.cancel.close)
|
|
2750
|
+
btn(t("Continue"), "primary", flows.cancel.next, !v.reason),
|
|
2751
|
+
btn(t("Never mind, keep my plan"), "quiet", flows.cancel.close)
|
|
2645
2752
|
];
|
|
2646
2753
|
else if (v.screen === "followup" && v.followUp) {
|
|
2647
2754
|
const f = v.followUp;
|
|
2648
2755
|
const label = v.flow.reasons.find((r) => r.id === v.reason)?.label;
|
|
2649
2756
|
body = [
|
|
2650
|
-
label ? muted(
|
|
2757
|
+
label ? muted(tf("You said: {label}", { label })) : null,
|
|
2651
2758
|
title(f.question),
|
|
2652
|
-
f.answers.length ? choices(f.answers.map((a, i) => ({ key: String(i), label: a, on: v.answer === a, press: () => flows.cancel.answer(a) }))) : input("Your answer (optional)", v.answer ?? "", flows.cancel.answer),
|
|
2653
|
-
btn("Continue", "primary", flows.cancel.next),
|
|
2654
|
-
btn("Never mind, keep my plan", "quiet", flows.cancel.close)
|
|
2759
|
+
f.answers.length ? choices(f.answers.map((a, i) => ({ key: String(i), label: f.answerLabels?.[i] || a, on: v.answer === a, press: () => flows.cancel.answer(a) }))) : input(t("Your answer (optional)"), v.answer ?? "", flows.cancel.answer),
|
|
2760
|
+
btn(t("Continue"), "primary", flows.cancel.next),
|
|
2761
|
+
btn(t("Never mind, keep my plan"), "quiet", flows.cancel.close)
|
|
2655
2762
|
];
|
|
2656
2763
|
} else if (v.screen === "offer" && v.offer) {
|
|
2657
2764
|
const o = v.offer;
|
|
2658
2765
|
const label = v.flow.reasons.find((r) => r.id === v.reason)?.label;
|
|
2659
|
-
const detail = o.kind === "discount" && o.percent ? v.price ? `${money(discounted(v.price.amount, o.percent), v.price.currency)
|
|
2766
|
+
const detail = o.kind === "discount" && o.percent ? v.price ? `${tf("{price} instead of {old}", { price: money(discounted(v.price.amount, o.percent), v.price.currency), old: money(v.price.amount, v.price.currency) })}${o.months ? ` ${forMonths(o.months)}` : ""}` : `${tf("{percent}% off", { percent: o.percent })}${o.months ? ` ${forMonths(o.months)}` : ""}` : o.kind === "pause" && o.months ? tf("Pause for up to {months}. Your data stays.", { months: monthsWord(o.months) }) : o.kind === "downgrade" && o.planName ? o.planName : null;
|
|
2660
2767
|
body = [
|
|
2661
|
-
label ? muted(
|
|
2768
|
+
label ? muted(tf("You said: {label}", { label })) : null,
|
|
2662
2769
|
title(o.headline),
|
|
2663
2770
|
detail ? (0, import_react2.createElement)(RN.View, { style: { backgroundColor: p.soft, borderRadius: 10, padding: 12 } }, text2(detail, { fontWeight: "600" })) : null,
|
|
2664
|
-
o.kind === "contact" ? input("What would make you stay?") : null,
|
|
2665
|
-
btn(v.busy ? "One moment\u2026" : offerButton(o), "primary", () => flows.cancel.accept(comment), v.busy),
|
|
2666
|
-
btn("No thanks, continue cancelling", "quiet", flows.cancel.decline, v.busy)
|
|
2771
|
+
o.kind === "contact" ? input(t("What would make you stay?")) : null,
|
|
2772
|
+
btn(v.busy ? t("One moment\u2026") : offerButton(o), "primary", () => flows.cancel.accept(comment), v.busy),
|
|
2773
|
+
btn(t("No thanks, continue cancelling"), "quiet", flows.cancel.decline, v.busy)
|
|
2667
2774
|
];
|
|
2668
2775
|
} else
|
|
2669
2776
|
body = [
|
|
2670
2777
|
title(v.flow.confirm.title),
|
|
2671
2778
|
v.flow.confirm.body ? muted(v.flow.confirm.body, { fontSize: 15 }) : null,
|
|
2672
|
-
v.steps > 1 ? input("Anything else we should know? (optional)") : null,
|
|
2673
|
-
btn(v.busy ? "One moment\u2026" : v.flow.confirm.button, "danger", () => flows.cancel.confirm(comment), v.busy),
|
|
2674
|
-
btn("Keep my plan", "secondary", flows.cancel.close, v.busy)
|
|
2779
|
+
v.steps > 1 ? input(t("Anything else we should know? (optional)")) : null,
|
|
2780
|
+
btn(v.busy ? t("One moment\u2026") : v.flow.confirm.button, "danger", () => flows.cancel.confirm(comment), v.busy),
|
|
2781
|
+
btn(t("Keep my plan"), "secondary", flows.cancel.close, v.busy)
|
|
2675
2782
|
];
|
|
2676
2783
|
return sheet([top, ...body, error], flows.cancel.close);
|
|
2677
2784
|
}
|
|
2678
|
-
const media = (
|
|
2679
|
-
const m2 = mediaOf(
|
|
2785
|
+
const media = (s2) => {
|
|
2786
|
+
const m2 = mediaOf(s2);
|
|
2680
2787
|
if (!m2) return null;
|
|
2681
2788
|
if (m2.kind === "image")
|
|
2682
2789
|
return RN.Image ? (0, import_react2.createElement)(RN.Image, {
|
|
@@ -2688,12 +2795,12 @@ var FlowsBody = ({ flows, paywall, appearance = {} }) => {
|
|
|
2688
2795
|
}) : null;
|
|
2689
2796
|
const v2 = videoEmbed(m2.url);
|
|
2690
2797
|
const open = v2?.kind === "file" ? v2.src : m2.url;
|
|
2691
|
-
return RN.Linking ? btn(
|
|
2798
|
+
return RN.Linking ? btn(`\u25B6 ${t("Watch the video")}`, "secondary", () => void RN.Linking?.openURL(open).catch(() => {
|
|
2692
2799
|
})) : null;
|
|
2693
2800
|
};
|
|
2694
2801
|
if (v.type === "step" && v.step.kind === "question") {
|
|
2695
|
-
const
|
|
2696
|
-
const q =
|
|
2802
|
+
const s2 = v.step;
|
|
2803
|
+
const q = s2.question;
|
|
2697
2804
|
const ok = answerOk(q, v.answer);
|
|
2698
2805
|
return sheet(
|
|
2699
2806
|
[
|
|
@@ -2704,48 +2811,84 @@ var FlowsBody = ({ flows, paywall, appearance = {} }) => {
|
|
|
2704
2811
|
{ length: v.total },
|
|
2705
2812
|
(_, i) => (0, import_react2.createElement)(RN.View, { key: i, style: { width: 16, height: 4, borderRadius: 4, backgroundColor: i <= v.index ? p.accent : p.line } })
|
|
2706
2813
|
) : [],
|
|
2707
|
-
close("Close", flows.onboarding.dismiss)
|
|
2814
|
+
close(t("Close"), flows.onboarding.dismiss)
|
|
2708
2815
|
),
|
|
2709
2816
|
v.index === 0 && v.flow.icon ? (0, import_react2.createElement)(RN.View, { key: "icon" }, iconNode(v.flow.icon, 36, p.accent)) : null,
|
|
2710
|
-
title(
|
|
2711
|
-
|
|
2817
|
+
title(s2.title),
|
|
2818
|
+
s2.body ? muted(s2.body, { fontSize: 15 }) : null,
|
|
2712
2819
|
q?.type === "choice" ? (0, import_react2.createElement)(
|
|
2713
2820
|
RN.ScrollView,
|
|
2714
2821
|
{ key: "opts", style: { maxHeight: 320 }, contentContainerStyle: { gap: 6 }, accessibilityRole: "radiogroup" },
|
|
2715
|
-
...
|
|
2716
|
-
|
|
2822
|
+
...[...q.options ?? [], ...q.other ? [{ id: OTHER_PREFIX, label: q.otherLabel || t("Other") }] : []].map((o) => {
|
|
2823
|
+
const isOther = o.id === OTHER_PREFIX;
|
|
2824
|
+
const on = isOther ? !!v.answer?.startsWith(OTHER_PREFIX) : v.answer === o.id;
|
|
2825
|
+
return (0, import_react2.createElement)(
|
|
2717
2826
|
RN.Pressable,
|
|
2718
2827
|
{
|
|
2719
2828
|
key: o.id,
|
|
2720
|
-
onPress: () => flows.onboarding.answer(o.id),
|
|
2829
|
+
onPress: () => isOther && on ? void 0 : flows.onboarding.answer(o.id),
|
|
2721
2830
|
accessibilityRole: "radio",
|
|
2722
|
-
accessibilityState: { checked:
|
|
2831
|
+
accessibilityState: { checked: on },
|
|
2723
2832
|
style: {
|
|
2724
2833
|
minHeight: 44,
|
|
2725
2834
|
paddingHorizontal: 12,
|
|
2726
2835
|
borderRadius: 10,
|
|
2727
2836
|
borderWidth: 1,
|
|
2728
|
-
borderColor:
|
|
2729
|
-
backgroundColor:
|
|
2837
|
+
borderColor: on ? p.accent : p.line,
|
|
2838
|
+
backgroundColor: on ? p.soft : p.bg,
|
|
2730
2839
|
justifyContent: "center"
|
|
2731
2840
|
}
|
|
2732
2841
|
},
|
|
2733
2842
|
text2(o.label, { fontWeight: "500" })
|
|
2734
|
-
)
|
|
2735
|
-
)
|
|
2843
|
+
);
|
|
2844
|
+
}),
|
|
2845
|
+
q.other && v.answer?.startsWith(OTHER_PREFIX) ? (0, import_react2.createElement)(RN.TextInput, {
|
|
2846
|
+
key: "other",
|
|
2847
|
+
value: v.answer.slice(OTHER_PREFIX.length),
|
|
2848
|
+
onChangeText: (t2) => flows.onboarding.answer(OTHER_PREFIX + t2),
|
|
2849
|
+
onSubmitEditing: flows.onboarding.next,
|
|
2850
|
+
placeholder: t("Your answer"),
|
|
2851
|
+
placeholderTextColor: p.muted,
|
|
2852
|
+
maxLength: 490,
|
|
2853
|
+
accessibilityLabel: q.otherLabel || t("Other"),
|
|
2854
|
+
style: { height: 44, borderWidth: 1, borderColor: p.line, borderRadius: 10, paddingHorizontal: 12, color: p.fg }
|
|
2855
|
+
}) : null
|
|
2856
|
+
) : q?.type === "scale" ? (0, import_react2.createElement)(
|
|
2857
|
+
RN.View,
|
|
2858
|
+
{ key: "scale", style: { gap: 6 } },
|
|
2859
|
+
(0, import_react2.createElement)(
|
|
2860
|
+
RN.View,
|
|
2861
|
+
{ style: { flexDirection: "row", gap: 4 }, accessibilityRole: "radiogroup" },
|
|
2862
|
+
...scaleOf(q).map((n) => {
|
|
2863
|
+
const on = v.answer === String(n);
|
|
2864
|
+
return (0, import_react2.createElement)(
|
|
2865
|
+
RN.Pressable,
|
|
2866
|
+
{
|
|
2867
|
+
key: n,
|
|
2868
|
+
onPress: () => flows.onboarding.answer(String(n)),
|
|
2869
|
+
accessibilityRole: "radio",
|
|
2870
|
+
accessibilityState: { checked: on },
|
|
2871
|
+
style: { flex: 1, height: 44, borderRadius: 8, borderWidth: 1, borderColor: on ? p.accent : p.line, backgroundColor: on ? p.accent : p.bg, alignItems: "center", justifyContent: "center" }
|
|
2872
|
+
},
|
|
2873
|
+
text2(String(n), { fontWeight: "500", color: on ? "#fff" : p.fg })
|
|
2874
|
+
);
|
|
2875
|
+
})
|
|
2876
|
+
),
|
|
2877
|
+
q.lowLabel || q.highLabel ? (0, import_react2.createElement)(RN.View, { style: { flexDirection: "row", justifyContent: "space-between" } }, muted(q.lowLabel || ""), muted(q.highLabel || "")) : null
|
|
2736
2878
|
) : q ? (0, import_react2.createElement)(RN.TextInput, {
|
|
2737
2879
|
key: "answer",
|
|
2738
2880
|
value: v.answer ?? "",
|
|
2739
|
-
onChangeText: (
|
|
2881
|
+
onChangeText: (t2) => flows.onboarding.answer(t2),
|
|
2740
2882
|
onSubmitEditing: flows.onboarding.next,
|
|
2741
|
-
placeholder: q.placeholder || "Your answer",
|
|
2883
|
+
placeholder: q.placeholder || t("Your answer"),
|
|
2742
2884
|
placeholderTextColor: p.muted,
|
|
2743
2885
|
maxLength: 500,
|
|
2744
|
-
accessibilityLabel:
|
|
2886
|
+
accessibilityLabel: s2.title,
|
|
2745
2887
|
style: { height: 44, borderWidth: 1, borderColor: p.line, borderRadius: 10, paddingHorizontal: 12, color: p.fg }
|
|
2746
2888
|
}) : null,
|
|
2747
|
-
btn(
|
|
2748
|
-
|
|
2889
|
+
btn(s2.button || (v.last ? t("Done") : t("Continue")), "primary", flows.onboarding.next, !ok),
|
|
2890
|
+
q?.optional ? btn(q.skipLabel || t("Skip"), "quiet", flows.onboarding.skip) : null,
|
|
2891
|
+
v.back ? btn(t("Back"), "quiet", flows.onboarding.back) : null
|
|
2749
2892
|
],
|
|
2750
2893
|
flows.onboarding.dismiss
|
|
2751
2894
|
);
|
|
@@ -2756,14 +2899,14 @@ var FlowsBody = ({ flows, paywall, appearance = {} }) => {
|
|
|
2756
2899
|
(0, import_react2.createElement)(
|
|
2757
2900
|
RN.View,
|
|
2758
2901
|
{ style: { flexDirection: "row", alignItems: "center" } },
|
|
2759
|
-
v.total > 1 ? muted(
|
|
2760
|
-
close("Close the tour", flows.onboarding.dismiss)
|
|
2902
|
+
v.total > 1 ? muted(tf("Step {n} of {total}", { n: v.index + 1, total: v.total })) : null,
|
|
2903
|
+
close(t("Close the tour"), flows.onboarding.dismiss)
|
|
2761
2904
|
),
|
|
2762
2905
|
v.step.kind === "modal" && v.flow.icon ? (0, import_react2.createElement)(RN.View, { key: "icon" }, iconNode(v.flow.icon, 36, p.accent)) : null,
|
|
2763
2906
|
media(v.step),
|
|
2764
2907
|
title(v.step.title),
|
|
2765
2908
|
v.step.body ? muted(v.step.body, { fontSize: 15 }) : null,
|
|
2766
|
-
btn(v.step.button || (v.last ? "Done" : "Next"), "primary", flows.onboarding.next)
|
|
2909
|
+
btn(v.step.button || (v.last ? t("Done") : t("Next")), "primary", flows.onboarding.next)
|
|
2767
2910
|
],
|
|
2768
2911
|
flows.onboarding.dismiss
|
|
2769
2912
|
);
|
|
@@ -2777,9 +2920,9 @@ var FlowsBody = ({ flows, paywall, appearance = {} }) => {
|
|
|
2777
2920
|
RN.View,
|
|
2778
2921
|
{ key: "t", style: { flexDirection: "row", alignItems: "center" } },
|
|
2779
2922
|
title(v.flow.name),
|
|
2780
|
-
close("Hide the checklist", flows.onboarding.dismiss)
|
|
2923
|
+
close(t("Hide the checklist"), flows.onboarding.dismiss)
|
|
2781
2924
|
),
|
|
2782
|
-
muted(
|
|
2925
|
+
muted(tf("{done} of {total} done", { done, total: v.items.length })),
|
|
2783
2926
|
...v.items.map(
|
|
2784
2927
|
({ step, done: d, opens }) => (0, import_react2.createElement)(
|
|
2785
2928
|
RN.Pressable,
|
|
@@ -2831,14 +2974,14 @@ var FlowsBody = ({ flows, paywall, appearance = {} }) => {
|
|
|
2831
2974
|
RN.View,
|
|
2832
2975
|
{ key: "t", style: { flexDirection: "row", alignItems: "center", gap: 8 } },
|
|
2833
2976
|
(0, import_react2.createElement)(RN.View, { style: { flex: 1 } }, text2(m.title, { fontWeight: "700" }), m.body ? muted(m.body) : null),
|
|
2834
|
-
close("Dismiss", flows.message.dismiss)
|
|
2977
|
+
close(t("Dismiss"), flows.message.dismiss)
|
|
2835
2978
|
),
|
|
2836
2979
|
m.button ? btn(m.button, "primary", flows.message.click) : null
|
|
2837
2980
|
])
|
|
2838
2981
|
);
|
|
2839
2982
|
return sheet(
|
|
2840
2983
|
[
|
|
2841
|
-
(0, import_react2.createElement)(RN.View, { key: "t", style: { flexDirection: "row" } }, close("Dismiss", flows.message.dismiss)),
|
|
2984
|
+
(0, import_react2.createElement)(RN.View, { key: "t", style: { flexDirection: "row" } }, close(t("Dismiss"), flows.message.dismiss)),
|
|
2842
2985
|
title(m.title),
|
|
2843
2986
|
m.body ? muted(m.body, { fontSize: 15 }) : null,
|
|
2844
2987
|
m.button ? btn(m.button, "primary", flows.message.click) : null
|
|
@@ -2912,10 +3055,10 @@ var expoPush = () => {
|
|
|
2912
3055
|
},
|
|
2913
3056
|
token: async (projectId) => {
|
|
2914
3057
|
if (RN2.Platform.OS === "android" && n.setNotificationChannelAsync)
|
|
2915
|
-
await n.setNotificationChannelAsync("default", { name: "Default", importance: n.AndroidImportance?.HIGH ?? 4 });
|
|
3058
|
+
await n.setNotificationChannelAsync("default", { name: t("Default"), importance: n.AndroidImportance?.HIGH ?? 4 });
|
|
2916
3059
|
const id = projectId ?? constants.expoConfig?.extra?.eas?.projectId ?? constants.easConfig?.projectId;
|
|
2917
|
-
const
|
|
2918
|
-
return typeof
|
|
3060
|
+
const push = await n.getExpoPushTokenAsync(id ? { projectId: id } : void 0);
|
|
3061
|
+
return typeof push?.data === "string" && push.data ? push.data : null;
|
|
2919
3062
|
},
|
|
2920
3063
|
onOpen: (listener) => {
|
|
2921
3064
|
const sub = n.addNotificationResponseReceivedListener((r) => listener(dataOf(r) ?? {}));
|
|
@@ -2926,8 +3069,8 @@ var expoPush = () => {
|
|
|
2926
3069
|
};
|
|
2927
3070
|
var asyncStorage = () => {
|
|
2928
3071
|
const m = asyncStorageModule;
|
|
2929
|
-
const
|
|
2930
|
-
return
|
|
3072
|
+
const s2 = m?.default ?? m;
|
|
3073
|
+
return s2 && typeof s2.getItem === "function" && typeof s2.setItem === "function" && typeof s2.removeItem === "function" ? s2 : null;
|
|
2931
3074
|
};
|
|
2932
3075
|
var DEVICE_TYPES = { 1: "phone", 2: "tablet", 3: "desktop", 4: "tv" };
|
|
2933
3076
|
var text = (v, max = 80) => typeof v === "string" && v.trim() ? v.trim().slice(0, max) : typeof v === "number" ? String(v) : void 0;
|
|
@@ -2971,10 +3114,10 @@ var recorderDeps = () => {
|
|
|
2971
3114
|
}
|
|
2972
3115
|
return {
|
|
2973
3116
|
capture: async (width) => {
|
|
2974
|
-
const
|
|
2975
|
-
if (!
|
|
3117
|
+
const s2 = RN2.Dimensions.get("screen");
|
|
3118
|
+
if (!s2?.width) return null;
|
|
2976
3119
|
const w = Math.round(width);
|
|
2977
|
-
const h3 = Math.round(
|
|
3120
|
+
const h3 = Math.round(s2.height / s2.width * width);
|
|
2978
3121
|
const img = await capture({ format: "jpg", quality: 0.5, result: "base64", width: w, height: h3 });
|
|
2979
3122
|
return img ? { img: img.replace(/^data:image\/\w+;base64,/, ""), w, h: h3 } : null;
|
|
2980
3123
|
},
|
|
@@ -3001,8 +3144,8 @@ var nativeDeps = (overrides = {}) => ({
|
|
|
3001
3144
|
context: readContext,
|
|
3002
3145
|
appId: text(expoApplication?.applicationId, 150),
|
|
3003
3146
|
screen: () => {
|
|
3004
|
-
const
|
|
3005
|
-
return
|
|
3147
|
+
const s2 = RN2.Dimensions.get("screen");
|
|
3148
|
+
return s2 ? { w: s2.width, h: s2.height } : void 0;
|
|
3006
3149
|
},
|
|
3007
3150
|
initialUrl: () => RN2.Linking.getInitialURL(),
|
|
3008
3151
|
openUrl: (url) => void RN2.Linking.openURL(url).catch(() => void 0),
|
|
@@ -3100,7 +3243,7 @@ var kit = (p) => {
|
|
|
3100
3243
|
RN3.View,
|
|
3101
3244
|
{ style: { flexDirection: "row", alignItems: "center", paddingHorizontal: 16, paddingTop: 16, paddingBottom: 12, borderBottomWidth: 1, borderBottomColor: p.line } },
|
|
3102
3245
|
title(heading),
|
|
3103
|
-
closeX("Close", onClose)
|
|
3246
|
+
closeX(t("Close"), onClose)
|
|
3104
3247
|
),
|
|
3105
3248
|
...children
|
|
3106
3249
|
)
|
|
@@ -3108,13 +3251,13 @@ var kit = (p) => {
|
|
|
3108
3251
|
return { text: text2, muted, title, btn, closeX, field, chip, page };
|
|
3109
3252
|
};
|
|
3110
3253
|
var timeAgo = (iso) => {
|
|
3111
|
-
const
|
|
3112
|
-
if (Number.isNaN(
|
|
3113
|
-
const d = Math.max(0, Date.now() -
|
|
3114
|
-
if (d < 60) return "just now";
|
|
3115
|
-
if (d < 3600) return
|
|
3116
|
-
if (d < 86400) return
|
|
3117
|
-
return
|
|
3254
|
+
const at = new Date(iso).getTime();
|
|
3255
|
+
if (Number.isNaN(at)) return "";
|
|
3256
|
+
const d = Math.max(0, Date.now() - at) / 1e3;
|
|
3257
|
+
if (d < 60) return t("just now");
|
|
3258
|
+
if (d < 3600) return tf("{n}m ago", { n: Math.floor(d / 60) });
|
|
3259
|
+
if (d < 86400) return tf("{n}h ago", { n: Math.floor(d / 3600) });
|
|
3260
|
+
return tf("{n}d ago", { n: Math.floor(d / 86400) });
|
|
3118
3261
|
};
|
|
3119
3262
|
var opener = () => {
|
|
3120
3263
|
let open = false;
|
|
@@ -3153,8 +3296,8 @@ var SurveySheet = ({ survey, api, p, onDone }) => {
|
|
|
3153
3296
|
}, [api, survey.id]);
|
|
3154
3297
|
(0, import_react3.useEffect)(() => {
|
|
3155
3298
|
if (!done) return;
|
|
3156
|
-
const
|
|
3157
|
-
return () => clearTimeout(
|
|
3299
|
+
const t2 = setTimeout(onDone, 2500);
|
|
3300
|
+
return () => clearTimeout(t2);
|
|
3158
3301
|
}, [done, onDone]);
|
|
3159
3302
|
const finish = (completed, all = answers) => {
|
|
3160
3303
|
if (finished.current) return;
|
|
@@ -3166,7 +3309,7 @@ var SurveySheet = ({ survey, api, p, onDone }) => {
|
|
|
3166
3309
|
onDone();
|
|
3167
3310
|
};
|
|
3168
3311
|
const next = () => {
|
|
3169
|
-
if (q.required && !isAnswered(value)) return setError("Please answer this question.");
|
|
3312
|
+
if (q.required && !isAnswered(value)) return setError(t("Please answer this question."));
|
|
3170
3313
|
setError("");
|
|
3171
3314
|
if (!last) return setStep(step + 1);
|
|
3172
3315
|
finish(true);
|
|
@@ -3196,14 +3339,14 @@ var SurveySheet = ({ survey, api, p, onDone }) => {
|
|
|
3196
3339
|
onPress: () => pick(n),
|
|
3197
3340
|
accessibilityRole: "radio",
|
|
3198
3341
|
accessibilityState: { checked: on },
|
|
3199
|
-
accessibilityLabel:
|
|
3342
|
+
accessibilityLabel: tf("{n} of {total}", { n, total: scale - 1 + start }),
|
|
3200
3343
|
style: { width: 40, height: 40, borderRadius: 8, alignItems: "center", justifyContent: "center", borderWidth: 1, borderColor: on ? p.accent : p.line, backgroundColor: on ? p.accent : p.bg }
|
|
3201
3344
|
},
|
|
3202
3345
|
k.text(String(n), { color: on ? "#ffffff" : p.fg, fontWeight: "600" })
|
|
3203
3346
|
);
|
|
3204
3347
|
})
|
|
3205
3348
|
),
|
|
3206
|
-
q.low || q.high || q.kind === "nps" ? (0, import_react3.createElement)(RN3.View, { style: { flexDirection: "row", justifyContent: "space-between" } }, k.muted(q.low ?? (q.kind === "nps" ? "Not likely" : "")), k.muted(q.high ?? (q.kind === "nps" ? "Very likely" : ""))) : null
|
|
3349
|
+
q.low || q.high || q.kind === "nps" ? (0, import_react3.createElement)(RN3.View, { style: { flexDirection: "row", justifyContent: "space-between" } }, k.muted(q.low ?? (q.kind === "nps" ? t("Not likely") : "")), k.muted(q.high ?? (q.kind === "nps" ? t("Very likely") : ""))) : null
|
|
3207
3350
|
);
|
|
3208
3351
|
} else if (q.kind === "single" || q.kind === "multi") {
|
|
3209
3352
|
const multi = q.kind === "multi";
|
|
@@ -3211,7 +3354,7 @@ var SurveySheet = ({ survey, api, p, onDone }) => {
|
|
|
3211
3354
|
input = (0, import_react3.createElement)(
|
|
3212
3355
|
RN3.View,
|
|
3213
3356
|
{ style: { gap: 6 } },
|
|
3214
|
-
...(q.options ?? []).map((o) => {
|
|
3357
|
+
...(q.options ?? []).map((o, i) => {
|
|
3215
3358
|
const on = multi ? picked.includes(o) : value === o;
|
|
3216
3359
|
return (0, import_react3.createElement)(
|
|
3217
3360
|
RN3.Pressable,
|
|
@@ -3222,24 +3365,25 @@ var SurveySheet = ({ survey, api, p, onDone }) => {
|
|
|
3222
3365
|
accessibilityState: { checked: on },
|
|
3223
3366
|
style: { minHeight: 44, paddingHorizontal: 12, borderRadius: 10, justifyContent: "center", borderWidth: 1, borderColor: on ? p.accent : p.line, backgroundColor: on ? p.soft : p.bg }
|
|
3224
3367
|
},
|
|
3225
|
-
|
|
3368
|
+
// The label in the person's language; what is recorded stays the option itself.
|
|
3369
|
+
k.text(q.labels?.[i] || o, { fontWeight: "500" })
|
|
3226
3370
|
);
|
|
3227
3371
|
})
|
|
3228
3372
|
);
|
|
3229
|
-
} else input = k.field({ value: value ?? "", onChange: pick, placeholder: q.placeholder ?? "Type your answer", multiline: true });
|
|
3230
|
-
const body = done ? [k.text(survey.thanks || "Thanks for your feedback.", { fontWeight: "600", textAlign: "center", paddingVertical: 16 })] : [
|
|
3373
|
+
} else input = k.field({ value: value ?? "", onChange: pick, placeholder: q.placeholder ?? t("Type your answer"), multiline: true });
|
|
3374
|
+
const body = done ? [k.text(survey.thanks || t("Thanks for your feedback."), { fontWeight: "600", textAlign: "center", paddingVertical: 16 })] : [
|
|
3231
3375
|
(0, import_react3.createElement)(
|
|
3232
3376
|
RN3.View,
|
|
3233
3377
|
{ key: "top", style: { flexDirection: "row", alignItems: "center", gap: 4 } },
|
|
3234
3378
|
...survey.questions.map((x, i) => (0, import_react3.createElement)(RN3.View, { key: x.key, style: { width: 16, height: 4, borderRadius: 4, backgroundColor: i <= step ? p.accent : p.line } })),
|
|
3235
3379
|
(0, import_react3.createElement)(RN3.View, { style: { flex: 1 } }),
|
|
3236
|
-
k.closeX("Close survey", dismiss)
|
|
3380
|
+
k.closeX(t("Close survey"), dismiss)
|
|
3237
3381
|
),
|
|
3238
|
-
(0, import_react3.createElement)(RN3.Text, { key: "q", accessibilityRole: "header", style: { color: p.fg, fontSize: 17, fontWeight: "700" } }, q.title, q.required ? "" : (0, import_react3.createElement)(RN3.Text, { style: { color: p.muted, fontSize: 13, fontWeight: "400" } }, "
|
|
3382
|
+
(0, import_react3.createElement)(RN3.Text, { key: "q", accessibilityRole: "header", style: { color: p.fg, fontSize: 17, fontWeight: "700" } }, q.title, q.required ? "" : (0, import_react3.createElement)(RN3.Text, { style: { color: p.muted, fontSize: 13, fontWeight: "400" } }, ` ${t("optional")}`)),
|
|
3239
3383
|
q.description ? k.muted(q.description) : null,
|
|
3240
3384
|
input,
|
|
3241
3385
|
error ? k.text(error, { color: p.danger, fontSize: 13 }) : null,
|
|
3242
|
-
k.btn(last ? "Send" : "Next", "primary", next)
|
|
3386
|
+
k.btn(last ? t("Send") : t("Next"), "primary", next)
|
|
3243
3387
|
];
|
|
3244
3388
|
return (0, import_react3.createElement)(
|
|
3245
3389
|
RN3.Modal,
|
|
@@ -3272,9 +3416,9 @@ var MRRocketSurveys = ({ client, appearance }) => {
|
|
|
3272
3416
|
const api = client.api();
|
|
3273
3417
|
if (stop || !api) return;
|
|
3274
3418
|
api.survey("", ctrl.signal).then((r) => {
|
|
3275
|
-
const
|
|
3276
|
-
if (stop || !
|
|
3277
|
-
setSurvey({ s, api });
|
|
3419
|
+
const s2 = r.survey;
|
|
3420
|
+
if (stop || !s2 || !s2.questions.length || client.isSurveyDone(s2.id)) return;
|
|
3421
|
+
setSurvey({ s: s2, api });
|
|
3278
3422
|
}).catch(() => void 0);
|
|
3279
3423
|
});
|
|
3280
3424
|
}, 3e3);
|
|
@@ -3312,7 +3456,7 @@ var FeedbackSheet = ({ client, p, title }) => {
|
|
|
3312
3456
|
const user = client.getUser();
|
|
3313
3457
|
(0, import_react3.useEffect)(() => {
|
|
3314
3458
|
const api = client.api();
|
|
3315
|
-
if (!api) return setError("Feedback is off while tracking is off.");
|
|
3459
|
+
if (!api) return setError(t("Feedback is off while tracking is off."));
|
|
3316
3460
|
const ctrl = new AbortController();
|
|
3317
3461
|
api.list({ sort: "top" }, ctrl.signal).then((l) => {
|
|
3318
3462
|
setPosts(l.posts);
|
|
@@ -3345,8 +3489,8 @@ var FeedbackSheet = ({ client, p, title }) => {
|
|
|
3345
3489
|
const tabs = (0, import_react3.createElement)(
|
|
3346
3490
|
RN3.View,
|
|
3347
3491
|
{ style: { flexDirection: "row", gap: 8, paddingHorizontal: 16, paddingTop: 12 } },
|
|
3348
|
-
k.chip("Top requests", tab === "top", () => setTab("top")),
|
|
3349
|
-
k.chip("Suggest", tab === "new", () => {
|
|
3492
|
+
k.chip(t("Top requests"), tab === "top", () => setTab("top")),
|
|
3493
|
+
k.chip(t("Suggest"), tab === "new", () => {
|
|
3350
3494
|
setTab("new");
|
|
3351
3495
|
setSent(false);
|
|
3352
3496
|
})
|
|
@@ -3355,8 +3499,8 @@ var FeedbackSheet = ({ client, p, title }) => {
|
|
|
3355
3499
|
RN3.ScrollView,
|
|
3356
3500
|
{ style: { flex: 1 }, contentContainerStyle: { padding: 16, gap: 10 } },
|
|
3357
3501
|
error ? k.text(error, { color: p.danger, fontSize: 13 }) : null,
|
|
3358
|
-
posts === null && !error ? k.muted("Loading\u2026") : null,
|
|
3359
|
-
posts?.length === 0 ? k.muted("No requests yet. Be the first to suggest one.") : null,
|
|
3502
|
+
posts === null && !error ? k.muted(t("Loading\u2026")) : null,
|
|
3503
|
+
posts?.length === 0 ? k.muted(t("No requests yet. Be the first to suggest one.")) : null,
|
|
3360
3504
|
...(posts ?? []).map(
|
|
3361
3505
|
(post) => (0, import_react3.createElement)(
|
|
3362
3506
|
RN3.View,
|
|
@@ -3367,7 +3511,7 @@ var FeedbackSheet = ({ client, p, title }) => {
|
|
|
3367
3511
|
onPress: () => vote(post),
|
|
3368
3512
|
accessibilityRole: "button",
|
|
3369
3513
|
accessibilityState: { selected: post.voted },
|
|
3370
|
-
accessibilityLabel:
|
|
3514
|
+
accessibilityLabel: post.voted ? tf("Remove your vote from {title}. {votes}", { title: post.title, votes: post.votes === 1 ? t("1 vote") : tf("{n} votes", { n: post.votes }) }) : tf("Upvote {title}. {votes}", { title: post.title, votes: post.votes === 1 ? t("1 vote") : tf("{n} votes", { n: post.votes }) }),
|
|
3371
3515
|
style: { width: 44, minHeight: 48, borderRadius: 10, alignItems: "center", justifyContent: "center", borderWidth: 1, borderColor: post.voted ? p.accent : p.line }
|
|
3372
3516
|
},
|
|
3373
3517
|
k.text("\u25B2", { fontSize: 11, color: post.voted ? p.accent : p.muted }),
|
|
@@ -3380,36 +3524,39 @@ var FeedbackSheet = ({ client, p, title }) => {
|
|
|
3380
3524
|
const form = (0, import_react3.createElement)(
|
|
3381
3525
|
RN3.ScrollView,
|
|
3382
3526
|
{ style: { flex: 1 }, contentContainerStyle: { padding: 16, gap: 10 }, keyboardShouldPersistTaps: "handled" },
|
|
3383
|
-
sent ? k.text("Thanks! Your request is on the board.", { fontWeight: "600" }) : null,
|
|
3527
|
+
sent ? k.text(t("Thanks! Your request is on the board."), { fontWeight: "600" }) : null,
|
|
3384
3528
|
boards.length > 1 ? (0, import_react3.createElement)(RN3.View, { style: { flexDirection: "row", flexWrap: "wrap", gap: 6 } }, ...boards.map((b) => k.chip(b.label, board === b.id, () => setBoard(b.id), b.id))) : null,
|
|
3385
|
-
k.field({ value: subject, onChange: setSubject, placeholder: "Short title", max: 120 }),
|
|
3386
|
-
k.field({ value: details, onChange: setDetails, placeholder: "Details (optional)", multiline: true }),
|
|
3529
|
+
k.field({ value: subject, onChange: setSubject, placeholder: t("Short title"), max: 120 }),
|
|
3530
|
+
k.field({ value: details, onChange: setDetails, placeholder: t("Details (optional)"), multiline: true }),
|
|
3387
3531
|
...user?.email ? [] : [
|
|
3388
|
-
k.muted("Your name and email are optional; they let the team follow up."),
|
|
3389
|
-
k.field({ value: name, onChange: setName, placeholder: "Your name (optional)", max: 100 }),
|
|
3390
|
-
k.field({ value: email, onChange: setEmail, placeholder: "Your email (optional)", max: 200, email: true })
|
|
3532
|
+
k.muted(t("Your name and email are optional; they let the team follow up.")),
|
|
3533
|
+
k.field({ value: name, onChange: setName, placeholder: t("Your name (optional)"), max: 100 }),
|
|
3534
|
+
k.field({ value: email, onChange: setEmail, placeholder: t("Your email (optional)"), max: 200, email: true })
|
|
3391
3535
|
],
|
|
3392
3536
|
error ? k.text(error, { color: p.danger, fontSize: 13 }) : null,
|
|
3393
|
-
k.btn(sending ? "Sending\u2026" : "Send request", "primary", submit, subject.trim().length < 3 || sending)
|
|
3537
|
+
k.btn(sending ? t("Sending\u2026") : t("Send request"), "primary", submit, subject.trim().length < 3 || sending)
|
|
3394
3538
|
);
|
|
3395
3539
|
return k.page(title, close, [tabs, tab === "top" ? list : form]);
|
|
3396
3540
|
};
|
|
3397
3541
|
var MRRocketFeedback = ({ client, appearance, options = {} }) => {
|
|
3398
3542
|
const p = usePalette(appearance);
|
|
3399
3543
|
const open = feedbackOpen.useOpen();
|
|
3400
|
-
return open ? (0, import_react3.createElement)(FeedbackSheet, { client, p, title: options.title ?? "Feedback" }) : null;
|
|
3544
|
+
return open ? (0, import_react3.createElement)(FeedbackSheet, { client, p, title: options.title ?? t("Feedback") }) : null;
|
|
3401
3545
|
};
|
|
3402
3546
|
var POLL_OPEN_MS = 4e3;
|
|
3403
3547
|
var POLL_WAITING_MS = 8e3;
|
|
3404
3548
|
var POLL_IDLE_MS = 3e4;
|
|
3549
|
+
var POLL_QUIET_MS = 6e4;
|
|
3405
3550
|
var WAITING_FOR_MS = 10 * 6e4;
|
|
3406
3551
|
var EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/;
|
|
3407
|
-
var whoLabel =
|
|
3408
|
-
var who = (from, agentName) => from === "ai" && agentName?.trim() || whoLabel
|
|
3552
|
+
var whoLabel = (from) => from === "visitor" ? t("You") : from === "ai" ? t("AI agent") : t("Team");
|
|
3553
|
+
var who = (from, agentName) => from === "ai" && agentName?.trim() || whoLabel(from);
|
|
3409
3554
|
var msgView = "home";
|
|
3555
|
+
var msgChat = null;
|
|
3410
3556
|
var messengerOpen = opener();
|
|
3411
|
-
var openAt = (v = "home") => {
|
|
3557
|
+
var openAt = (v = "home", chatId = null) => {
|
|
3412
3558
|
msgView = v;
|
|
3559
|
+
msgChat = chatId;
|
|
3413
3560
|
messengerOpen.set(true);
|
|
3414
3561
|
};
|
|
3415
3562
|
var useMessenger = () => ({
|
|
@@ -3436,7 +3583,10 @@ try {
|
|
|
3436
3583
|
imagePicker = null;
|
|
3437
3584
|
}
|
|
3438
3585
|
var PHOTO_TYPES = { jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", webp: "image/webp", gif: "image/gif" };
|
|
3439
|
-
var
|
|
3586
|
+
var samePlace = (a, b) => a.chatId === b.chatId && a.fresh === b.fresh;
|
|
3587
|
+
var shownChat = (at, chats) => at.fresh ? null : at.chatId ?? (chats.length === 1 ? chats[0].id : null);
|
|
3588
|
+
var isAnswer = (m) => m.from === "team" || m.from === "ai";
|
|
3589
|
+
var lineOf = (m) => m.body || (m.attachments?.length === 1 ? tf("Sent {name}", { name: m.attachments[0].name }) : m.attachments?.length ? tf("Sent {n} files", { n: m.attachments.length }) : "");
|
|
3440
3590
|
var svg2 = null;
|
|
3441
3591
|
try {
|
|
3442
3592
|
svg2 = require("react-native-svg");
|
|
@@ -3473,42 +3623,80 @@ var HU = {
|
|
|
3473
3623
|
var hairline = { shadowColor: "#000", shadowOpacity: 0.07, shadowRadius: 3, shadowOffset: { width: 0, height: 1 }, elevation: 1 };
|
|
3474
3624
|
var useChat = (client, o, open, view) => {
|
|
3475
3625
|
const [thread, setThread] = (0, import_react3.useState)(null);
|
|
3626
|
+
const [chats, setChats] = (0, import_react3.useState)([]);
|
|
3627
|
+
const [hasEmail, setHasEmail] = (0, import_react3.useState)(false);
|
|
3628
|
+
const [place, setPlace] = (0, import_react3.useState)({ chatId: null, fresh: false });
|
|
3476
3629
|
const [loaded, setLoaded] = (0, import_react3.useState)(false);
|
|
3477
3630
|
const [error, setError] = (0, import_react3.useState)("");
|
|
3478
3631
|
const [unseen, setUnseen] = (0, import_react3.useState)([]);
|
|
3479
|
-
const known = (0, import_react3.useRef)(
|
|
3632
|
+
const known = (0, import_react3.useRef)(/* @__PURE__ */ new Map());
|
|
3633
|
+
const seeded = (0, import_react3.useRef)(false);
|
|
3480
3634
|
const watching = open && view === "messages";
|
|
3481
3635
|
const watch = (0, import_react3.useRef)(watching);
|
|
3482
3636
|
watch.current = watching;
|
|
3637
|
+
const at = (0, import_react3.useRef)(place);
|
|
3638
|
+
const list = (0, import_react3.useRef)(chats);
|
|
3639
|
+
const readFailed = (0, import_react3.useRef)(false);
|
|
3483
3640
|
const [active, setActive] = (0, import_react3.useState)(RN3.AppState.currentState === "active");
|
|
3484
3641
|
(0, import_react3.useEffect)(() => {
|
|
3485
3642
|
const sub = RN3.AppState.addEventListener("change", (st) => setActive(st === "active"));
|
|
3486
3643
|
return () => sub.remove();
|
|
3487
3644
|
}, []);
|
|
3645
|
+
const enter = (0, import_react3.useCallback)((chatId, fresh = false) => {
|
|
3646
|
+
const next = { chatId, fresh };
|
|
3647
|
+
if (samePlace(at.current, next)) return;
|
|
3648
|
+
at.current = next;
|
|
3649
|
+
setPlace(next);
|
|
3650
|
+
setThread(null);
|
|
3651
|
+
setError("");
|
|
3652
|
+
}, []);
|
|
3488
3653
|
const take = (0, import_react3.useCallback)(
|
|
3489
|
-
(
|
|
3490
|
-
const
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
}
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3654
|
+
(t2) => {
|
|
3655
|
+
const read = t2.conversation;
|
|
3656
|
+
const last = t2.messages.at(-1);
|
|
3657
|
+
const all = t2.conversations ?? (read && last ? [{ id: read.id, status: read.status, subject: lineOf(t2.messages[0]), last }] : []);
|
|
3658
|
+
const fresh = [];
|
|
3659
|
+
const learn = (chat, answers, newest) => {
|
|
3660
|
+
const was = known.current.get(chat);
|
|
3661
|
+
const since = was ?? (seeded.current ? "" : void 0);
|
|
3662
|
+
if (since !== void 0) fresh.push(...answers.filter((m) => m.at > since).map((m) => ({ ...m, chat })));
|
|
3663
|
+
known.current.set(chat, newest ?? was ?? "");
|
|
3664
|
+
};
|
|
3665
|
+
for (const c of all) if (c.id !== read?.id) learn(c.id, c.last && isAnswer(c.last) ? [c.last] : [], c.last?.at);
|
|
3666
|
+
if (read) learn(read.id, t2.messages.filter(isAnswer), last?.at);
|
|
3667
|
+
seeded.current = true;
|
|
3668
|
+
if (fresh.length && o.haptics !== false) buzz();
|
|
3669
|
+
const shown2 = shownChat(at.current, all);
|
|
3670
|
+
const counted = fresh.filter((m) => !watch.current || m.chat !== shown2);
|
|
3671
|
+
setUnseen((u) => [...u.filter((m) => all.some((c) => c.id === m.chat)), ...counted]);
|
|
3672
|
+
list.current = all;
|
|
3673
|
+
setChats(all);
|
|
3674
|
+
setHasEmail(t2.hasEmail);
|
|
3675
|
+
if (at.current.chatId === null || read?.id === at.current.chatId) setThread(t2);
|
|
3501
3676
|
},
|
|
3502
3677
|
[o.haptics]
|
|
3503
3678
|
);
|
|
3679
|
+
const arrived = (0, import_react3.useCallback)(
|
|
3680
|
+
(from, t2) => {
|
|
3681
|
+
const went = t2.conversation?.id;
|
|
3682
|
+
if (went && samePlace(at.current, from)) {
|
|
3683
|
+
at.current = { chatId: went, fresh: false };
|
|
3684
|
+
setPlace(at.current);
|
|
3685
|
+
}
|
|
3686
|
+
take(t2);
|
|
3687
|
+
},
|
|
3688
|
+
[take]
|
|
3689
|
+
);
|
|
3690
|
+
const shown = shownChat(place, chats);
|
|
3504
3691
|
(0, import_react3.useEffect)(() => {
|
|
3505
|
-
if (watching) setUnseen(
|
|
3506
|
-
}, [watching]);
|
|
3692
|
+
if (watching && shown) setUnseen((u) => u.filter((m) => m.chat !== shown));
|
|
3693
|
+
}, [watching, shown]);
|
|
3507
3694
|
(0, import_react3.useEffect)(() => {
|
|
3508
3695
|
if (!active) return;
|
|
3509
3696
|
let stop = false;
|
|
3510
3697
|
let timer;
|
|
3511
3698
|
let ctrl = null;
|
|
3699
|
+
let refused = false;
|
|
3512
3700
|
const tick = async () => {
|
|
3513
3701
|
const api = client.api();
|
|
3514
3702
|
if (!api) {
|
|
@@ -3516,23 +3704,32 @@ var useChat = (client, o, open, view) => {
|
|
|
3516
3704
|
return;
|
|
3517
3705
|
}
|
|
3518
3706
|
ctrl = new AbortController();
|
|
3519
|
-
let t = null;
|
|
3520
3707
|
try {
|
|
3521
3708
|
await client.ready;
|
|
3522
|
-
|
|
3709
|
+
const t2 = await api.chat(ctrl.signal, place.chatId ?? void 0);
|
|
3523
3710
|
if (stop) return;
|
|
3524
|
-
take(
|
|
3525
|
-
setError("");
|
|
3711
|
+
take(t2);
|
|
3712
|
+
if (readFailed.current) setError("");
|
|
3713
|
+
readFailed.current = false;
|
|
3526
3714
|
} catch (e) {
|
|
3527
3715
|
if (e.name === "AbortError" || stop) return;
|
|
3528
|
-
|
|
3716
|
+
refused = [401, 403].includes(e.status ?? 0);
|
|
3717
|
+
if (e.status === 404 && place.chatId) enter(null);
|
|
3718
|
+
else if (watch.current) {
|
|
3719
|
+
readFailed.current = true;
|
|
3720
|
+
setError(e.message);
|
|
3721
|
+
}
|
|
3529
3722
|
}
|
|
3530
3723
|
setLoaded(true);
|
|
3531
|
-
const
|
|
3532
|
-
const
|
|
3533
|
-
const
|
|
3534
|
-
const ms = watch.current ? POLL_OPEN_MS : waiting && recent ? POLL_WAITING_MS : waiting || open ? POLL_IDLE_MS : 0;
|
|
3724
|
+
const running = list.current.filter((c) => c.status !== "solved");
|
|
3725
|
+
const recent = running.some((c) => c.last && Date.now() - new Date(c.last.at).getTime() < WAITING_FOR_MS);
|
|
3726
|
+
const ms = watch.current ? POLL_OPEN_MS : running.length && recent ? POLL_WAITING_MS : running.length || open ? POLL_IDLE_MS : 0;
|
|
3535
3727
|
if (!stop && ms) timer = setTimeout(tick, ms);
|
|
3728
|
+
else if (!stop) timer = setTimeout(quiet, POLL_QUIET_MS);
|
|
3729
|
+
};
|
|
3730
|
+
const quiet = () => {
|
|
3731
|
+
if (client.getUser() && !refused) void tick();
|
|
3732
|
+
else timer = setTimeout(quiet, POLL_QUIET_MS);
|
|
3536
3733
|
};
|
|
3537
3734
|
void tick();
|
|
3538
3735
|
return () => {
|
|
@@ -3540,8 +3737,22 @@ var useChat = (client, o, open, view) => {
|
|
|
3540
3737
|
ctrl?.abort();
|
|
3541
3738
|
clearTimeout(timer);
|
|
3542
3739
|
};
|
|
3543
|
-
}, [client, take, active, open, watching]);
|
|
3544
|
-
return {
|
|
3740
|
+
}, [client, take, enter, active, open, watching, place]);
|
|
3741
|
+
return {
|
|
3742
|
+
// A new conversation has nothing to show yet; polls keep reading the newest for the list.
|
|
3743
|
+
thread: place.fresh ? null : thread,
|
|
3744
|
+
chats,
|
|
3745
|
+
hasEmail,
|
|
3746
|
+
place,
|
|
3747
|
+
/** Messages shows the list: several conversations and none chosen. */
|
|
3748
|
+
listing: place.chatId === null && !place.fresh && chats.length > 1,
|
|
3749
|
+
enter,
|
|
3750
|
+
loaded,
|
|
3751
|
+
error,
|
|
3752
|
+
setError,
|
|
3753
|
+
unseen,
|
|
3754
|
+
arrived
|
|
3755
|
+
};
|
|
3545
3756
|
};
|
|
3546
3757
|
var useArticles = (client, on, query) => {
|
|
3547
3758
|
const [list, setList] = (0, import_react3.useState)(null);
|
|
@@ -3549,7 +3760,7 @@ var useArticles = (client, on, query) => {
|
|
|
3549
3760
|
(0, import_react3.useEffect)(() => {
|
|
3550
3761
|
if (!on) return;
|
|
3551
3762
|
let stop = false;
|
|
3552
|
-
const
|
|
3763
|
+
const t2 = setTimeout(
|
|
3553
3764
|
() => void client.ready.then(
|
|
3554
3765
|
() => client.api()?.articles(query.trim() || void 0).then((r) => {
|
|
3555
3766
|
if (stop) return;
|
|
@@ -3561,27 +3772,27 @@ var useArticles = (client, on, query) => {
|
|
|
3561
3772
|
);
|
|
3562
3773
|
return () => {
|
|
3563
3774
|
stop = true;
|
|
3564
|
-
clearTimeout(
|
|
3775
|
+
clearTimeout(t2);
|
|
3565
3776
|
};
|
|
3566
3777
|
}, [client, on, query]);
|
|
3567
3778
|
return { list, any };
|
|
3568
3779
|
};
|
|
3569
3780
|
var spansOf = (p, spans, base) => spans.map(
|
|
3570
|
-
(
|
|
3781
|
+
(s2, i) => (0, import_react3.createElement)(
|
|
3571
3782
|
RN3.Text,
|
|
3572
3783
|
{
|
|
3573
3784
|
key: i,
|
|
3574
|
-
onPress:
|
|
3575
|
-
accessibilityRole:
|
|
3785
|
+
onPress: s2.href ? () => void RN3.Linking.openURL(s2.href).catch(() => void 0) : void 0,
|
|
3786
|
+
accessibilityRole: s2.href ? "link" : void 0,
|
|
3576
3787
|
style: {
|
|
3577
3788
|
...base,
|
|
3578
|
-
...
|
|
3579
|
-
...
|
|
3580
|
-
...
|
|
3581
|
-
...
|
|
3789
|
+
...s2.bold ? { fontWeight: "700" } : {},
|
|
3790
|
+
...s2.italic ? { fontStyle: "italic" } : {},
|
|
3791
|
+
...s2.code ? { fontFamily: RN3.Platform.OS === "ios" ? "Menlo" : "monospace", backgroundColor: p.soft } : {},
|
|
3792
|
+
...s2.href ? { color: p.accent, textDecorationLine: "underline" } : {}
|
|
3582
3793
|
}
|
|
3583
3794
|
},
|
|
3584
|
-
|
|
3795
|
+
s2.text
|
|
3585
3796
|
)
|
|
3586
3797
|
);
|
|
3587
3798
|
var ArticleView = ({ client, p, id, onAsk }) => {
|
|
@@ -3595,7 +3806,7 @@ var ArticleView = ({ client, p, id, onAsk }) => {
|
|
|
3595
3806
|
return () => ctrl.abort();
|
|
3596
3807
|
}, [client, id]);
|
|
3597
3808
|
if (error) return k.text(error, { color: p.danger });
|
|
3598
|
-
if (!a) return k.muted("Loading\u2026");
|
|
3809
|
+
if (!a) return k.muted(t("Loading\u2026"));
|
|
3599
3810
|
const base = { color: p.fg, fontSize: 15, lineHeight: 22 };
|
|
3600
3811
|
const blocks = a.blocks.map((b, i) => {
|
|
3601
3812
|
if (b.type === "h2" || b.type === "h3") return (0, import_react3.createElement)(RN3.Text, { key: i, accessibilityRole: "header", style: { ...base, fontSize: b.type === "h2" ? 17 : 16, fontWeight: "700", marginTop: 8 } }, ...spansOf(p, b.spans, {}));
|
|
@@ -3624,11 +3835,11 @@ var ArticleView = ({ client, p, id, onAsk }) => {
|
|
|
3624
3835
|
(0, import_react3.createElement)(
|
|
3625
3836
|
RN3.View,
|
|
3626
3837
|
{ style: { flexDirection: "row", alignItems: "center", gap: 8, padding: 12, borderRadius: 10, backgroundColor: p.soft, marginTop: 8 } },
|
|
3627
|
-
rated ? k.muted("Thanks for letting us know.") : k.muted("Did this answer your question?", { flex: 1 }),
|
|
3628
|
-
rated ? null : k.btn("Yes", "secondary", () => rate(true), false, { height: 34 }),
|
|
3629
|
-
rated ? null : k.btn("No", "secondary", () => rate(false), false, { height: 34 })
|
|
3838
|
+
rated ? k.muted(t("Thanks for letting us know.")) : k.muted(t("Did this answer your question?"), { flex: 1 }),
|
|
3839
|
+
rated ? null : k.btn(t("Yes"), "secondary", () => rate(true), false, { height: 34 }),
|
|
3840
|
+
rated ? null : k.btn(t("No"), "secondary", () => rate(false), false, { height: 34 })
|
|
3630
3841
|
),
|
|
3631
|
-
k.btn("Still need help? Send us a message", "secondary", onAsk)
|
|
3842
|
+
k.btn(t("Still need help? Send us a message"), "secondary", onAsk)
|
|
3632
3843
|
);
|
|
3633
3844
|
};
|
|
3634
3845
|
var MessengerSheet = ({
|
|
@@ -3642,14 +3853,15 @@ var MessengerSheet = ({
|
|
|
3642
3853
|
}) => {
|
|
3643
3854
|
const k = kit(p);
|
|
3644
3855
|
const [body, setBody] = (0, import_react3.useState)("");
|
|
3856
|
+
const [asked, setAsked] = (0, import_react3.useState)("");
|
|
3645
3857
|
const [email, setEmail] = (0, import_react3.useState)("");
|
|
3646
3858
|
const [pending, setPending] = (0, import_react3.useState)(null);
|
|
3647
3859
|
const [slow, setSlow] = (0, import_react3.useState)(false);
|
|
3648
3860
|
(0, import_react3.useEffect)(() => {
|
|
3649
3861
|
setSlow(false);
|
|
3650
3862
|
if (!pending) return;
|
|
3651
|
-
const
|
|
3652
|
-
return () => clearTimeout(
|
|
3863
|
+
const t2 = setTimeout(() => setSlow(true), 1500);
|
|
3864
|
+
return () => clearTimeout(t2);
|
|
3653
3865
|
}, [pending]);
|
|
3654
3866
|
const [photos, setPhotos] = (0, import_react3.useState)([]);
|
|
3655
3867
|
const [query, setQuery] = (0, import_react3.useState)("");
|
|
@@ -3657,24 +3869,25 @@ var MessengerSheet = ({
|
|
|
3657
3869
|
const arts = useArticles(client, helpOn, query);
|
|
3658
3870
|
const scroll = (0, import_react3.useRef)(null);
|
|
3659
3871
|
const close = () => messengerOpen.set(false);
|
|
3660
|
-
const greeting = o.greeting ?? "Hi! How can we help?";
|
|
3661
|
-
const { thread } = chat;
|
|
3662
|
-
const
|
|
3872
|
+
const greeting = o.greeting ?? t("Hi! How can we help?");
|
|
3873
|
+
const { thread, place } = chat;
|
|
3874
|
+
const pendingHere = pending && samePlace(pending.to, place) ? pending : null;
|
|
3875
|
+
const count = (thread?.messages.length ?? 0) + (pendingHere ? 1 : 0);
|
|
3663
3876
|
(0, import_react3.useEffect)(() => {
|
|
3664
3877
|
if (view === "messages") setTimeout(() => scroll.current?.scrollToEnd?.({ animated: true }), 50);
|
|
3665
|
-
}, [count, view]);
|
|
3666
|
-
const needEmail = (o.askEmail ?? true) && !
|
|
3878
|
+
}, [count, view, place]);
|
|
3879
|
+
const needEmail = (o.askEmail ?? true) && !chat.hasEmail && !client.getUser()?.email;
|
|
3667
3880
|
const emailOk = !email.trim() || EMAIL.test(email.trim());
|
|
3668
3881
|
const pickPhotos = async () => {
|
|
3669
3882
|
const api = client.api();
|
|
3670
3883
|
if (!api || !imagePicker?.launchImageLibraryAsync || pending) return;
|
|
3671
3884
|
const room = CHAT_FILES_PER_MESSAGE - photos.length;
|
|
3672
|
-
if (room <= 0) return chat.setError(
|
|
3885
|
+
if (room <= 0) return chat.setError(tf("Up to {n} files per message.", { n: CHAT_FILES_PER_MESSAGE }));
|
|
3673
3886
|
let res;
|
|
3674
3887
|
try {
|
|
3675
3888
|
res = await imagePicker.launchImageLibraryAsync({ allowsMultipleSelection: room > 1, selectionLimit: room, quality: 0.8, exif: false });
|
|
3676
3889
|
} catch (e) {
|
|
3677
|
-
return chat.setError(e.message || "The photos could not be opened.");
|
|
3890
|
+
return chat.setError(e.message || t("The photos could not be opened."));
|
|
3678
3891
|
}
|
|
3679
3892
|
if (res.canceled || res.cancelled || !res.assets?.length) return;
|
|
3680
3893
|
chat.setError("");
|
|
@@ -3683,11 +3896,11 @@ var MessengerSheet = ({
|
|
|
3683
3896
|
const name = a.fileName || a.uri.split("/").pop() || "photo.jpg";
|
|
3684
3897
|
const type = a.mimeType && Object.values(PHOTO_TYPES).includes(a.mimeType) ? a.mimeType : PHOTO_TYPES[name.split(".").pop()?.toLowerCase() ?? ""];
|
|
3685
3898
|
if (!type) {
|
|
3686
|
-
chat.setError("Only pictures (PNG, JPEG, WebP, GIF) can be sent.");
|
|
3899
|
+
chat.setError(t("Only pictures (PNG, JPEG, WebP, GIF) can be sent."));
|
|
3687
3900
|
continue;
|
|
3688
3901
|
}
|
|
3689
3902
|
if (a.fileSize && a.fileSize > CHAT_FILE_MAX_BYTES) {
|
|
3690
|
-
chat.setError(
|
|
3903
|
+
chat.setError(tf("{name} is larger than 10 MB.", { name }));
|
|
3691
3904
|
continue;
|
|
3692
3905
|
}
|
|
3693
3906
|
const ph = { key: `local-${Date.now().toString(36)}${Math.random().toString(36).slice(2, 7)}`, uri: a.uri, name, type, size: a.fileSize ?? 0, up: Promise.resolve() };
|
|
@@ -3706,29 +3919,29 @@ var MessengerSheet = ({
|
|
|
3706
3919
|
setPhotos((list) => [...list, ...added]);
|
|
3707
3920
|
};
|
|
3708
3921
|
const canSend = (!!body.trim() || photos.length > 0) && !pending && emailOk;
|
|
3709
|
-
const
|
|
3710
|
-
const text2 = body.trim();
|
|
3922
|
+
const deliver = async (text2, files, to) => {
|
|
3711
3923
|
const api = client.api();
|
|
3712
|
-
|
|
3713
|
-
if (!text2 && !files.length || pending || !emailOk || !api) return;
|
|
3924
|
+
if (!api) return;
|
|
3714
3925
|
setPending({
|
|
3715
3926
|
id: "pending",
|
|
3716
3927
|
from: "visitor",
|
|
3717
3928
|
body: text2,
|
|
3718
3929
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3930
|
+
to,
|
|
3719
3931
|
...files.length ? { attachments: files.map((f) => ({ id: f.key, name: f.name, type: f.type, size: f.size })) } : {}
|
|
3720
3932
|
});
|
|
3721
|
-
setBody("");
|
|
3722
|
-
setPhotos([]);
|
|
3723
3933
|
chat.setError("");
|
|
3724
3934
|
try {
|
|
3725
3935
|
await Promise.all(files.map((f) => f.up));
|
|
3726
3936
|
const ids = files.filter((f) => f.id).map((f) => f.id);
|
|
3727
|
-
if (!text2 && !ids.length) throw new Error("The photo could not be sent.");
|
|
3937
|
+
if (!text2 && !ids.length) throw new Error(t("The photo could not be sent."));
|
|
3728
3938
|
const win = RN3.Dimensions.get("screen");
|
|
3729
|
-
|
|
3939
|
+
const conversation = to.fresh ? "new" : to.chatId ?? thread?.conversation?.id;
|
|
3940
|
+
chat.arrived(
|
|
3941
|
+
to,
|
|
3730
3942
|
await api.sendChat({
|
|
3731
3943
|
body: text2,
|
|
3944
|
+
...conversation ? { conversation } : {},
|
|
3732
3945
|
email: needEmail && email.trim() ? email.trim() : void 0,
|
|
3733
3946
|
...ids.length ? { attachments: ids } : {},
|
|
3734
3947
|
// A little context for a bug report: the system and the screen size.
|
|
@@ -3736,13 +3949,44 @@ var MessengerSheet = ({
|
|
|
3736
3949
|
})
|
|
3737
3950
|
);
|
|
3738
3951
|
} catch (e) {
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3952
|
+
const gone = e.status === 404;
|
|
3953
|
+
chat.enter(gone ? null : to.chatId, gone || to.fresh);
|
|
3954
|
+
setBody((typed) => [text2, typed.trim()].filter(Boolean).join("\n"));
|
|
3955
|
+
setPhotos((list) => [...files.filter((f) => f.id), ...list]);
|
|
3956
|
+
chat.setError(gone ? t("This conversation is no longer available. Your message is still here to send as a new one.") : e.message);
|
|
3742
3957
|
} finally {
|
|
3743
3958
|
setPending(null);
|
|
3744
3959
|
}
|
|
3745
3960
|
};
|
|
3961
|
+
const send = () => {
|
|
3962
|
+
const text2 = body.trim();
|
|
3963
|
+
const files = photos;
|
|
3964
|
+
if (!text2 && !files.length || pending || !emailOk || !client.api()) return;
|
|
3965
|
+
setBody("");
|
|
3966
|
+
setPhotos([]);
|
|
3967
|
+
void deliver(text2, files, place);
|
|
3968
|
+
};
|
|
3969
|
+
const openMessages = () => {
|
|
3970
|
+
chat.enter(null);
|
|
3971
|
+
setView("messages");
|
|
3972
|
+
};
|
|
3973
|
+
const openChat = (id) => {
|
|
3974
|
+
chat.enter(id);
|
|
3975
|
+
setView("messages");
|
|
3976
|
+
};
|
|
3977
|
+
const startChat = () => {
|
|
3978
|
+
chat.enter(null, true);
|
|
3979
|
+
setView("messages");
|
|
3980
|
+
};
|
|
3981
|
+
const ask = () => {
|
|
3982
|
+
const text2 = asked.trim();
|
|
3983
|
+
if (!text2) return openMessages();
|
|
3984
|
+
if (pending || !client.api()) return;
|
|
3985
|
+
setAsked("");
|
|
3986
|
+
startChat();
|
|
3987
|
+
if (emailOk) void deliver(text2, [], { chatId: null, fresh: true });
|
|
3988
|
+
else setBody((typed) => [text2, typed.trim()].filter(Boolean).join("\n"));
|
|
3989
|
+
};
|
|
3746
3990
|
const localUris = (0, import_react3.useRef)(/* @__PURE__ */ new Map());
|
|
3747
3991
|
for (const f of photos) localUris.current.set(f.key, f.uri);
|
|
3748
3992
|
const fileUrl = (id) => localUris.current.get(id) ?? client.api()?.attachmentUrl(id) ?? "";
|
|
@@ -3797,7 +4041,7 @@ var MessengerSheet = ({
|
|
|
3797
4041
|
k.text(`\u{1F4C4} ${a.title}`, { fontSize: 13 })
|
|
3798
4042
|
)
|
|
3799
4043
|
),
|
|
3800
|
-
m.at ? k.muted(`${mine ? "" : `${who(m.from, o.agentName)} \xB7 `}${isPending && slow ? "Sending\u2026" : timeAgo(m.at)}`, { fontSize: 11, textAlign: mine ? "right" : "left", paddingHorizontal: 6 }) : null
|
|
4044
|
+
m.at ? k.muted(`${mine ? "" : `${who(m.from, o.agentName)} \xB7 `}${isPending && slow ? t("Sending\u2026") : timeAgo(m.at)}`, { fontSize: 11, textAlign: mine ? "right" : "left", paddingHorizontal: 6 }) : null
|
|
3801
4045
|
);
|
|
3802
4046
|
};
|
|
3803
4047
|
const row = (a) => (0, import_react3.createElement)(
|
|
@@ -3810,55 +4054,76 @@ var MessengerSheet = ({
|
|
|
3810
4054
|
const searchRow = (onPress) => (0, import_react3.createElement)(
|
|
3811
4055
|
RN3.Pressable,
|
|
3812
4056
|
{ key: "s", onPress, accessibilityRole: "search", style: { flexDirection: "row", alignItems: "center", height: 44, borderRadius: r.field, paddingHorizontal: 14, backgroundColor: hu.def } },
|
|
3813
|
-
k.muted("Search for help", { flex: 1, fontSize: 15 }),
|
|
4057
|
+
k.muted(t("Search for help"), { flex: 1, fontSize: 15 }),
|
|
3814
4058
|
glyph("search", p.accent, 16)
|
|
3815
4059
|
);
|
|
3816
|
-
const search = k.field({ value: query, onChange: setQuery, placeholder: "Search for help", max: 200, style: { borderWidth: 0, borderRadius: r.field, backgroundColor: raised, ...surf } });
|
|
4060
|
+
const search = k.field({ value: query, onChange: setQuery, placeholder: t("Search for help"), max: 200, style: { borderWidth: 0, borderRadius: r.field, backgroundColor: raised, ...surf } });
|
|
3817
4061
|
const first = client.getUser()?.name?.trim().split(/\s+/)[0];
|
|
3818
|
-
const rest = greeting.replace(/^(hi|hello|hey|hallo)( there)?\s*[
|
|
4062
|
+
const rest = (o.greeting !== void 0 ? greeting.replace(/^(hi|hello|hey|hallo|moin|servus|bonjour|salut|hola|ciao|salve|hoi|ol[áa]|cze[śs][ćc]|hej|こんにちは)( there)?\s*[!,.!、。]*\s*/i, "").trim() : t("How can we help?")) || t("How can we help you today?");
|
|
3819
4063
|
const hero = (fg, sub) => (0, import_react3.createElement)(
|
|
3820
4064
|
RN3.Text,
|
|
3821
4065
|
{ key: "hero", accessibilityRole: "header", style: { color: fg, fontSize: 25, fontWeight: "600", letterSpacing: -0.6, lineHeight: 30 } },
|
|
3822
|
-
first ?
|
|
3823
|
-
|
|
4066
|
+
`${first ? tf("Hi, {name}", { name: first }) : t("Hi there")} \u{1F44B}
|
|
4067
|
+
`,
|
|
3824
4068
|
(0, import_react3.createElement)(RN3.Text, { style: { color: sub, fontWeight: "500" } }, rest)
|
|
3825
4069
|
);
|
|
3826
|
-
const
|
|
4070
|
+
const chatRow = (c) => {
|
|
4071
|
+
const m = c.last;
|
|
4072
|
+
const n = chat.unseen.filter((u) => u.chat === c.id).length;
|
|
4073
|
+
const closed = c.status === "solved" ? ` \xB7 ${t("Closed")}` : "";
|
|
4074
|
+
const subject = c.subject || (m ? lineOf(m) : "");
|
|
4075
|
+
const line = m ? lineOf(m) : "";
|
|
4076
|
+
return (0, import_react3.createElement)(
|
|
4077
|
+
RN3.Pressable,
|
|
4078
|
+
{ key: c.id, onPress: () => openChat(c.id), accessibilityRole: "button", accessibilityLabel: `${tf("Conversation: {subject}", { subject })}${closed}${n ? `, ${tf("{n} new", { n })}` : ""}` },
|
|
4079
|
+
card(
|
|
4080
|
+
[
|
|
4081
|
+
(0, import_react3.createElement)(
|
|
4082
|
+
RN3.View,
|
|
4083
|
+
{ key: "r", style: { flexDirection: "row", alignItems: "center", gap: 10 } },
|
|
4084
|
+
title ? teamMark(title, alpha(p.accent, 0.16), p.accent, 26) : null,
|
|
4085
|
+
(0, import_react3.createElement)(
|
|
4086
|
+
RN3.View,
|
|
4087
|
+
{ style: { flex: 1, gap: 1 } },
|
|
4088
|
+
(0, import_react3.createElement)(RN3.Text, { numberOfLines: 1, style: { color: hu.fg, fontSize: 14, fontWeight: "500" } }, subject),
|
|
4089
|
+
m && line !== subject ? (0, import_react3.createElement)(RN3.Text, { numberOfLines: 1, style: { color: hu.muted, fontSize: 13 } }, line) : null,
|
|
4090
|
+
m ? k.muted(`${who(m.from, o.agentName)} \xB7 ${timeAgo(m.at)}${closed}`, { fontSize: 11.5 }) : null
|
|
4091
|
+
),
|
|
4092
|
+
n ? (0, import_react3.createElement)(RN3.View, { style: { minWidth: 20, height: 20, paddingHorizontal: 5, borderRadius: 10, backgroundColor: p.danger, alignItems: "center", justifyContent: "center" } }, k.text(String(Math.min(n, 9)), { color: "#fff", fontSize: 11, fontWeight: "700" })) : glyph("next", p.muted, 14)
|
|
4093
|
+
)
|
|
4094
|
+
],
|
|
4095
|
+
{ paddingVertical: 10, paddingHorizontal: 12 }
|
|
4096
|
+
)
|
|
4097
|
+
);
|
|
4098
|
+
};
|
|
3827
4099
|
const showHelp = helpOn && arts.any !== false;
|
|
3828
4100
|
let content;
|
|
3829
4101
|
if (view === "home") {
|
|
4102
|
+
const running = chat.chats.filter((c) => c.status !== "solved");
|
|
4103
|
+
const rows = (running.length ? running : chat.chats).slice(0, 2);
|
|
3830
4104
|
const cards = [
|
|
3831
|
-
// "Ask a question":
|
|
4105
|
+
// "Ask a question": its own field; the words start a new conversation.
|
|
3832
4106
|
(0, import_react3.createElement)(
|
|
3833
4107
|
RN3.View,
|
|
3834
4108
|
{ key: "ask", style: { flexDirection: "row", alignItems: "center", gap: 8, height: 48, borderRadius: 24, paddingLeft: 18, paddingRight: 6, backgroundColor: hu.surface, ...surf } },
|
|
3835
|
-
k.field({ value:
|
|
4109
|
+
k.field({ value: asked, onChange: setAsked, placeholder: t("Ask a question"), max: 4e3, onSubmit: ask, style: { flex: 1, borderWidth: 0, paddingHorizontal: 0, backgroundColor: "transparent" } }),
|
|
3836
4110
|
(0, import_react3.createElement)(
|
|
3837
4111
|
RN3.Pressable,
|
|
3838
4112
|
{
|
|
3839
|
-
onPress:
|
|
3840
|
-
setView("messages");
|
|
3841
|
-
if (body.trim()) void send();
|
|
3842
|
-
},
|
|
4113
|
+
onPress: ask,
|
|
3843
4114
|
accessibilityRole: "button",
|
|
3844
|
-
accessibilityLabel: "Send",
|
|
3845
|
-
style: { width: 36, height: 36, borderRadius: 18, alignItems: "center", justifyContent: "center", backgroundColor:
|
|
4115
|
+
accessibilityLabel: t("Send"),
|
|
4116
|
+
style: { width: 36, height: 36, borderRadius: 18, alignItems: "center", justifyContent: "center", backgroundColor: asked.trim() ? p.accent : hu.def }
|
|
3846
4117
|
},
|
|
3847
|
-
glyph("up",
|
|
4118
|
+
glyph("up", asked.trim() ? ink : hu.muted, 18)
|
|
3848
4119
|
)
|
|
3849
4120
|
),
|
|
3850
|
-
|
|
4121
|
+
...rows.map(chatRow),
|
|
4122
|
+
chat.chats.length > rows.length ? (0, import_react3.createElement)(
|
|
3851
4123
|
RN3.Pressable,
|
|
3852
|
-
{ key: "
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
RN3.View,
|
|
3856
|
-
{ key: "r", style: { flexDirection: "row", alignItems: "center", gap: 10 } },
|
|
3857
|
-
title ? teamMark(title, alpha(p.accent, 0.16), p.accent, 26) : null,
|
|
3858
|
-
(0, import_react3.createElement)(RN3.View, { style: { flex: 1, gap: 1 } }, (0, import_react3.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 })),
|
|
3859
|
-
chat.unseen.length ? (0, import_react3.createElement)(RN3.View, { style: { width: 8, height: 8, borderRadius: 4, backgroundColor: p.danger } }) : glyph("next", p.muted, 14)
|
|
3860
|
-
)
|
|
3861
|
-
], { paddingVertical: 10, paddingHorizontal: 12 })
|
|
4124
|
+
{ key: "all", onPress: openMessages, accessibilityRole: "button", accessibilityLabel: tf("All conversations ({n})", { n: chat.chats.length }), style: { flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 4, paddingVertical: 6 } },
|
|
4125
|
+
k.muted(tf("All conversations ({n})", { n: chat.chats.length }), { fontSize: 13, fontWeight: "500" }),
|
|
4126
|
+
glyph("next", p.muted, 12)
|
|
3862
4127
|
) : null,
|
|
3863
4128
|
showHelp ? card([searchRow(() => setView("help")), ...(arts.list ?? []).slice(0, 4).map(row)], { padding: 8, gap: 2 }) : null
|
|
3864
4129
|
];
|
|
@@ -3873,7 +4138,14 @@ var MessengerSheet = ({
|
|
|
3873
4138
|
(0, import_react3.createElement)(RN3.View, { style: { backgroundColor: p.accent, paddingHorizontal: 22, paddingTop: 4, paddingBottom: 64 } }, hero(ink, alpha(ink, 0.8))),
|
|
3874
4139
|
(0, import_react3.createElement)(RN3.View, { style: { marginTop: -48, paddingHorizontal: 16, gap: 12 } }, ...cards)
|
|
3875
4140
|
);
|
|
3876
|
-
} else if (view === "messages")
|
|
4141
|
+
} else if (view === "messages" && chat.listing)
|
|
4142
|
+
content = (0, import_react3.createElement)(
|
|
4143
|
+
RN3.ScrollView,
|
|
4144
|
+
{ key: "chats", style: { flex: 1, backgroundColor: canvas }, contentContainerStyle: { paddingHorizontal: 16, paddingTop: 4, paddingBottom: 16, gap: 12 } },
|
|
4145
|
+
...chat.chats.map(chatRow),
|
|
4146
|
+
k.btn(t("New conversation"), "primary", startChat)
|
|
4147
|
+
);
|
|
4148
|
+
else if (view === "messages")
|
|
3877
4149
|
content = (0, import_react3.createElement)(
|
|
3878
4150
|
RN3.View,
|
|
3879
4151
|
{ key: "messages", style: { flex: 1, backgroundColor: hu.bg } },
|
|
@@ -3881,11 +4153,11 @@ var MessengerSheet = ({
|
|
|
3881
4153
|
RN3.ScrollView,
|
|
3882
4154
|
{ ref: scroll, style: { flex: 1 }, contentContainerStyle: { paddingHorizontal: 16, paddingTop: 4, paddingBottom: 16, gap: 12 } },
|
|
3883
4155
|
// The greeting opens an empty chat only, never above the visitor's first words (2026-10-30).
|
|
3884
|
-
!thread?.messages.length && !
|
|
3885
|
-
!chat.loaded ? k.muted("Loading\u2026") : null,
|
|
4156
|
+
!thread?.messages.length && !pendingHere && !place.chatId ? bubble({ id: "greeting", from: "team", body: greeting, at: "" }) : null,
|
|
4157
|
+
!chat.loaded && !place.fresh && !pendingHere || place.chatId && !thread ? k.muted(t("Loading\u2026")) : null,
|
|
3886
4158
|
...(thread?.messages ?? []).map((m) => bubble(m)),
|
|
3887
|
-
|
|
3888
|
-
thread?.conversation?.status === "solved" && !
|
|
4159
|
+
pendingHere ? bubble(pendingHere, true) : null,
|
|
4160
|
+
thread?.conversation?.status === "solved" && !pendingHere ? k.muted(t("This conversation is closed. Write to start a new one."), { textAlign: "center" }) : null
|
|
3889
4161
|
),
|
|
3890
4162
|
(0, import_react3.createElement)(
|
|
3891
4163
|
RN3.View,
|
|
@@ -3899,13 +4171,13 @@ var MessengerSheet = ({
|
|
|
3899
4171
|
(f) => (0, import_react3.createElement)(
|
|
3900
4172
|
RN3.View,
|
|
3901
4173
|
{ key: f.key, style: { width: 56, height: 56 } },
|
|
3902
|
-
(0, import_react3.createElement)(RN3.Image, { source: { uri: f.uri }, resizeMode: "cover", accessibilityLabel: f.id ? f.name :
|
|
4174
|
+
(0, import_react3.createElement)(RN3.Image, { source: { uri: f.uri }, resizeMode: "cover", accessibilityLabel: f.id ? f.name : tf("{name}, uploading", { name: f.name }), style: { width: 56, height: 56, borderRadius: Math.max(4, r.card - 4), backgroundColor: p.soft, opacity: f.id ? 1 : 0.5 } }),
|
|
3903
4175
|
(0, import_react3.createElement)(
|
|
3904
4176
|
RN3.Pressable,
|
|
3905
4177
|
{
|
|
3906
4178
|
onPress: () => setPhotos((list) => list.filter((x) => x.key !== f.key)),
|
|
3907
4179
|
accessibilityRole: "button",
|
|
3908
|
-
accessibilityLabel:
|
|
4180
|
+
accessibilityLabel: tf("Remove {name}", { name: f.name }),
|
|
3909
4181
|
hitSlop: 8,
|
|
3910
4182
|
style: { position: "absolute", top: -7, right: -7, width: 22, height: 22, borderRadius: 11, borderWidth: 2, borderColor: p.bg, backgroundColor: p.fg, alignItems: "center", justifyContent: "center" }
|
|
3911
4183
|
},
|
|
@@ -3922,28 +4194,28 @@ var MessengerSheet = ({
|
|
|
3922
4194
|
needEmail ? k.field({
|
|
3923
4195
|
value: email,
|
|
3924
4196
|
onChange: setEmail,
|
|
3925
|
-
placeholder: "Email for our reply (optional)",
|
|
4197
|
+
placeholder: t("Email for our reply (optional)"),
|
|
3926
4198
|
email: true,
|
|
3927
4199
|
max: 200,
|
|
3928
4200
|
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 }
|
|
3929
4201
|
}) : null,
|
|
3930
|
-
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" } }),
|
|
4202
|
+
k.field({ value: body, onChange: setBody, placeholder: t("Write a message\u2026"), multiline: true, max: 4e3, style: { minHeight: 28, maxHeight: 120, borderWidth: 0, paddingHorizontal: 0, paddingVertical: 4, backgroundColor: "transparent" } }),
|
|
3931
4203
|
(0, import_react3.createElement)(
|
|
3932
4204
|
RN3.View,
|
|
3933
4205
|
{ style: { flexDirection: "row", alignItems: "center", marginLeft: -8 } },
|
|
3934
4206
|
imagePicker?.launchImageLibraryAsync ? (0, import_react3.createElement)(
|
|
3935
4207
|
RN3.Pressable,
|
|
3936
|
-
{ onPress: () => void pickPhotos(), disabled: !!pending, accessibilityRole: "button", accessibilityLabel: "Attach a photo or screenshot", hitSlop: 6, style: { width: 34, height: 34, borderRadius: 17, alignItems: "center", justifyContent: "center" } },
|
|
4208
|
+
{ onPress: () => void pickPhotos(), disabled: !!pending, accessibilityRole: "button", accessibilityLabel: t("Attach a photo or screenshot"), hitSlop: 6, style: { width: 34, height: 34, borderRadius: 17, alignItems: "center", justifyContent: "center" } },
|
|
3937
4209
|
glyph("clip", hu.muted, 19)
|
|
3938
4210
|
) : null,
|
|
3939
4211
|
(0, import_react3.createElement)(RN3.View, { style: { flex: 1 } }),
|
|
3940
4212
|
(0, import_react3.createElement)(
|
|
3941
4213
|
RN3.Pressable,
|
|
3942
4214
|
{
|
|
3943
|
-
onPress:
|
|
4215
|
+
onPress: send,
|
|
3944
4216
|
disabled: !canSend,
|
|
3945
4217
|
accessibilityRole: "button",
|
|
3946
|
-
accessibilityLabel: "Send",
|
|
4218
|
+
accessibilityLabel: t("Send"),
|
|
3947
4219
|
style: { width: 34, height: 34, borderRadius: 17, alignItems: "center", justifyContent: "center", backgroundColor: canSend ? p.accent : hu.def }
|
|
3948
4220
|
},
|
|
3949
4221
|
glyph("up", canSend ? ink : hu.muted, 18)
|
|
@@ -3959,15 +4231,15 @@ var MessengerSheet = ({
|
|
|
3959
4231
|
RN3.ScrollView,
|
|
3960
4232
|
{ key: "help", style: { flex: 1, backgroundColor: canvas }, contentContainerStyle: { paddingHorizontal: 16, paddingTop: 4, paddingBottom: 16, gap: 12 }, keyboardShouldPersistTaps: "handled" },
|
|
3961
4233
|
search,
|
|
3962
|
-
arts.list === null ? k.muted("Loading\u2026") : null,
|
|
3963
|
-
arts.list?.length === 0 ? (0, import_react3.createElement)(RN3.View, { style: { gap: 10, alignItems: "center", paddingVertical: 24 } }, k.muted(query.trim() ?
|
|
4234
|
+
arts.list === null ? k.muted(t("Loading\u2026")) : null,
|
|
4235
|
+
arts.list?.length === 0 ? (0, import_react3.createElement)(RN3.View, { style: { gap: 10, alignItems: "center", paddingVertical: 24 } }, k.muted(query.trim() ? tf("Nothing found for \u201C{q}\u201D.", { q: query.trim() }) : t("No help articles yet.")), k.btn(t("Send us a message"), "primary", startChat)) : null,
|
|
3964
4236
|
...query.trim() ? arts.list?.length ? [card((arts.list ?? []).map(row), { padding: 8 })] : [] : [...groups].map(([name, items]) => (0, import_react3.createElement)(RN3.View, { key: name, style: { gap: 6 } }, k.muted(`${name} \xB7 ${items.length}`, { fontSize: 12, fontWeight: "600", paddingHorizontal: 2 }), card(items.map(row), { padding: 8 })))
|
|
3965
4237
|
);
|
|
3966
4238
|
} else
|
|
3967
4239
|
content = (0, import_react3.createElement)(
|
|
3968
4240
|
RN3.ScrollView,
|
|
3969
4241
|
{ key: `a-${view.article}`, style: { flex: 1 }, contentContainerStyle: { paddingHorizontal: 16, paddingTop: 4, paddingBottom: 32 } },
|
|
3970
|
-
(0, import_react3.createElement)(ArticleView, { client, p, id: view.article, onAsk:
|
|
4242
|
+
(0, import_react3.createElement)(ArticleView, { client, p, id: view.article, onAsk: startChat })
|
|
3971
4243
|
);
|
|
3972
4244
|
const current = typeof view === "string" ? view : "help";
|
|
3973
4245
|
const tab = (v, name, icon, badge = 0) => {
|
|
@@ -3976,7 +4248,7 @@ var MessengerSheet = ({
|
|
|
3976
4248
|
RN3.Pressable,
|
|
3977
4249
|
{
|
|
3978
4250
|
key: v,
|
|
3979
|
-
onPress: () => setView(v),
|
|
4251
|
+
onPress: () => v === "messages" ? openMessages() : setView(v),
|
|
3980
4252
|
accessibilityRole: "tab",
|
|
3981
4253
|
accessibilityLabel: name,
|
|
3982
4254
|
accessibilityState: { selected: on },
|
|
@@ -3992,21 +4264,21 @@ var MessengerSheet = ({
|
|
|
3992
4264
|
const ground = onAccent ? p.accent : hu.bg;
|
|
3993
4265
|
const back = (to, label) => (0, import_react3.createElement)(
|
|
3994
4266
|
RN3.Pressable,
|
|
3995
|
-
{ key: "back", onPress:
|
|
4267
|
+
{ key: "back", onPress: to, accessibilityRole: "button", accessibilityLabel: label, hitSlop: 10, style: { width: 32, height: 32, alignItems: "center", justifyContent: "center", transform: [{ rotate: "180deg" }] } },
|
|
3996
4268
|
glyph("next", hu.fg, 18)
|
|
3997
4269
|
);
|
|
3998
4270
|
const head = (0, import_react3.createElement)(
|
|
3999
4271
|
RN3.View,
|
|
4000
4272
|
{ key: "head", style: { flexDirection: "row", alignItems: "center", gap: 8, paddingLeft: 12, paddingRight: 12, paddingTop: 12, paddingBottom: 6, backgroundColor: ground } },
|
|
4001
|
-
typeof view !== "string" ? back("help", "All articles") : view === "messages" ? back("home", "Back to home") : view === "home" && (o.logo || title) ? o.logo ? (0, import_react3.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,
|
|
4002
|
-
// In the chat: who answers, in the middle.
|
|
4003
|
-
view === "messages" && title ? (0, import_react3.createElement)(
|
|
4273
|
+
typeof view !== "string" ? back(() => setView("help"), t("All articles")) : view === "messages" && !chat.listing && chat.chats.length > 1 ? back(openMessages, t("All conversations")) : view === "messages" ? back(() => setView("home"), t("Back to home")) : view === "home" && (o.logo || title) ? o.logo ? (0, import_react3.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,
|
|
4274
|
+
// In the chat: who answers, in the middle; over the list its name.
|
|
4275
|
+
view === "messages" && chat.listing ? (0, import_react3.createElement)(RN3.View, { style: { flex: 1, alignItems: "center" } }, k.text(t("Messages"), { color: hu.fg, fontSize: 15, fontWeight: "600" })) : view === "messages" && title ? (0, import_react3.createElement)(
|
|
4004
4276
|
RN3.View,
|
|
4005
4277
|
{ style: { flex: 1, flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 8 } },
|
|
4006
4278
|
teamMark(title, alpha(p.accent, 0.16), p.accent, 28),
|
|
4007
4279
|
(0, import_react3.createElement)(RN3.Text, { numberOfLines: 1, style: { flexShrink: 1, color: hu.fg, fontSize: 15, fontWeight: "600" } }, title)
|
|
4008
4280
|
) : (0, import_react3.createElement)(RN3.View, { style: { flex: 1 } }),
|
|
4009
|
-
(0, import_react3.createElement)(RN3.Pressable, { onPress: close, accessibilityRole: "button", accessibilityLabel: "Close", hitSlop: 12, style: { width: 32, height: 32, borderRadius: 10, alignItems: "center", justifyContent: "center" } }, glyph("cross", onAccent ? ink : p.muted, 18))
|
|
4281
|
+
(0, import_react3.createElement)(RN3.Pressable, { onPress: close, accessibilityRole: "button", accessibilityLabel: t("Close"), hitSlop: 12, style: { width: 32, height: 32, borderRadius: 10, alignItems: "center", justifyContent: "center" } }, glyph("cross", onAccent ? ink : p.muted, 18))
|
|
4010
4282
|
);
|
|
4011
4283
|
const powered = brand ? (0, import_react3.createElement)(
|
|
4012
4284
|
RN3.View,
|
|
@@ -4014,8 +4286,9 @@ var MessengerSheet = ({
|
|
|
4014
4286
|
(0, import_react3.createElement)(
|
|
4015
4287
|
RN3.Text,
|
|
4016
4288
|
{ onPress: () => void RN3.Linking.openURL(BRAND_URL).catch(() => void 0), accessibilityRole: "link", hitSlop: 8, style: { color: p.muted, fontSize: 11.5 } },
|
|
4017
|
-
"Powered by ",
|
|
4018
|
-
(0, import_react3.createElement)(RN3.Text, { style: { fontWeight: "600" } }, "MRRocket")
|
|
4289
|
+
t("Powered by {brand}").split("{brand}")[0],
|
|
4290
|
+
(0, import_react3.createElement)(RN3.Text, { style: { fontWeight: "600" } }, "MRRocket"),
|
|
4291
|
+
t("Powered by {brand}").split("{brand}")[1]
|
|
4019
4292
|
)
|
|
4020
4293
|
) : null;
|
|
4021
4294
|
return (0, import_react3.createElement)(
|
|
@@ -4026,12 +4299,12 @@ var MessengerSheet = ({
|
|
|
4026
4299
|
{ behavior: RN3.Platform.OS === "ios" ? "padding" : void 0, style: { flex: 1, backgroundColor: hu.bg } },
|
|
4027
4300
|
head,
|
|
4028
4301
|
content,
|
|
4029
|
-
view === "messages" ? null : (0, import_react3.createElement)(
|
|
4302
|
+
view === "messages" && !chat.listing ? null : (0, import_react3.createElement)(
|
|
4030
4303
|
RN3.View,
|
|
4031
4304
|
{ key: "tabs", accessibilityRole: "tablist", style: { flexDirection: "row", marginHorizontal: 12, marginTop: 6, marginBottom: 6, padding: 4, borderRadius: 22, backgroundColor: hu.surface } },
|
|
4032
|
-
tab("home", "Home", "home"),
|
|
4033
|
-
tab("messages", "Messages", "chat", chat.unseen.length),
|
|
4034
|
-
showHelp ? tab("help", "Help", "help") : null
|
|
4305
|
+
tab("home", t("Home"), "home"),
|
|
4306
|
+
tab("messages", t("Messages"), "chat", chat.unseen.length),
|
|
4307
|
+
showHelp ? tab("help", t("Help"), "help") : null
|
|
4035
4308
|
),
|
|
4036
4309
|
powered,
|
|
4037
4310
|
(0, import_react3.createElement)(RN3.View, { style: { height: 16 } })
|
|
@@ -4046,7 +4319,7 @@ var MRRocketMessenger = ({ client, appearance, options: given = {} }) => {
|
|
|
4046
4319
|
() => client.api()?.messengerLook(ctrl.signal).then(setLook).catch(() => void 0)
|
|
4047
4320
|
);
|
|
4048
4321
|
return () => ctrl.abort();
|
|
4049
|
-
}, [client]);
|
|
4322
|
+
}, [client, wordsVersion()]);
|
|
4050
4323
|
const defined = Object.fromEntries(Object.entries(given).filter(([, v]) => v !== void 0));
|
|
4051
4324
|
const options = look ? {
|
|
4052
4325
|
title: look.title,
|
|
@@ -4070,25 +4343,29 @@ var MRRocketMessenger = ({ client, appearance, options: given = {} }) => {
|
|
|
4070
4343
|
const open = messengerOpen.useOpen();
|
|
4071
4344
|
const [view, setViewState] = (0, import_react3.useState)(msgView);
|
|
4072
4345
|
const [banner, setBanner] = (0, import_react3.useState)(null);
|
|
4073
|
-
(0, import_react3.useEffect)(() => {
|
|
4074
|
-
if (open) setViewState(msgView);
|
|
4075
|
-
}, [open]);
|
|
4076
4346
|
const setView = (v) => {
|
|
4077
4347
|
msgView = v;
|
|
4078
4348
|
setViewState(v);
|
|
4079
4349
|
};
|
|
4080
4350
|
const chat = useChat(client, options, open, view);
|
|
4351
|
+
const { enter } = chat;
|
|
4352
|
+
(0, import_react3.useEffect)(() => {
|
|
4353
|
+
if (!open) return;
|
|
4354
|
+
setViewState(msgView);
|
|
4355
|
+
if (msgView === "messages") enter(msgChat);
|
|
4356
|
+
}, [open, enter]);
|
|
4081
4357
|
const latest = chat.unseen.at(-1);
|
|
4082
4358
|
(0, import_react3.useEffect)(() => {
|
|
4083
4359
|
if (!latest || open || options.preview === false) return;
|
|
4084
4360
|
setBanner(latest);
|
|
4085
|
-
const
|
|
4086
|
-
return () => clearTimeout(
|
|
4361
|
+
const t2 = setTimeout(() => setBanner(null), 6e3);
|
|
4362
|
+
return () => clearTimeout(t2);
|
|
4087
4363
|
}, [latest, open, options.preview]);
|
|
4088
4364
|
(0, import_react3.useEffect)(() => {
|
|
4089
4365
|
if (open) setBanner(null);
|
|
4090
4366
|
}, [open]);
|
|
4091
4367
|
const unseen = chat.unseen.length;
|
|
4368
|
+
const news = latest?.chat ?? null;
|
|
4092
4369
|
const ink = inkOn(p.accent);
|
|
4093
4370
|
const label = options.label?.trim() ?? "";
|
|
4094
4371
|
const r = CORNERS[options.corners ?? "round"];
|
|
@@ -4103,9 +4380,9 @@ var MRRocketMessenger = ({ client, appearance, options: given = {} }) => {
|
|
|
4103
4380
|
(0, import_react3.createElement)(
|
|
4104
4381
|
RN3.Pressable,
|
|
4105
4382
|
{
|
|
4106
|
-
onPress: () => openAt(
|
|
4383
|
+
onPress: () => openAt(news ? "messages" : "home", news),
|
|
4107
4384
|
accessibilityRole: "button",
|
|
4108
|
-
accessibilityLabel: unseen ?
|
|
4385
|
+
accessibilityLabel: unseen === 1 ? t("Open chat, 1 new reply") : unseen ? tf("Open chat, {n} new replies", { n: unseen }) : t("Open chat"),
|
|
4109
4386
|
style: {
|
|
4110
4387
|
height: label ? 50 : 56,
|
|
4111
4388
|
minWidth: label ? 0 : 56,
|
|
@@ -4132,7 +4409,7 @@ var MRRocketMessenger = ({ client, appearance, options: given = {} }) => {
|
|
|
4132
4409
|
RN3.Pressable,
|
|
4133
4410
|
{
|
|
4134
4411
|
key: "banner",
|
|
4135
|
-
onPress: () => openAt("messages"),
|
|
4412
|
+
onPress: () => openAt("messages", banner.chat),
|
|
4136
4413
|
accessibilityRole: "button",
|
|
4137
4414
|
accessibilityLiveRegion: "polite",
|
|
4138
4415
|
style: {
|
|
@@ -4152,7 +4429,7 @@ var MRRocketMessenger = ({ client, appearance, options: given = {} }) => {
|
|
|
4152
4429
|
elevation: 6
|
|
4153
4430
|
}
|
|
4154
4431
|
},
|
|
4155
|
-
k.muted(`${who(banner.from, options.agentName)}${unseen > 1 ? ` \xB7 ${
|
|
4432
|
+
k.muted(`${who(banner.from, options.agentName)}${unseen > 1 ? ` \xB7 ${tf("{n} new", { n: unseen })}` : ""}`, { fontWeight: "600", fontSize: 12 }),
|
|
4156
4433
|
(0, import_react3.createElement)(RN3.Text, { numberOfLines: 3, style: { color: p.fg, fontSize: 15, lineHeight: 20 } }, banner.body)
|
|
4157
4434
|
) : null;
|
|
4158
4435
|
if (!launcher && !toast && !open) return null;
|
|
@@ -4195,6 +4472,10 @@ var getMRRocket = () => shared;
|
|
|
4195
4472
|
var Ctx = (0, import_react4.createContext)(null);
|
|
4196
4473
|
var MRRocketProvider = ({ client: given, children, consent, flows = true, appearance, surveys = true, messenger = {}, feedback = {}, heatmaps = true, ...options }) => {
|
|
4197
4474
|
const [client] = (0, import_react4.useState)(() => given ?? createMRRocket({ ...options, consent }));
|
|
4475
|
+
(0, import_react4.useSyncExternalStore)(onWords, wordsVersion, wordsVersion);
|
|
4476
|
+
(0, import_react4.useEffect)(() => {
|
|
4477
|
+
if (options.locale) setLocale(options.locale);
|
|
4478
|
+
}, [options.locale]);
|
|
4198
4479
|
(0, import_react4.useEffect)(() => {
|
|
4199
4480
|
if (consent) client.setConsent(consent);
|
|
4200
4481
|
}, [client, consent]);
|