@mrrocket/sdk 0.14.0 → 0.15.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/README.md +9 -3
- package/dist/api-client.d.ts +62 -1
- package/dist/api-client.js +28 -1
- package/dist/api-client.js.map +1 -1
- package/dist/browser/mrrocket-flows.js +2 -2
- package/dist/browser/mrrocket-messenger.js +202 -46
- package/dist/browser/mrrocket.js +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/flows-ui.js +48 -1
- package/dist/flows-ui.js.map +1 -1
- package/dist/flows.d.ts +51 -1
- package/dist/flows.js +62 -21
- package/dist/flows.js.map +1 -1
- package/dist/messenger-ui.d.ts +19 -6
- package/dist/messenger-ui.js +332 -78
- package/dist/messenger-ui.js.map +1 -1
- package/dist/messenger.d.ts +11 -3
- package/dist/messenger.js +6 -3
- package/dist/messenger.js.map +1 -1
- package/dist/react-native/index.d.cts +102 -6
- package/dist/react-native/index.d.ts +102 -6
- package/dist/react-native/index.js +447 -181
- package/dist/react-native/index.js.map +1 -1
- package/dist/styles-css.d.ts +1 -1
- package/dist/styles-css.js +200 -44
- package/dist/styles-css.js.map +1 -1
- package/package.json +5 -1
|
@@ -50,7 +50,7 @@ module.exports = __toCommonJS(native_exports);
|
|
|
50
50
|
var import_react3 = require("react");
|
|
51
51
|
|
|
52
52
|
// src/core.ts
|
|
53
|
-
var SDK_VERSION = "0.
|
|
53
|
+
var SDK_VERSION = "0.15.0";
|
|
54
54
|
var propOf = (person, name) => {
|
|
55
55
|
const want = name.trim().toLowerCase();
|
|
56
56
|
for (const [k, v] of Object.entries(person)) if (k.toLowerCase() === want) return v;
|
|
@@ -173,7 +173,14 @@ var textChanges = (config, distinctId, preview) => {
|
|
|
173
173
|
var newDistinctId = () => typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `ck_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;
|
|
174
174
|
|
|
175
175
|
// src/api-client.ts
|
|
176
|
-
var
|
|
176
|
+
var CHAT_FILE_MAX_BYTES = 10 * 1024 * 1024;
|
|
177
|
+
var CHAT_FILES_PER_MESSAGE = 4;
|
|
178
|
+
var CORNERS = {
|
|
179
|
+
round: { panel: 22, bubble: 18, tail: 6, card: 16, field: 20 },
|
|
180
|
+
soft: { panel: 14, bubble: 12, tail: 4, card: 10, field: 10 },
|
|
181
|
+
square: { panel: 6, bubble: 6, tail: 2, card: 4, field: 4 }
|
|
182
|
+
};
|
|
183
|
+
var BRAND_URL = "https://mrrocket.dev/?utm_source=messenger&utm_medium=powered-by";
|
|
177
184
|
var inkOn = (hex) => {
|
|
178
185
|
const m = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(hex);
|
|
179
186
|
if (!m) return "#ffffff";
|
|
@@ -197,7 +204,8 @@ var call = async (url, init, signal) => {
|
|
|
197
204
|
res = await fetch(url, {
|
|
198
205
|
...init,
|
|
199
206
|
signal,
|
|
200
|
-
|
|
207
|
+
// A form (a file upload) sets its own multipart type with the boundary.
|
|
208
|
+
headers: typeof init?.body === "string" ? { "content-type": "application/json" } : void 0
|
|
201
209
|
});
|
|
202
210
|
} catch (e) {
|
|
203
211
|
if (e.name === "AbortError") throw e;
|
|
@@ -230,7 +238,19 @@ var createApi = (c) => {
|
|
|
230
238
|
seen: (id) => post(`/surveys/${encodeURIComponent(id)}/seen`, { user: void 0 }),
|
|
231
239
|
respond: (id, r) => post(`/surveys/${encodeURIComponent(id)}/respond`, r),
|
|
232
240
|
chat: (signal) => call(`${base}/messages?${qs({ key: c.apiKey, distinctId: c.distinctId })}`, void 0, signal),
|
|
241
|
+
/** A message; `attachments` are upload ids from `uploadChatFile` (then the body may be empty). */
|
|
233
242
|
sendChat: (m) => post("/messages", m),
|
|
243
|
+
/** Uploads one picked file for the next message (pictures and PDFs, at most 10 MB); answers its upload id. */
|
|
244
|
+
uploadChatFile: (file, signal) => {
|
|
245
|
+
const form = new FormData();
|
|
246
|
+
form.append("key", c.apiKey);
|
|
247
|
+
form.append("distinctId", c.distinctId);
|
|
248
|
+
if ("uri" in file) form.append("file", file);
|
|
249
|
+
else form.append("file", file, file.name || "file");
|
|
250
|
+
return call(`${base}/messages/attachments`, { method: "POST", body: form }, signal);
|
|
251
|
+
},
|
|
252
|
+
/** Where a sent file of this visitor's chat is read (an image's src, a PDF's link). */
|
|
253
|
+
attachmentUrl: (id) => `${base}/messages/attachments/${encodeURIComponent(id)}?${qs({ key: c.apiKey, distinctId: c.distinctId })}`,
|
|
234
254
|
articles: (q, signal) => call(`${base}/articles?${qs({ key: c.apiKey, q })}`, void 0, signal),
|
|
235
255
|
article: (id, signal) => call(`${base}/articles/${encodeURIComponent(id)}?${qs({ key: c.apiKey, distinctId: c.distinctId })}`, void 0, signal),
|
|
236
256
|
messengerLook: (signal) => call(`${base}/messenger?${qs({ key: c.apiKey })}`, void 0, signal),
|
|
@@ -343,6 +363,9 @@ var pageMatches = (pattern, path) => {
|
|
|
343
363
|
return at === (p.replace(/\/+$/, "") || "/");
|
|
344
364
|
};
|
|
345
365
|
var stepsIn = (nodes) => nodes.flatMap((n) => n.kind === "condition" ? [...stepsIn(n.yes), ...stepsIn(n.no)] : n.kind === "wait" ? [] : [n]);
|
|
366
|
+
var monthsSince = (since, now = Date.now()) => since == null || since === "" ? NaN : Math.floor((now - new Date(since).getTime()) / 2630016e3);
|
|
367
|
+
var low = (x) => x?.trim().toLowerCase();
|
|
368
|
+
var ruleMatches = ({ when: w = {} }, c) => (w.answer == null || low(w.answer) === low(c.answer)) && (w.plan == null || [w.plan].flat().some((x) => low(x) === low(c.plan))) && !(c.months >= w.tenureUnder) && !(c.months < w.tenureOver) && (w.tenureUnder == null || c.months >= 0) && (w.tenureOver == null || c.months >= 0);
|
|
346
369
|
var createFlows = (env) => {
|
|
347
370
|
let config = null;
|
|
348
371
|
let cancel = null;
|
|
@@ -510,18 +533,23 @@ var createFlows = (env) => {
|
|
|
510
533
|
};
|
|
511
534
|
const view = () => {
|
|
512
535
|
if (cancel) {
|
|
513
|
-
const
|
|
536
|
+
const c = cancel;
|
|
537
|
+
const followUp = c.flow.followUps?.[c.reason];
|
|
538
|
+
const f = followUp ? 1 : 0;
|
|
539
|
+
const steps = c.plain ? 1 : 3 + f + (c.flow.second?.[c.reason] ? 1 : 0);
|
|
514
540
|
return {
|
|
515
541
|
type: "cancel",
|
|
516
|
-
screen:
|
|
517
|
-
flow:
|
|
518
|
-
reason:
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
542
|
+
screen: c.screen,
|
|
543
|
+
flow: c.flow,
|
|
544
|
+
reason: c.reason,
|
|
545
|
+
...c.screen === "followup" && followUp ? { followUp, answer: c.answer } : {},
|
|
546
|
+
offer: c.screen === "offer" ? c.offer : void 0,
|
|
547
|
+
...c.screen === "offer" ? { round: c.round } : {},
|
|
548
|
+
price: c.options.price,
|
|
549
|
+
busy: c.busy,
|
|
550
|
+
error: c.error,
|
|
551
|
+
step: c.screen === "survey" ? 1 : c.screen === "followup" ? 2 : c.screen === "offer" ? 1 + f + c.round : steps,
|
|
552
|
+
steps
|
|
525
553
|
};
|
|
526
554
|
}
|
|
527
555
|
const ob = onboardingView();
|
|
@@ -600,12 +628,22 @@ var createFlows = (env) => {
|
|
|
600
628
|
});
|
|
601
629
|
return loading;
|
|
602
630
|
};
|
|
631
|
+
const offerAt = (c) => c.round > 1 ? { round: 2 } : c.rule >= 0 ? { rule: c.rule } : {};
|
|
632
|
+
const toOffer = (c) => {
|
|
633
|
+
const who = { answer: c.answer, plan: c.options.plan, months: monthsSince(c.options.customerSince, env.now?.()) };
|
|
634
|
+
const rules = c.flow.rules?.[c.reason] ?? [];
|
|
635
|
+
c.rule = rules.findIndex((r) => ruleMatches(r, who));
|
|
636
|
+
c.offer = c.rule < 0 ? c.flow.offers[c.reason] : rules[c.rule].offer ?? void 0;
|
|
637
|
+
c.round = 1;
|
|
638
|
+
c.screen = c.offer ? "offer" : "confirm";
|
|
639
|
+
};
|
|
603
640
|
const result = (c, outcome, comment) => {
|
|
604
|
-
const offer = c.
|
|
641
|
+
const offer = c.screen === "offer" ? c.offer : void 0;
|
|
605
642
|
return {
|
|
606
643
|
outcome,
|
|
607
644
|
run: c.run,
|
|
608
645
|
...c.reason ? { reason: c.reason, reasonLabel: c.flow.reasons.find((r) => r.id === c.reason)?.label } : {},
|
|
646
|
+
...c.answer ? { answer: c.answer } : {},
|
|
609
647
|
...outcome === "saved" && offer ? { offer } : {},
|
|
610
648
|
...comment?.trim() ? { comment: comment.trim().slice(0, 500) } : {}
|
|
611
649
|
};
|
|
@@ -618,7 +656,8 @@ var createFlows = (env) => {
|
|
|
618
656
|
outcome: r.outcome,
|
|
619
657
|
customer: c.options.customer ?? "consumer",
|
|
620
658
|
...r.reason ? { reason: r.reason } : {},
|
|
621
|
-
...r.
|
|
659
|
+
...r.answer ? { answer: r.answer } : {},
|
|
660
|
+
...r.offer ? { offer: r.offer.kind, ...offerAt(c) } : {},
|
|
622
661
|
...r.comment ? { comment: r.comment } : {}
|
|
623
662
|
});
|
|
624
663
|
if (cancel === c) cancel = null;
|
|
@@ -648,7 +687,7 @@ var createFlows = (env) => {
|
|
|
648
687
|
let resolve = () => {
|
|
649
688
|
};
|
|
650
689
|
const promise = new Promise((r) => resolve = r);
|
|
651
|
-
const c = { flow, options, plain, screen: plain ? "confirm" : "survey", run: runId(), busy: false, resolve, promise };
|
|
690
|
+
const c = { flow, options, plain, screen: plain ? "confirm" : "survey", round: 1, run: runId(), busy: false, resolve, promise };
|
|
652
691
|
cancel = c;
|
|
653
692
|
env.track(CANCEL_EVENT, { action: "shown", run: c.run, version: flow.version, customer: options.customer ?? "consumer", plain });
|
|
654
693
|
emit();
|
|
@@ -700,29 +739,43 @@ var createFlows = (env) => {
|
|
|
700
739
|
},
|
|
701
740
|
cancel: {
|
|
702
741
|
pick: (reason) => {
|
|
703
|
-
if (!cancel || cancel.busy || !cancel.flow.reasons.some((r) => r.id === reason)) return;
|
|
742
|
+
if (!cancel || cancel.busy || cancel.screen !== "survey" || !cancel.flow.reasons.some((r) => r.id === reason)) return;
|
|
743
|
+
if (cancel.reason !== reason) cancel.answer = void 0;
|
|
704
744
|
cancel.reason = reason;
|
|
705
745
|
cancel.error = void 0;
|
|
706
746
|
emit();
|
|
707
747
|
},
|
|
748
|
+
answer: (text2) => {
|
|
749
|
+
const c = cancel;
|
|
750
|
+
if (!c || c.busy || c.screen !== "followup") return;
|
|
751
|
+
const t = text2.slice(0, 500);
|
|
752
|
+
c.answer = t.trim() ? t : void 0;
|
|
753
|
+
emit();
|
|
754
|
+
},
|
|
708
755
|
next: () => {
|
|
709
756
|
const c = cancel;
|
|
710
|
-
if (!c ||
|
|
711
|
-
|
|
712
|
-
|
|
757
|
+
if (!c || !c.reason) return;
|
|
758
|
+
if (c.screen === "survey") {
|
|
759
|
+
env.track(CANCEL_EVENT, { action: "answered", run: c.run, version: c.flow.version, reason: c.reason });
|
|
760
|
+
if (c.flow.followUps?.[c.reason]) c.screen = "followup";
|
|
761
|
+
else toOffer(c);
|
|
762
|
+
} else if (c.screen === "followup") {
|
|
763
|
+
c.answer = c.answer?.trim();
|
|
764
|
+
toOffer(c);
|
|
765
|
+
} else return;
|
|
713
766
|
emit();
|
|
714
767
|
},
|
|
715
768
|
accept: (comment) => {
|
|
716
769
|
const c = cancel;
|
|
717
770
|
if (!c || c.screen !== "offer" || !c.reason) return;
|
|
718
|
-
const offer = c.flow.offers[c.reason];
|
|
719
771
|
if (!c.busy)
|
|
720
772
|
env.track(CANCEL_EVENT, {
|
|
721
773
|
action: "offer",
|
|
722
774
|
run: c.run,
|
|
723
775
|
version: c.flow.version,
|
|
724
776
|
reason: c.reason,
|
|
725
|
-
offer: offer?.kind,
|
|
777
|
+
offer: c.offer?.kind,
|
|
778
|
+
...offerAt(c),
|
|
726
779
|
outcome: "accepted"
|
|
727
780
|
});
|
|
728
781
|
settle(c, "saved", comment);
|
|
@@ -735,10 +788,16 @@ var createFlows = (env) => {
|
|
|
735
788
|
run: c.run,
|
|
736
789
|
version: c.flow.version,
|
|
737
790
|
reason: c.reason,
|
|
738
|
-
offer: c.
|
|
791
|
+
offer: c.offer?.kind,
|
|
792
|
+
...offerAt(c),
|
|
739
793
|
outcome: "declined"
|
|
740
794
|
});
|
|
741
|
-
c.
|
|
795
|
+
const second = c.round === 1 && c.reason ? c.flow.second?.[c.reason] : void 0;
|
|
796
|
+
if (second) {
|
|
797
|
+
c.offer = second;
|
|
798
|
+
c.rule = void 0;
|
|
799
|
+
c.round = 2;
|
|
800
|
+
} else c.screen = "confirm";
|
|
742
801
|
c.error = void 0;
|
|
743
802
|
emit();
|
|
744
803
|
},
|
|
@@ -1899,9 +1958,9 @@ var MRRocketFlows = ({ flows, paywall, appearance = {} }) => {
|
|
|
1899
1958
|
card(children, { borderBottomLeftRadius: 0, borderBottomRightRadius: 0, paddingBottom: 34 })
|
|
1900
1959
|
)
|
|
1901
1960
|
);
|
|
1902
|
-
const input = (placeholder) => (0, import_react.createElement)(RN.TextInput, {
|
|
1903
|
-
value
|
|
1904
|
-
onChangeText:
|
|
1961
|
+
const input = (placeholder, value = comment, onChange = setComment) => (0, import_react.createElement)(RN.TextInput, {
|
|
1962
|
+
value,
|
|
1963
|
+
onChangeText: onChange,
|
|
1905
1964
|
placeholder,
|
|
1906
1965
|
placeholderTextColor: p.muted,
|
|
1907
1966
|
multiline: true,
|
|
@@ -1986,38 +2045,49 @@ var MRRocketFlows = ({ flows, paywall, appearance = {} }) => {
|
|
|
1986
2045
|
);
|
|
1987
2046
|
const error = v.error ? text2(v.error, { color: p.danger, fontSize: 13 }) : null;
|
|
1988
2047
|
let body = [];
|
|
2048
|
+
const choices = (items) => (0, import_react.createElement)(
|
|
2049
|
+
RN.ScrollView,
|
|
2050
|
+
{ style: { maxHeight: 320 }, contentContainerStyle: { gap: 6 }, accessibilityRole: "radiogroup" },
|
|
2051
|
+
...items.map(
|
|
2052
|
+
(c) => (0, import_react.createElement)(
|
|
2053
|
+
RN.Pressable,
|
|
2054
|
+
{
|
|
2055
|
+
key: c.key,
|
|
2056
|
+
onPress: c.press,
|
|
2057
|
+
accessibilityRole: "radio",
|
|
2058
|
+
accessibilityState: { checked: c.on },
|
|
2059
|
+
style: {
|
|
2060
|
+
minHeight: 44,
|
|
2061
|
+
paddingHorizontal: 12,
|
|
2062
|
+
borderRadius: 10,
|
|
2063
|
+
borderWidth: 1,
|
|
2064
|
+
borderColor: c.on ? p.accent : p.line,
|
|
2065
|
+
backgroundColor: c.on ? p.soft : p.bg,
|
|
2066
|
+
justifyContent: "center"
|
|
2067
|
+
}
|
|
2068
|
+
},
|
|
2069
|
+
text2(c.label, { fontWeight: "500" })
|
|
2070
|
+
)
|
|
2071
|
+
)
|
|
2072
|
+
);
|
|
1989
2073
|
if (v.screen === "survey")
|
|
1990
2074
|
body = [
|
|
1991
2075
|
title(v.flow.question || "Why are you leaving?"),
|
|
1992
|
-
(
|
|
1993
|
-
RN.ScrollView,
|
|
1994
|
-
{ style: { maxHeight: 320 }, contentContainerStyle: { gap: 6 }, accessibilityRole: "radiogroup" },
|
|
1995
|
-
...v.flow.reasons.map(
|
|
1996
|
-
(r) => (0, import_react.createElement)(
|
|
1997
|
-
RN.Pressable,
|
|
1998
|
-
{
|
|
1999
|
-
key: r.id,
|
|
2000
|
-
onPress: () => flows.cancel.pick(r.id),
|
|
2001
|
-
accessibilityRole: "radio",
|
|
2002
|
-
accessibilityState: { checked: v.reason === r.id },
|
|
2003
|
-
style: {
|
|
2004
|
-
minHeight: 44,
|
|
2005
|
-
paddingHorizontal: 12,
|
|
2006
|
-
borderRadius: 10,
|
|
2007
|
-
borderWidth: 1,
|
|
2008
|
-
borderColor: v.reason === r.id ? p.accent : p.line,
|
|
2009
|
-
backgroundColor: v.reason === r.id ? p.soft : p.bg,
|
|
2010
|
-
justifyContent: "center"
|
|
2011
|
-
}
|
|
2012
|
-
},
|
|
2013
|
-
text2(r.label, { fontWeight: "500" })
|
|
2014
|
-
)
|
|
2015
|
-
)
|
|
2016
|
-
),
|
|
2076
|
+
choices(v.flow.reasons.map((r) => ({ key: r.id, label: r.label, on: v.reason === r.id, press: () => flows.cancel.pick(r.id) }))),
|
|
2017
2077
|
btn("Continue", "primary", flows.cancel.next, !v.reason),
|
|
2018
2078
|
btn("Never mind, keep my plan", "quiet", flows.cancel.close)
|
|
2019
2079
|
];
|
|
2020
|
-
else if (v.screen === "
|
|
2080
|
+
else if (v.screen === "followup" && v.followUp) {
|
|
2081
|
+
const f = v.followUp;
|
|
2082
|
+
const label = v.flow.reasons.find((r) => r.id === v.reason)?.label;
|
|
2083
|
+
body = [
|
|
2084
|
+
label ? muted(`You said: ${label}`) : null,
|
|
2085
|
+
title(f.question),
|
|
2086
|
+
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),
|
|
2087
|
+
btn("Continue", "primary", flows.cancel.next),
|
|
2088
|
+
btn("Never mind, keep my plan", "quiet", flows.cancel.close)
|
|
2089
|
+
];
|
|
2090
|
+
} else if (v.screen === "offer" && v.offer) {
|
|
2021
2091
|
const o = v.offer;
|
|
2022
2092
|
const label = v.flow.reasons.find((r) => r.id === v.reason)?.label;
|
|
2023
2093
|
const detail = o.kind === "discount" && o.percent ? v.price ? `${money(discounted(v.price.amount, o.percent), v.price.currency)} instead of ${money(v.price.amount, v.price.currency)}${o.months ? ` for ${months(o.months)}` : ""}` : `${o.percent}% off${o.months ? ` for ${months(o.months)}` : ""}` : o.kind === "pause" && o.months ? `Pause for up to ${months(o.months)}. Your data stays.` : o.kind === "downgrade" && o.planName ? o.planName : null;
|
|
@@ -2693,6 +2763,13 @@ var buzz = () => {
|
|
|
2693
2763
|
} catch {
|
|
2694
2764
|
}
|
|
2695
2765
|
};
|
|
2766
|
+
var imagePicker = null;
|
|
2767
|
+
try {
|
|
2768
|
+
imagePicker = require("expo-image-picker");
|
|
2769
|
+
} catch {
|
|
2770
|
+
imagePicker = null;
|
|
2771
|
+
}
|
|
2772
|
+
var PHOTO_TYPES = { jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", webp: "image/webp", gif: "image/gif" };
|
|
2696
2773
|
var teamOf = (t) => (t?.messages ?? []).filter((m) => m.from !== "visitor");
|
|
2697
2774
|
var svg = null;
|
|
2698
2775
|
try {
|
|
@@ -2708,9 +2785,10 @@ var GLYPH = {
|
|
|
2708
2785
|
up: "M8 13V3M3.5 7.5 8 3l4.5 4.5",
|
|
2709
2786
|
next: "M6 3.5 10.5 8 6 12.5",
|
|
2710
2787
|
search: "M7 11.5a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9zM10.5 10.5l3 3",
|
|
2711
|
-
cross: "m4 4 8 8M12 4l-8 8"
|
|
2788
|
+
cross: "m4 4 8 8M12 4l-8 8",
|
|
2789
|
+
clip: "m14.3 7.4-6.1 6.1a4 4 0 0 1-5.7-5.7l5.7-5.7a2.7 2.7 0 0 1 3.8 3.8L6.3 11.6a1.3 1.3 0 0 1-1.9-1.9l5.7-5.6"
|
|
2712
2790
|
};
|
|
2713
|
-
var FALLBACK = { chat: "\u{1F4AC}", home: "\u2302", help: "?", send: "\u2192", up: "\u2191", next: "\u203A", search: "\u2315", cross: "\xD7" };
|
|
2791
|
+
var FALLBACK = { chat: "\u{1F4AC}", home: "\u2302", help: "?", send: "\u2192", up: "\u2191", next: "\u203A", search: "\u2315", cross: "\xD7", clip: "\u{1F4CE}" };
|
|
2714
2792
|
var glyph = (name, color, size = 20) => {
|
|
2715
2793
|
const Svg = svg?.Svg ?? svg?.default;
|
|
2716
2794
|
if (!svg || !Svg) return (0, import_react2.createElement)(RN3.Text, { style: { color, fontSize: Math.round(size * 0.85), lineHeight: size + 2, textAlign: "center", minWidth: size } }, FALLBACK[name]);
|
|
@@ -2722,7 +2800,11 @@ var teamMark = (title, bg, fg, size = 36) => (0, import_react2.createElement)(
|
|
|
2722
2800
|
{ style: { width: size, height: size, borderRadius: size / 2, backgroundColor: bg, alignItems: "center", justifyContent: "center" } },
|
|
2723
2801
|
(0, import_react2.createElement)(RN3.Text, { style: { color: fg, fontSize: Math.round(size * 0.42), fontWeight: "700" } }, (title.trim()[0] ?? "?").toUpperCase())
|
|
2724
2802
|
);
|
|
2725
|
-
var
|
|
2803
|
+
var HU = {
|
|
2804
|
+
light: { bg: "#ffffff", surface: "#f4f4f5", def: "#ececee", segment: "#ffffff", fg: "#18181b", muted: "#71717a" },
|
|
2805
|
+
dark: { bg: "#111113", surface: "#1c1c1f", def: "#26262a", segment: "#3a3a40", fg: "#fafafa", muted: "#a1a1aa" }
|
|
2806
|
+
};
|
|
2807
|
+
var hairline = { shadowColor: "#000", shadowOpacity: 0.07, shadowRadius: 3, shadowOffset: { width: 0, height: 1 }, elevation: 1 };
|
|
2726
2808
|
var useChat = (client, o, open, view) => {
|
|
2727
2809
|
const [thread, setThread] = (0, import_react2.useState)(null);
|
|
2728
2810
|
const [loaded, setLoaded] = (0, import_react2.useState)(false);
|
|
@@ -2889,12 +2971,14 @@ var MessengerSheet = ({
|
|
|
2889
2971
|
o,
|
|
2890
2972
|
chat,
|
|
2891
2973
|
view,
|
|
2892
|
-
setView
|
|
2974
|
+
setView,
|
|
2975
|
+
brand
|
|
2893
2976
|
}) => {
|
|
2894
2977
|
const k = kit(p);
|
|
2895
2978
|
const [body, setBody] = (0, import_react2.useState)("");
|
|
2896
2979
|
const [email, setEmail] = (0, import_react2.useState)("");
|
|
2897
2980
|
const [pending, setPending] = (0, import_react2.useState)(null);
|
|
2981
|
+
const [photos, setPhotos] = (0, import_react2.useState)([]);
|
|
2898
2982
|
const [query, setQuery] = (0, import_react2.useState)("");
|
|
2899
2983
|
const helpOn = o.help !== false;
|
|
2900
2984
|
const arts = useArticles(client, helpOn, query);
|
|
@@ -2908,52 +2992,135 @@ var MessengerSheet = ({
|
|
|
2908
2992
|
}, [count, view]);
|
|
2909
2993
|
const needEmail = (o.askEmail ?? true) && !thread?.hasEmail && !client.getUser()?.email;
|
|
2910
2994
|
const emailOk = !email.trim() || EMAIL.test(email.trim());
|
|
2995
|
+
const pickPhotos = async () => {
|
|
2996
|
+
const api = client.api();
|
|
2997
|
+
if (!api || !imagePicker?.launchImageLibraryAsync || pending) return;
|
|
2998
|
+
const room = CHAT_FILES_PER_MESSAGE - photos.length;
|
|
2999
|
+
if (room <= 0) return chat.setError(`Up to ${CHAT_FILES_PER_MESSAGE} files per message.`);
|
|
3000
|
+
let res;
|
|
3001
|
+
try {
|
|
3002
|
+
res = await imagePicker.launchImageLibraryAsync({ allowsMultipleSelection: room > 1, selectionLimit: room, quality: 0.8, exif: false });
|
|
3003
|
+
} catch (e) {
|
|
3004
|
+
return chat.setError(e.message || "The photos could not be opened.");
|
|
3005
|
+
}
|
|
3006
|
+
if (res.canceled || res.cancelled || !res.assets?.length) return;
|
|
3007
|
+
chat.setError("");
|
|
3008
|
+
const added = [];
|
|
3009
|
+
for (const a of res.assets.slice(0, room)) {
|
|
3010
|
+
const name = a.fileName || a.uri.split("/").pop() || "photo.jpg";
|
|
3011
|
+
const type = a.mimeType && Object.values(PHOTO_TYPES).includes(a.mimeType) ? a.mimeType : PHOTO_TYPES[name.split(".").pop()?.toLowerCase() ?? ""];
|
|
3012
|
+
if (!type) {
|
|
3013
|
+
chat.setError("Only pictures (PNG, JPEG, WebP, GIF) can be sent.");
|
|
3014
|
+
continue;
|
|
3015
|
+
}
|
|
3016
|
+
if (a.fileSize && a.fileSize > CHAT_FILE_MAX_BYTES) {
|
|
3017
|
+
chat.setError(`${name} is larger than 10 MB.`);
|
|
3018
|
+
continue;
|
|
3019
|
+
}
|
|
3020
|
+
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() };
|
|
3021
|
+
ph.up = api.uploadChatFile({ uri: a.uri, name, type }).then(
|
|
3022
|
+
(r2) => {
|
|
3023
|
+
ph.id = r2.id;
|
|
3024
|
+
setPhotos((list) => list.map((x) => x.key === ph.key ? { ...ph } : x));
|
|
3025
|
+
},
|
|
3026
|
+
(e) => {
|
|
3027
|
+
setPhotos((list) => list.filter((x) => x.key !== ph.key));
|
|
3028
|
+
chat.setError(`${name}: ${e.message}`);
|
|
3029
|
+
}
|
|
3030
|
+
);
|
|
3031
|
+
added.push(ph);
|
|
3032
|
+
}
|
|
3033
|
+
setPhotos((list) => [...list, ...added]);
|
|
3034
|
+
};
|
|
3035
|
+
const canSend = (!!body.trim() || photos.length > 0) && !pending && emailOk;
|
|
2911
3036
|
const send = async () => {
|
|
2912
3037
|
const text2 = body.trim();
|
|
2913
3038
|
const api = client.api();
|
|
2914
|
-
|
|
2915
|
-
|
|
3039
|
+
const files = photos;
|
|
3040
|
+
if (!text2 && !files.length || pending || !emailOk || !api) return;
|
|
3041
|
+
setPending({
|
|
3042
|
+
id: "pending",
|
|
3043
|
+
from: "visitor",
|
|
3044
|
+
body: text2,
|
|
3045
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3046
|
+
...files.length ? { attachments: files.map((f) => ({ id: f.key, name: f.name, type: f.type, size: f.size })) } : {}
|
|
3047
|
+
});
|
|
2916
3048
|
setBody("");
|
|
3049
|
+
setPhotos([]);
|
|
2917
3050
|
chat.setError("");
|
|
2918
3051
|
try {
|
|
2919
|
-
|
|
3052
|
+
await Promise.all(files.map((f) => f.up));
|
|
3053
|
+
const ids = files.filter((f) => f.id).map((f) => f.id);
|
|
3054
|
+
if (!text2 && !ids.length) throw new Error("The photo could not be sent.");
|
|
3055
|
+
const win = RN3.Dimensions.get("screen");
|
|
3056
|
+
chat.take(
|
|
3057
|
+
await api.sendChat({
|
|
3058
|
+
body: text2,
|
|
3059
|
+
email: needEmail && email.trim() ? email.trim() : void 0,
|
|
3060
|
+
...ids.length ? { attachments: ids } : {},
|
|
3061
|
+
// A little context for a bug report: the system and the screen size.
|
|
3062
|
+
context: { os: `${RN3.Platform.OS === "ios" ? "iOS" : RN3.Platform.OS === "android" ? "Android" : RN3.Platform.OS} ${RN3.Platform.Version ?? ""}`.trim(), browser: "App", viewport: `${Math.round(win.width)}\xD7${Math.round(win.height)}` }
|
|
3063
|
+
})
|
|
3064
|
+
);
|
|
2920
3065
|
} catch (e) {
|
|
2921
3066
|
setBody(text2);
|
|
3067
|
+
setPhotos(files.filter((f) => f.id));
|
|
2922
3068
|
chat.setError(e.message);
|
|
2923
3069
|
} finally {
|
|
2924
3070
|
setPending(null);
|
|
2925
3071
|
}
|
|
2926
3072
|
};
|
|
3073
|
+
const localUris = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
|
|
3074
|
+
for (const f of photos) localUris.current.set(f.key, f.uri);
|
|
3075
|
+
const fileUrl = (id) => localUris.current.get(id) ?? client.api()?.attachmentUrl(id) ?? "";
|
|
2927
3076
|
const ink = inkOn(p.accent);
|
|
2928
3077
|
const dark = p.bg === DARK.bg;
|
|
2929
|
-
const
|
|
2930
|
-
const
|
|
2931
|
-
const
|
|
2932
|
-
const
|
|
3078
|
+
const r = CORNERS[o.corners ?? "round"];
|
|
3079
|
+
const plain = o.header !== "color";
|
|
3080
|
+
const hu = dark ? HU.dark : HU.light;
|
|
3081
|
+
const surf = {};
|
|
3082
|
+
const segLift = dark ? {} : hairline;
|
|
3083
|
+
const canvas = hu.bg;
|
|
3084
|
+
const raised = hu.surface;
|
|
3085
|
+
const title = o.title?.trim() ?? "";
|
|
2933
3086
|
const bubble = (m, isPending = false) => {
|
|
2934
3087
|
const mine = m.from === "visitor";
|
|
2935
3088
|
return (0, import_react2.createElement)(
|
|
2936
3089
|
RN3.View,
|
|
2937
3090
|
{ key: m.id, style: { alignSelf: mine ? "flex-end" : "flex-start", maxWidth: "82%", gap: 4 } },
|
|
2938
|
-
(
|
|
3091
|
+
...(m.attachments ?? []).map(
|
|
3092
|
+
(f) => (0, import_react2.createElement)(
|
|
3093
|
+
RN3.Pressable,
|
|
3094
|
+
{
|
|
3095
|
+
key: f.id,
|
|
3096
|
+
onPress: () => void RN3.Linking.openURL(fileUrl(f.id)).catch(() => void 0),
|
|
3097
|
+
accessibilityRole: "imagebutton",
|
|
3098
|
+
accessibilityLabel: f.name,
|
|
3099
|
+
style: { alignSelf: mine ? "flex-end" : "flex-start", opacity: isPending ? 0.7 : 1 }
|
|
3100
|
+
},
|
|
3101
|
+
f.type.startsWith("image/") ? (0, import_react2.createElement)(RN3.Image, { source: { uri: fileUrl(f.id) }, resizeMode: "cover", style: { width: 200, height: 150, borderRadius: r.bubble, backgroundColor: p.soft } }) : (0, import_react2.createElement)(RN3.View, { style: { backgroundColor: p.soft, borderRadius: r.card, paddingHorizontal: 10, paddingVertical: 7 } }, k.text(`\u{1F4C4} ${f.name}`, { fontSize: 13 }))
|
|
3102
|
+
)
|
|
3103
|
+
),
|
|
3104
|
+
!m.body && m.attachments?.length ? null : (0, import_react2.createElement)(
|
|
2939
3105
|
RN3.View,
|
|
2940
3106
|
{
|
|
2941
3107
|
style: {
|
|
2942
|
-
backgroundColor: mine ? p.accent :
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
3108
|
+
backgroundColor: mine ? p.accent : hu.surface,
|
|
3109
|
+
...mine ? {} : surf,
|
|
3110
|
+
borderRadius: r.bubble,
|
|
3111
|
+
borderBottomRightRadius: mine ? r.tail : r.bubble,
|
|
3112
|
+
borderBottomLeftRadius: mine ? r.bubble : r.tail,
|
|
2946
3113
|
paddingHorizontal: 14,
|
|
2947
3114
|
paddingVertical: 9,
|
|
2948
3115
|
opacity: isPending ? 0.7 : 1
|
|
2949
3116
|
}
|
|
2950
3117
|
},
|
|
2951
|
-
k.text(m.body, { color: mine ? ink :
|
|
3118
|
+
k.text(m.body, { color: mine ? ink : hu.fg })
|
|
2952
3119
|
),
|
|
2953
3120
|
...(m.articles ?? []).map(
|
|
2954
3121
|
(a) => (0, import_react2.createElement)(
|
|
2955
3122
|
RN3.Pressable,
|
|
2956
|
-
{ key: a.id, onPress: () => setView({ article: a.id }), accessibilityRole: "link", style: {
|
|
3123
|
+
{ key: a.id, onPress: () => setView({ article: a.id }), accessibilityRole: "link", style: { backgroundColor: p.soft, borderRadius: r.card, paddingHorizontal: 10, paddingVertical: 7 } },
|
|
2957
3124
|
k.text(`\u{1F4C4} ${a.title}`, { fontSize: 13 })
|
|
2958
3125
|
)
|
|
2959
3126
|
),
|
|
@@ -2966,60 +3133,80 @@ var MessengerSheet = ({
|
|
|
2966
3133
|
k.text(a.title, { flex: 1 }),
|
|
2967
3134
|
glyph("next", p.accent, 14)
|
|
2968
3135
|
);
|
|
2969
|
-
const card = (children, style = {}) => (0, import_react2.createElement)(RN3.View, { style: { backgroundColor: raised, borderRadius:
|
|
3136
|
+
const card = (children, style = {}) => (0, import_react2.createElement)(RN3.View, { style: { backgroundColor: raised, borderRadius: r.card, padding: 16, ...surf, ...style } }, ...children);
|
|
2970
3137
|
const searchRow = (onPress) => (0, import_react2.createElement)(
|
|
2971
3138
|
RN3.Pressable,
|
|
2972
|
-
{ key: "s", onPress, accessibilityRole: "search", style: { flexDirection: "row", alignItems: "center", height: 44, borderRadius:
|
|
3139
|
+
{ key: "s", onPress, accessibilityRole: "search", style: { flexDirection: "row", alignItems: "center", height: 44, borderRadius: r.field, paddingHorizontal: 14, backgroundColor: hu.def } },
|
|
2973
3140
|
k.muted("Search for help", { flex: 1, fontSize: 15 }),
|
|
2974
3141
|
glyph("search", p.accent, 16)
|
|
2975
3142
|
);
|
|
2976
|
-
const search = k.field({ value: query, onChange: setQuery, placeholder: "Search for help", max: 200, style: { borderWidth: 0, borderRadius:
|
|
3143
|
+
const search = k.field({ value: query, onChange: setQuery, placeholder: "Search for help", max: 200, style: { borderWidth: 0, borderRadius: r.field, backgroundColor: raised, ...surf } });
|
|
3144
|
+
const first = client.getUser()?.name?.trim().split(/\s+/)[0];
|
|
3145
|
+
const rest = greeting.replace(/^(hi|hello|hey|hallo)( there)?\s*[!,.]*\s*/i, "").trim() || "How can we help you today?";
|
|
3146
|
+
const hero = (fg, sub) => (0, import_react2.createElement)(
|
|
3147
|
+
RN3.Text,
|
|
3148
|
+
{ key: "hero", accessibilityRole: "header", style: { color: fg, fontSize: 25, fontWeight: "600", letterSpacing: -0.6, lineHeight: 30 } },
|
|
3149
|
+
first ? `Hi, ${first} \u{1F44B}
|
|
3150
|
+
` : "Hi there \u{1F44B}\n",
|
|
3151
|
+
(0, import_react2.createElement)(RN3.Text, { style: { color: sub, fontWeight: "500" } }, rest)
|
|
3152
|
+
);
|
|
2977
3153
|
const last = thread?.messages.at(-1);
|
|
2978
3154
|
const showHelp = helpOn && arts.any !== false;
|
|
2979
3155
|
let content;
|
|
2980
|
-
if (view === "home")
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
{ key: "home", style: { flex: 1, backgroundColor: canvas }, contentContainerStyle: { paddingBottom: 24 }, keyboardShouldPersistTaps: "handled" },
|
|
2984
|
-
(0, import_react2.createElement)(RN3.View, { style: { backgroundColor: p.accent, paddingHorizontal: 22, paddingTop: 4, paddingBottom: 64 } }, (0, import_react2.createElement)(RN3.Text, { style: { color: ink, fontSize: 28, fontWeight: "700", letterSpacing: -0.4, lineHeight: 34 } }, greeting)),
|
|
3156
|
+
if (view === "home") {
|
|
3157
|
+
const cards = [
|
|
3158
|
+
// "Ask a question": the words go into the chat's field and are sent as the chat opens.
|
|
2985
3159
|
(0, import_react2.createElement)(
|
|
2986
3160
|
RN3.View,
|
|
2987
|
-
{ style: {
|
|
2988
|
-
|
|
2989
|
-
RN3.Pressable,
|
|
2990
|
-
{ key: "recent", onPress: () => setView("messages"), accessibilityRole: "button" },
|
|
2991
|
-
card([
|
|
2992
|
-
k.muted("Recent message", { fontSize: 12, fontWeight: "600", marginBottom: 10 }),
|
|
2993
|
-
(0, import_react2.createElement)(
|
|
2994
|
-
RN3.View,
|
|
2995
|
-
{ key: "r", style: { flexDirection: "row", alignItems: "center", gap: 12 } },
|
|
2996
|
-
teamMark(title, p.accent, ink),
|
|
2997
|
-
(0, import_react2.createElement)(RN3.View, { style: { flex: 1, gap: 2 } }, (0, import_react2.createElement)(RN3.Text, { numberOfLines: 1, style: { color: p.fg, fontSize: 15 } }, last.body), k.muted(`${last.from === "visitor" ? "You" : whoLabel[last.from]} \xB7 ${timeAgo(last.at)}`, { fontSize: 12 })),
|
|
2998
|
-
chat.unseen.length ? (0, import_react2.createElement)(RN3.View, { style: { minWidth: 20, height: 20, borderRadius: 10, paddingHorizontal: 6, backgroundColor: p.danger, alignItems: "center", justifyContent: "center" } }, k.text(String(chat.unseen.length), { color: "#fff", fontSize: 12, fontWeight: "700" })) : glyph("next", p.muted, 14)
|
|
2999
|
-
)
|
|
3000
|
-
])
|
|
3001
|
-
) : null,
|
|
3161
|
+
{ key: "ask", style: { flexDirection: "row", alignItems: "center", gap: 8, height: 48, borderRadius: 24, paddingLeft: 18, paddingRight: 6, backgroundColor: hu.surface, ...surf } },
|
|
3162
|
+
k.field({ value: body, onChange: setBody, placeholder: "Ask a question", max: 4e3, style: { flex: 1, borderWidth: 0, paddingHorizontal: 0, backgroundColor: "transparent" } }),
|
|
3002
3163
|
(0, import_react2.createElement)(
|
|
3003
3164
|
RN3.Pressable,
|
|
3004
|
-
{
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3165
|
+
{
|
|
3166
|
+
onPress: () => {
|
|
3167
|
+
setView("messages");
|
|
3168
|
+
if (body.trim()) void send();
|
|
3169
|
+
},
|
|
3170
|
+
accessibilityRole: "button",
|
|
3171
|
+
accessibilityLabel: "Send",
|
|
3172
|
+
style: { width: 36, height: 36, borderRadius: 18, alignItems: "center", justifyContent: "center", backgroundColor: body.trim() ? p.accent : hu.def }
|
|
3173
|
+
},
|
|
3174
|
+
glyph("up", body.trim() ? ink : hu.muted, 18)
|
|
3175
|
+
)
|
|
3176
|
+
),
|
|
3177
|
+
last ? (0, import_react2.createElement)(
|
|
3178
|
+
RN3.Pressable,
|
|
3179
|
+
{ key: "recent", onPress: () => setView("messages"), accessibilityRole: "button" },
|
|
3180
|
+
card([
|
|
3181
|
+
(0, import_react2.createElement)(
|
|
3182
|
+
RN3.View,
|
|
3183
|
+
{ key: "r", style: { flexDirection: "row", alignItems: "center", gap: 10 } },
|
|
3184
|
+
title ? teamMark(title, alpha(p.accent, 0.16), p.accent, 26) : null,
|
|
3185
|
+
(0, import_react2.createElement)(RN3.View, { style: { flex: 1, gap: 1 } }, (0, import_react2.createElement)(RN3.Text, { numberOfLines: 1, style: { color: hu.fg, fontSize: 14, fontWeight: "500" } }, last.body || (last.attachments?.length ? `Sent ${last.attachments.length === 1 ? last.attachments[0].name : `${last.attachments.length} files`}` : "")), k.muted(`${last.from === "visitor" ? "You" : whoLabel[last.from]} \xB7 ${timeAgo(last.at)}`, { fontSize: 11.5 })),
|
|
3186
|
+
chat.unseen.length ? (0, import_react2.createElement)(RN3.View, { style: { width: 8, height: 8, borderRadius: 4, backgroundColor: p.danger } }) : glyph("next", p.muted, 14)
|
|
3011
3187
|
)
|
|
3012
|
-
)
|
|
3013
|
-
|
|
3014
|
-
)
|
|
3188
|
+
], { paddingVertical: 10, paddingHorizontal: 12 })
|
|
3189
|
+
) : null,
|
|
3190
|
+
showHelp ? card([searchRow(() => setView("help")), ...(arts.list ?? []).slice(0, 4).map(row)], { padding: 8, gap: 2 }) : null
|
|
3191
|
+
];
|
|
3192
|
+
content = plain ? (0, import_react2.createElement)(
|
|
3193
|
+
RN3.ScrollView,
|
|
3194
|
+
{ key: "home", style: { flex: 1, backgroundColor: canvas }, contentContainerStyle: { paddingHorizontal: 16, paddingTop: 4, paddingBottom: 24, gap: 12 }, keyboardShouldPersistTaps: "handled" },
|
|
3195
|
+
(0, import_react2.createElement)(RN3.View, { key: "top", style: { paddingHorizontal: 6, paddingBottom: 10 } }, hero(p.fg, p.muted)),
|
|
3196
|
+
...cards
|
|
3197
|
+
) : (0, import_react2.createElement)(
|
|
3198
|
+
RN3.ScrollView,
|
|
3199
|
+
{ key: "home", style: { flex: 1, backgroundColor: canvas }, contentContainerStyle: { paddingBottom: 24 }, keyboardShouldPersistTaps: "handled" },
|
|
3200
|
+
(0, import_react2.createElement)(RN3.View, { style: { backgroundColor: p.accent, paddingHorizontal: 22, paddingTop: 4, paddingBottom: 64 } }, hero(ink, alpha(ink, 0.8))),
|
|
3201
|
+
(0, import_react2.createElement)(RN3.View, { style: { marginTop: -48, paddingHorizontal: 16, gap: 12 } }, ...cards)
|
|
3015
3202
|
);
|
|
3016
|
-
else if (view === "messages")
|
|
3203
|
+
} else if (view === "messages")
|
|
3017
3204
|
content = (0, import_react2.createElement)(
|
|
3018
3205
|
RN3.View,
|
|
3019
|
-
{ key: "messages", style: { flex: 1, backgroundColor:
|
|
3206
|
+
{ key: "messages", style: { flex: 1, backgroundColor: hu.bg } },
|
|
3020
3207
|
(0, import_react2.createElement)(
|
|
3021
3208
|
RN3.ScrollView,
|
|
3022
|
-
{ ref: scroll, style: { flex: 1 }, contentContainerStyle: {
|
|
3209
|
+
{ ref: scroll, style: { flex: 1 }, contentContainerStyle: { paddingHorizontal: 16, paddingTop: 4, paddingBottom: 16, gap: 12 } },
|
|
3023
3210
|
bubble({ id: "greeting", from: "team", body: greeting, at: "" }),
|
|
3024
3211
|
!chat.loaded ? k.muted("Loading\u2026") : null,
|
|
3025
3212
|
...(thread?.messages ?? []).map((m) => bubble(m)),
|
|
@@ -3028,23 +3215,57 @@ var MessengerSheet = ({
|
|
|
3028
3215
|
),
|
|
3029
3216
|
(0, import_react2.createElement)(
|
|
3030
3217
|
RN3.View,
|
|
3031
|
-
{ style: { paddingHorizontal: 12, paddingTop: 8, paddingBottom:
|
|
3218
|
+
{ style: { paddingHorizontal: 12, paddingTop: 8, paddingBottom: 6, gap: 8 } },
|
|
3032
3219
|
chat.error ? k.text(chat.error, { color: p.danger, fontSize: 12 }) : null,
|
|
3033
|
-
needEmail ? k.field({ value: email, onChange: setEmail, placeholder: "Your email (optional), so we can follow up", email: true, max: 200, style: { borderWidth: emailOk ? 0 : 1, borderColor: p.danger, borderRadius:
|
|
3220
|
+
needEmail ? k.field({ value: email, onChange: setEmail, placeholder: "Your email (optional), so we can follow up", email: true, max: 200, style: { borderWidth: emailOk ? 0 : 1, borderColor: p.danger, borderRadius: r.field, paddingHorizontal: 16, backgroundColor: hu.surface, ...surf } }) : null,
|
|
3221
|
+
// Photos picked for the next message, each with a way to take it out again.
|
|
3222
|
+
photos.length ? (0, import_react2.createElement)(
|
|
3223
|
+
RN3.View,
|
|
3224
|
+
{ style: { flexDirection: "row", flexWrap: "wrap", gap: 10, paddingTop: 6, paddingHorizontal: 2 } },
|
|
3225
|
+
...photos.map(
|
|
3226
|
+
(f) => (0, import_react2.createElement)(
|
|
3227
|
+
RN3.View,
|
|
3228
|
+
{ key: f.key, style: { width: 56, height: 56 } },
|
|
3229
|
+
(0, import_react2.createElement)(RN3.Image, { source: { uri: f.uri }, resizeMode: "cover", accessibilityLabel: f.id ? f.name : `${f.name}, uploading`, style: { width: 56, height: 56, borderRadius: Math.max(4, r.card - 4), backgroundColor: p.soft, opacity: f.id ? 1 : 0.5 } }),
|
|
3230
|
+
(0, import_react2.createElement)(
|
|
3231
|
+
RN3.Pressable,
|
|
3232
|
+
{
|
|
3233
|
+
onPress: () => setPhotos((list) => list.filter((x) => x.key !== f.key)),
|
|
3234
|
+
accessibilityRole: "button",
|
|
3235
|
+
accessibilityLabel: `Remove ${f.name}`,
|
|
3236
|
+
hitSlop: 8,
|
|
3237
|
+
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" }
|
|
3238
|
+
},
|
|
3239
|
+
glyph("cross", p.bg, 10)
|
|
3240
|
+
)
|
|
3241
|
+
)
|
|
3242
|
+
)
|
|
3243
|
+
) : null,
|
|
3244
|
+
// The message field on top, the paperclip (when the app has expo-image-picker) and the send button under it.
|
|
3034
3245
|
(0, import_react2.createElement)(
|
|
3035
3246
|
RN3.View,
|
|
3036
|
-
{ style: {
|
|
3037
|
-
k.field({ value: body, onChange: setBody, placeholder: "Write a message\u2026", multiline: true, max: 4e3, style: {
|
|
3247
|
+
{ style: { gap: 2, borderRadius: 20, paddingTop: 10, paddingBottom: 6, paddingRight: 6, paddingLeft: 16, backgroundColor: hu.surface, ...surf } },
|
|
3248
|
+
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" } }),
|
|
3038
3249
|
(0, import_react2.createElement)(
|
|
3039
|
-
RN3.
|
|
3040
|
-
{
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
accessibilityRole: "button",
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
},
|
|
3047
|
-
|
|
3250
|
+
RN3.View,
|
|
3251
|
+
{ style: { flexDirection: "row", alignItems: "center", marginLeft: -8 } },
|
|
3252
|
+
imagePicker?.launchImageLibraryAsync ? (0, import_react2.createElement)(
|
|
3253
|
+
RN3.Pressable,
|
|
3254
|
+
{ 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" } },
|
|
3255
|
+
glyph("clip", hu.muted, 19)
|
|
3256
|
+
) : null,
|
|
3257
|
+
(0, import_react2.createElement)(RN3.View, { style: { flex: 1 } }),
|
|
3258
|
+
(0, import_react2.createElement)(
|
|
3259
|
+
RN3.Pressable,
|
|
3260
|
+
{
|
|
3261
|
+
onPress: () => void send(),
|
|
3262
|
+
disabled: !canSend,
|
|
3263
|
+
accessibilityRole: "button",
|
|
3264
|
+
accessibilityLabel: "Send",
|
|
3265
|
+
style: { width: 34, height: 34, borderRadius: 17, alignItems: "center", justifyContent: "center", backgroundColor: canSend ? p.accent : hu.def }
|
|
3266
|
+
},
|
|
3267
|
+
glyph("up", canSend ? ink : hu.muted, 18)
|
|
3268
|
+
)
|
|
3048
3269
|
)
|
|
3049
3270
|
)
|
|
3050
3271
|
)
|
|
@@ -3054,7 +3275,7 @@ var MessengerSheet = ({
|
|
|
3054
3275
|
for (const a of arts.list ?? []) groups.set(a.collection, [...groups.get(a.collection) ?? [], a]);
|
|
3055
3276
|
content = (0, import_react2.createElement)(
|
|
3056
3277
|
RN3.ScrollView,
|
|
3057
|
-
{ key: "help", style: { flex: 1, backgroundColor: canvas }, contentContainerStyle: {
|
|
3278
|
+
{ key: "help", style: { flex: 1, backgroundColor: canvas }, contentContainerStyle: { paddingHorizontal: 16, paddingTop: 4, paddingBottom: 16, gap: 12 }, keyboardShouldPersistTaps: "handled" },
|
|
3058
3279
|
search,
|
|
3059
3280
|
arts.list === null ? k.muted("Loading\u2026") : null,
|
|
3060
3281
|
arts.list?.length === 0 ? (0, import_react2.createElement)(RN3.View, { style: { gap: 10, alignItems: "center", paddingVertical: 24 } }, k.muted(query.trim() ? `Nothing found for \u201C${query.trim()}\u201D.` : "No help articles yet."), k.btn("Send us a message", "primary", () => setView("messages"))) : null,
|
|
@@ -3063,46 +3284,73 @@ var MessengerSheet = ({
|
|
|
3063
3284
|
} else
|
|
3064
3285
|
content = (0, import_react2.createElement)(
|
|
3065
3286
|
RN3.ScrollView,
|
|
3066
|
-
{ key: `a-${view.article}`, style: { flex: 1 }, contentContainerStyle: {
|
|
3287
|
+
{ key: `a-${view.article}`, style: { flex: 1 }, contentContainerStyle: { paddingHorizontal: 16, paddingTop: 4, paddingBottom: 32 } },
|
|
3067
3288
|
(0, import_react2.createElement)(ArticleView, { client, p, id: view.article, onAsk: () => setView("messages") })
|
|
3068
3289
|
);
|
|
3069
3290
|
const current = typeof view === "string" ? view : "help";
|
|
3070
|
-
const tab = (v, name, icon, badge = 0) =>
|
|
3291
|
+
const tab = (v, name, icon, badge = 0) => {
|
|
3292
|
+
const on = current === v;
|
|
3293
|
+
return (0, import_react2.createElement)(
|
|
3294
|
+
RN3.Pressable,
|
|
3295
|
+
{
|
|
3296
|
+
key: v,
|
|
3297
|
+
onPress: () => setView(v),
|
|
3298
|
+
accessibilityRole: "tab",
|
|
3299
|
+
accessibilityState: { selected: on },
|
|
3300
|
+
style: { flex: 1, flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 6, height: 36, borderRadius: 18, ...on ? { backgroundColor: hu.segment, ...segLift } : {} }
|
|
3301
|
+
},
|
|
3302
|
+
glyph(icon, on ? hu.fg : hu.muted, 17),
|
|
3303
|
+
k.text(name, { fontSize: 13.5, fontWeight: "500", color: on ? hu.fg : hu.muted }),
|
|
3304
|
+
badge ? (0, import_react2.createElement)(RN3.View, { style: { position: "absolute", top: 7, right: 12, width: 8, height: 8, borderRadius: 4, backgroundColor: p.danger } }) : null
|
|
3305
|
+
);
|
|
3306
|
+
};
|
|
3307
|
+
const onAccent = view === "home" && !plain;
|
|
3308
|
+
const ground = onAccent ? p.accent : hu.bg;
|
|
3309
|
+
const back = (to, label) => (0, import_react2.createElement)(
|
|
3071
3310
|
RN3.Pressable,
|
|
3072
|
-
{ key:
|
|
3073
|
-
glyph(
|
|
3074
|
-
k.text(name, { fontSize: 12, fontWeight: "600", color: current === v ? p.accent : p.muted }),
|
|
3075
|
-
badge ? (0, import_react2.createElement)(RN3.View, { style: { position: "absolute", top: 4, right: "28%", minWidth: 18, height: 18, borderRadius: 9, backgroundColor: p.danger, alignItems: "center", justifyContent: "center" } }, k.text(String(Math.min(badge, 9)), { color: "#fff", fontSize: 11, fontWeight: "700" })) : null
|
|
3311
|
+
{ key: "back", onPress: () => setView(to), accessibilityRole: "button", accessibilityLabel: label, hitSlop: 10, style: { width: 32, height: 32, alignItems: "center", justifyContent: "center", transform: [{ rotate: "180deg" }] } },
|
|
3312
|
+
glyph("next", hu.fg, 18)
|
|
3076
3313
|
);
|
|
3077
|
-
const onHome = view === "home";
|
|
3078
|
-
const headFg = onHome ? ink : p.fg;
|
|
3079
3314
|
const head = (0, import_react2.createElement)(
|
|
3080
3315
|
RN3.View,
|
|
3081
|
-
{ key: "head", style: { flexDirection: "row", alignItems: "center", gap:
|
|
3082
|
-
typeof view !== "string" ? (
|
|
3083
|
-
|
|
3316
|
+
{ key: "head", style: { flexDirection: "row", alignItems: "center", gap: 8, paddingLeft: 12, paddingRight: 12, paddingTop: 12, paddingBottom: 6, backgroundColor: ground } },
|
|
3317
|
+
typeof view !== "string" ? back("help", "All articles") : view === "messages" ? back("home", "Back to home") : view === "home" && (o.logo || title) ? o.logo ? (0, import_react2.createElement)(RN3.Image, { key: "logo", source: { uri: o.logo }, accessibilityIgnoresInvertColors: true, style: { width: 28, height: 28, borderRadius: 8, marginLeft: 4 } }) : teamMark(title, alpha(p.accent, 0.16), p.accent, 28) : null,
|
|
3318
|
+
// In the chat: who answers, in the middle.
|
|
3319
|
+
view === "messages" && title ? (0, import_react2.createElement)(
|
|
3084
3320
|
RN3.View,
|
|
3085
|
-
{ style: { flex: 1,
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
),
|
|
3089
|
-
(0, import_react2.createElement)(RN3.Pressable, { onPress: close, accessibilityRole: "button", accessibilityLabel: "Close", hitSlop: 12, style: { width: 32, height: 32, borderRadius: 10, alignItems: "center", justifyContent: "center" } }, glyph("cross",
|
|
3321
|
+
{ style: { flex: 1, flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 8 } },
|
|
3322
|
+
teamMark(title, alpha(p.accent, 0.16), p.accent, 28),
|
|
3323
|
+
(0, import_react2.createElement)(RN3.Text, { numberOfLines: 1, style: { flexShrink: 1, color: hu.fg, fontSize: 15, fontWeight: "600" } }, title)
|
|
3324
|
+
) : (0, import_react2.createElement)(RN3.View, { style: { flex: 1 } }),
|
|
3325
|
+
(0, import_react2.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))
|
|
3090
3326
|
);
|
|
3327
|
+
const powered = brand ? (0, import_react2.createElement)(
|
|
3328
|
+
RN3.View,
|
|
3329
|
+
{ key: "brand", style: { paddingTop: 2, paddingBottom: 6, alignItems: "center", backgroundColor: hu.bg } },
|
|
3330
|
+
(0, import_react2.createElement)(
|
|
3331
|
+
RN3.Text,
|
|
3332
|
+
{ onPress: () => void RN3.Linking.openURL(BRAND_URL).catch(() => void 0), accessibilityRole: "link", hitSlop: 8, style: { color: p.muted, fontSize: 11.5 } },
|
|
3333
|
+
"Powered by ",
|
|
3334
|
+
(0, import_react2.createElement)(RN3.Text, { style: { fontWeight: "600" } }, "MRRocket")
|
|
3335
|
+
)
|
|
3336
|
+
) : null;
|
|
3091
3337
|
return (0, import_react2.createElement)(
|
|
3092
3338
|
RN3.Modal,
|
|
3093
3339
|
{ visible: true, animationType: "slide", presentationStyle: "pageSheet", onRequestClose: close },
|
|
3094
3340
|
(0, import_react2.createElement)(
|
|
3095
3341
|
RN3.KeyboardAvoidingView,
|
|
3096
|
-
{ behavior: RN3.Platform.OS === "ios" ? "padding" : void 0, style: { flex: 1, backgroundColor:
|
|
3342
|
+
{ behavior: RN3.Platform.OS === "ios" ? "padding" : void 0, style: { flex: 1, backgroundColor: hu.bg } },
|
|
3097
3343
|
head,
|
|
3098
3344
|
content,
|
|
3099
|
-
(0, import_react2.createElement)(
|
|
3345
|
+
view === "messages" ? null : (0, import_react2.createElement)(
|
|
3100
3346
|
RN3.View,
|
|
3101
|
-
{ key: "tabs", accessibilityRole: "tablist", style: { flexDirection: "row",
|
|
3347
|
+
{ key: "tabs", accessibilityRole: "tablist", style: { flexDirection: "row", marginHorizontal: 12, marginTop: 6, marginBottom: 6, padding: 4, borderRadius: 22, backgroundColor: hu.surface } },
|
|
3102
3348
|
tab("home", "Home", "home"),
|
|
3103
|
-
tab("messages", "Messages", "chat",
|
|
3349
|
+
tab("messages", "Messages", "chat", chat.unseen.length),
|
|
3104
3350
|
showHelp ? tab("help", "Help", "help") : null
|
|
3105
|
-
)
|
|
3351
|
+
),
|
|
3352
|
+
powered,
|
|
3353
|
+
(0, import_react2.createElement)(RN3.View, { style: { height: 16 } })
|
|
3106
3354
|
)
|
|
3107
3355
|
);
|
|
3108
3356
|
};
|
|
@@ -3127,6 +3375,9 @@ var MRRocketMessenger = ({ client, appearance, options: given = {} }) => {
|
|
|
3127
3375
|
preview: look.preview,
|
|
3128
3376
|
askEmail: look.askEmail,
|
|
3129
3377
|
haptics: look.sound,
|
|
3378
|
+
corners: look.corners,
|
|
3379
|
+
header: look.header,
|
|
3380
|
+
logo: look.logo,
|
|
3130
3381
|
...defined
|
|
3131
3382
|
} : given;
|
|
3132
3383
|
const p = usePalette({ theme: appearance?.theme ?? look?.theme, accent: appearance?.accent ?? look?.accent });
|
|
@@ -3155,37 +3406,42 @@ var MRRocketMessenger = ({ client, appearance, options: given = {} }) => {
|
|
|
3155
3406
|
const unseen = chat.unseen.length;
|
|
3156
3407
|
const ink = inkOn(p.accent);
|
|
3157
3408
|
const label = options.label?.trim() ?? "";
|
|
3158
|
-
const
|
|
3409
|
+
const r = CORNERS[options.corners ?? "round"];
|
|
3410
|
+
const [edge, side] = (options.position ?? "bottom-right").split("-");
|
|
3411
|
+
const place = {
|
|
3412
|
+
...edge === "top" ? { top: 60 } : edge === "bottom" ? { bottom: 34 } : { top: 0, bottom: 0, justifyContent: "center" },
|
|
3413
|
+
...side === "left" ? { left: 20 } : side === "right" ? { right: 20 } : { left: 0, right: 0, alignItems: "center" }
|
|
3414
|
+
};
|
|
3159
3415
|
const launcher = options.launcher ? (0, import_react2.createElement)(
|
|
3160
|
-
RN3.
|
|
3161
|
-
{
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
}
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3416
|
+
RN3.View,
|
|
3417
|
+
{ key: "launcher", pointerEvents: "box-none", style: { position: "absolute", ...place } },
|
|
3418
|
+
(0, import_react2.createElement)(
|
|
3419
|
+
RN3.Pressable,
|
|
3420
|
+
{
|
|
3421
|
+
onPress: () => openAt(unseen ? "messages" : "home"),
|
|
3422
|
+
accessibilityRole: "button",
|
|
3423
|
+
accessibilityLabel: unseen ? `Open chat, ${unseen} new` : "Open chat",
|
|
3424
|
+
style: {
|
|
3425
|
+
height: label ? 50 : 56,
|
|
3426
|
+
minWidth: label ? 0 : 56,
|
|
3427
|
+
paddingHorizontal: label ? 18 : 0,
|
|
3428
|
+
borderRadius: 28,
|
|
3429
|
+
flexDirection: "row",
|
|
3430
|
+
gap: 8,
|
|
3431
|
+
backgroundColor: p.accent,
|
|
3432
|
+
alignItems: "center",
|
|
3433
|
+
justifyContent: "center",
|
|
3434
|
+
shadowColor: p.accent,
|
|
3435
|
+
shadowOpacity: 0.4,
|
|
3436
|
+
shadowRadius: 12,
|
|
3437
|
+
shadowOffset: { width: 0, height: 6 },
|
|
3438
|
+
elevation: 6
|
|
3439
|
+
}
|
|
3440
|
+
},
|
|
3441
|
+
glyph("chat", ink, label ? 20 : 26),
|
|
3442
|
+
label ? k.text(label, { color: ink, fontWeight: "700" }) : null,
|
|
3443
|
+
unseen ? (0, import_react2.createElement)(RN3.View, { style: { position: "absolute", top: -2, right: -2, minWidth: 20, height: 20, borderRadius: 10, backgroundColor: p.danger, alignItems: "center", justifyContent: "center", borderWidth: 2, borderColor: p.bg } }, k.text(String(Math.min(unseen, 9)), { color: "#fff", fontSize: 11, fontWeight: "700" })) : null
|
|
3444
|
+
)
|
|
3189
3445
|
) : null;
|
|
3190
3446
|
const toast = banner ? (0, import_react2.createElement)(
|
|
3191
3447
|
RN3.Pressable,
|
|
@@ -3194,15 +3450,25 @@ var MRRocketMessenger = ({ client, appearance, options: given = {} }) => {
|
|
|
3194
3450
|
onPress: () => openAt("messages"),
|
|
3195
3451
|
accessibilityRole: "button",
|
|
3196
3452
|
accessibilityLiveRegion: "polite",
|
|
3197
|
-
style: {
|
|
3453
|
+
style: {
|
|
3454
|
+
position: "absolute",
|
|
3455
|
+
top: 54,
|
|
3456
|
+
left: 16,
|
|
3457
|
+
right: 16,
|
|
3458
|
+
gap: 3,
|
|
3459
|
+
backgroundColor: p.bg === DARK.bg ? p.soft : p.bg,
|
|
3460
|
+
borderRadius: r.card + 2,
|
|
3461
|
+
paddingHorizontal: 16,
|
|
3462
|
+
paddingVertical: 13,
|
|
3463
|
+
shadowColor: "#101218",
|
|
3464
|
+
shadowOpacity: 0.14,
|
|
3465
|
+
shadowRadius: 18,
|
|
3466
|
+
shadowOffset: { width: 0, height: 8 },
|
|
3467
|
+
elevation: 6
|
|
3468
|
+
}
|
|
3198
3469
|
},
|
|
3199
|
-
|
|
3200
|
-
(0, import_react2.createElement)(
|
|
3201
|
-
RN3.View,
|
|
3202
|
-
{ style: { flex: 1, gap: 2 } },
|
|
3203
|
-
k.muted(`${whoLabel[banner.from]}${unseen > 1 ? ` \xB7 ${unseen} new` : ""}`, { fontWeight: "600", fontSize: 12 }),
|
|
3204
|
-
(0, import_react2.createElement)(RN3.Text, { numberOfLines: 3, style: { color: p.fg, fontSize: 15 } }, banner.body)
|
|
3205
|
-
)
|
|
3470
|
+
k.muted(`${whoLabel[banner.from]}${unseen > 1 ? ` \xB7 ${unseen} new` : ""}`, { fontWeight: "600", fontSize: 12 }),
|
|
3471
|
+
(0, import_react2.createElement)(RN3.Text, { numberOfLines: 3, style: { color: p.fg, fontSize: 15, lineHeight: 20 } }, banner.body)
|
|
3206
3472
|
) : null;
|
|
3207
3473
|
if (!launcher && !toast && !open) return null;
|
|
3208
3474
|
return (0, import_react2.createElement)(
|
|
@@ -3210,7 +3476,7 @@ var MRRocketMessenger = ({ client, appearance, options: given = {} }) => {
|
|
|
3210
3476
|
{ pointerEvents: "box-none", style: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0 } },
|
|
3211
3477
|
launcher,
|
|
3212
3478
|
toast,
|
|
3213
|
-
open ? (0, import_react2.createElement)(MessengerSheet, { client, p, o: options, chat, view, setView }) : null
|
|
3479
|
+
open ? (0, import_react2.createElement)(MessengerSheet, { client, p, o: options, chat, view, setView, brand: look?.branding !== false }) : null
|
|
3214
3480
|
);
|
|
3215
3481
|
};
|
|
3216
3482
|
|