@mrrocket/sdk 0.13.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/CHANGELOG.md +8 -1
- package/README.md +43 -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 +11 -2
- package/dist/browser/mrrocket-messenger.js +304 -70
- package/dist/browser/mrrocket.js +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +2 -1
- package/dist/browser.js.map +1 -1
- package/dist/client.d.ts +12 -0
- package/dist/client.js +22 -0
- package/dist/client.js.map +1 -1
- package/dist/context.d.ts +3 -0
- package/dist/context.js.map +1 -1
- package/dist/core.d.ts +4 -1
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/flows-ui.d.ts +6 -4
- package/dist/flows-ui.js +147 -5
- 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/index.d.ts +2 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/messenger-ui.d.ts +37 -2
- package/dist/messenger-ui.js +597 -109
- 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/paywall.d.ts +123 -0
- package/dist/paywall.js +131 -0
- package/dist/paywall.js.map +1 -0
- package/dist/react-native/index.d.cts +235 -8
- package/dist/react-native/index.d.ts +235 -8
- package/dist/react-native/index.js +650 -184
- package/dist/react-native/index.js.map +1 -1
- package/dist/styles-css.d.ts +1 -1
- package/dist/styles-css.js +302 -68
- package/dist/styles-css.js.map +1 -1
- package/package.json +5 -1
|
@@ -41,6 +41,7 @@ __export(native_exports, {
|
|
|
41
41
|
useMessenger: () => useMessenger,
|
|
42
42
|
useNavigationTracking: () => useNavigationTracking,
|
|
43
43
|
useOnboarding: () => useOnboarding,
|
|
44
|
+
usePaywall: () => usePaywall,
|
|
44
45
|
usePush: () => usePush,
|
|
45
46
|
useText: () => useText,
|
|
46
47
|
useVariant: () => useVariant
|
|
@@ -49,7 +50,7 @@ module.exports = __toCommonJS(native_exports);
|
|
|
49
50
|
var import_react3 = require("react");
|
|
50
51
|
|
|
51
52
|
// src/core.ts
|
|
52
|
-
var SDK_VERSION = "0.
|
|
53
|
+
var SDK_VERSION = "0.15.0";
|
|
53
54
|
var propOf = (person, name) => {
|
|
54
55
|
const want = name.trim().toLowerCase();
|
|
55
56
|
for (const [k, v] of Object.entries(person)) if (k.toLowerCase() === want) return v;
|
|
@@ -172,7 +173,14 @@ var textChanges = (config, distinctId, preview) => {
|
|
|
172
173
|
var newDistinctId = () => typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `ck_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;
|
|
173
174
|
|
|
174
175
|
// src/api-client.ts
|
|
175
|
-
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";
|
|
176
184
|
var inkOn = (hex) => {
|
|
177
185
|
const m = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(hex);
|
|
178
186
|
if (!m) return "#ffffff";
|
|
@@ -196,7 +204,8 @@ var call = async (url, init, signal) => {
|
|
|
196
204
|
res = await fetch(url, {
|
|
197
205
|
...init,
|
|
198
206
|
signal,
|
|
199
|
-
|
|
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
|
|
200
209
|
});
|
|
201
210
|
} catch (e) {
|
|
202
211
|
if (e.name === "AbortError") throw e;
|
|
@@ -229,7 +238,19 @@ var createApi = (c) => {
|
|
|
229
238
|
seen: (id) => post(`/surveys/${encodeURIComponent(id)}/seen`, { user: void 0 }),
|
|
230
239
|
respond: (id, r) => post(`/surveys/${encodeURIComponent(id)}/respond`, r),
|
|
231
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). */
|
|
232
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 })}`,
|
|
233
254
|
articles: (q, signal) => call(`${base}/articles?${qs({ key: c.apiKey, q })}`, void 0, signal),
|
|
234
255
|
article: (id, signal) => call(`${base}/articles/${encodeURIComponent(id)}?${qs({ key: c.apiKey, distinctId: c.distinctId })}`, void 0, signal),
|
|
235
256
|
messengerLook: (signal) => call(`${base}/messenger?${qs({ key: c.apiKey })}`, void 0, signal),
|
|
@@ -342,6 +363,9 @@ var pageMatches = (pattern, path) => {
|
|
|
342
363
|
return at === (p.replace(/\/+$/, "") || "/");
|
|
343
364
|
};
|
|
344
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);
|
|
345
369
|
var createFlows = (env) => {
|
|
346
370
|
let config = null;
|
|
347
371
|
let cancel = null;
|
|
@@ -509,18 +533,23 @@ var createFlows = (env) => {
|
|
|
509
533
|
};
|
|
510
534
|
const view = () => {
|
|
511
535
|
if (cancel) {
|
|
512
|
-
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);
|
|
513
540
|
return {
|
|
514
541
|
type: "cancel",
|
|
515
|
-
screen:
|
|
516
|
-
flow:
|
|
517
|
-
reason:
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
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
|
|
524
553
|
};
|
|
525
554
|
}
|
|
526
555
|
const ob = onboardingView();
|
|
@@ -599,12 +628,22 @@ var createFlows = (env) => {
|
|
|
599
628
|
});
|
|
600
629
|
return loading;
|
|
601
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
|
+
};
|
|
602
640
|
const result = (c, outcome, comment) => {
|
|
603
|
-
const offer = c.
|
|
641
|
+
const offer = c.screen === "offer" ? c.offer : void 0;
|
|
604
642
|
return {
|
|
605
643
|
outcome,
|
|
606
644
|
run: c.run,
|
|
607
645
|
...c.reason ? { reason: c.reason, reasonLabel: c.flow.reasons.find((r) => r.id === c.reason)?.label } : {},
|
|
646
|
+
...c.answer ? { answer: c.answer } : {},
|
|
608
647
|
...outcome === "saved" && offer ? { offer } : {},
|
|
609
648
|
...comment?.trim() ? { comment: comment.trim().slice(0, 500) } : {}
|
|
610
649
|
};
|
|
@@ -617,7 +656,8 @@ var createFlows = (env) => {
|
|
|
617
656
|
outcome: r.outcome,
|
|
618
657
|
customer: c.options.customer ?? "consumer",
|
|
619
658
|
...r.reason ? { reason: r.reason } : {},
|
|
620
|
-
...r.
|
|
659
|
+
...r.answer ? { answer: r.answer } : {},
|
|
660
|
+
...r.offer ? { offer: r.offer.kind, ...offerAt(c) } : {},
|
|
621
661
|
...r.comment ? { comment: r.comment } : {}
|
|
622
662
|
});
|
|
623
663
|
if (cancel === c) cancel = null;
|
|
@@ -647,7 +687,7 @@ var createFlows = (env) => {
|
|
|
647
687
|
let resolve = () => {
|
|
648
688
|
};
|
|
649
689
|
const promise = new Promise((r) => resolve = r);
|
|
650
|
-
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 };
|
|
651
691
|
cancel = c;
|
|
652
692
|
env.track(CANCEL_EVENT, { action: "shown", run: c.run, version: flow.version, customer: options.customer ?? "consumer", plain });
|
|
653
693
|
emit();
|
|
@@ -699,29 +739,43 @@ var createFlows = (env) => {
|
|
|
699
739
|
},
|
|
700
740
|
cancel: {
|
|
701
741
|
pick: (reason) => {
|
|
702
|
-
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;
|
|
703
744
|
cancel.reason = reason;
|
|
704
745
|
cancel.error = void 0;
|
|
705
746
|
emit();
|
|
706
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
|
+
},
|
|
707
755
|
next: () => {
|
|
708
756
|
const c = cancel;
|
|
709
|
-
if (!c ||
|
|
710
|
-
|
|
711
|
-
|
|
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;
|
|
712
766
|
emit();
|
|
713
767
|
},
|
|
714
768
|
accept: (comment) => {
|
|
715
769
|
const c = cancel;
|
|
716
770
|
if (!c || c.screen !== "offer" || !c.reason) return;
|
|
717
|
-
const offer = c.flow.offers[c.reason];
|
|
718
771
|
if (!c.busy)
|
|
719
772
|
env.track(CANCEL_EVENT, {
|
|
720
773
|
action: "offer",
|
|
721
774
|
run: c.run,
|
|
722
775
|
version: c.flow.version,
|
|
723
776
|
reason: c.reason,
|
|
724
|
-
offer: offer?.kind,
|
|
777
|
+
offer: c.offer?.kind,
|
|
778
|
+
...offerAt(c),
|
|
725
779
|
outcome: "accepted"
|
|
726
780
|
});
|
|
727
781
|
settle(c, "saved", comment);
|
|
@@ -734,10 +788,16 @@ var createFlows = (env) => {
|
|
|
734
788
|
run: c.run,
|
|
735
789
|
version: c.flow.version,
|
|
736
790
|
reason: c.reason,
|
|
737
|
-
offer: c.
|
|
791
|
+
offer: c.offer?.kind,
|
|
792
|
+
...offerAt(c),
|
|
738
793
|
outcome: "declined"
|
|
739
794
|
});
|
|
740
|
-
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";
|
|
741
801
|
c.error = void 0;
|
|
742
802
|
emit();
|
|
743
803
|
},
|
|
@@ -805,6 +865,131 @@ var createFlows = (env) => {
|
|
|
805
865
|
return controller;
|
|
806
866
|
};
|
|
807
867
|
|
|
868
|
+
// src/paywall.ts
|
|
869
|
+
var PAYWALL_EVENT = "$paywall";
|
|
870
|
+
var paywallPages = (config, w, distinctId) => {
|
|
871
|
+
const e = w.experiment ? config?.experiments.find((x) => x.key === w.experiment) : void 0;
|
|
872
|
+
const id = e ? assign(e, distinctId) : w.variants[0]?.id;
|
|
873
|
+
const v = w.variants.find((x) => x.id === id) ?? w.variants[0];
|
|
874
|
+
return v ? { variant: v.id, pages: v.pages, test: e?.status === "running" && !e.winner ? e.key : void 0 } : null;
|
|
875
|
+
};
|
|
876
|
+
var createPaywallHost = (env) => {
|
|
877
|
+
let run = null;
|
|
878
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
879
|
+
const emit = () => {
|
|
880
|
+
for (const l of [...listeners]) {
|
|
881
|
+
try {
|
|
882
|
+
l();
|
|
883
|
+
} catch {
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
const base = () => run ? { paywall: run.w.id, placement: run.w.placement, variant: run.variant } : {};
|
|
888
|
+
const show = () => {
|
|
889
|
+
if (run && !run.seen.has(run.index)) {
|
|
890
|
+
run.seen.add(run.index);
|
|
891
|
+
env.track(PAYWALL_EVENT, { ...base(), page: run.pages[run.index].id, index: run.index, action: "page" });
|
|
892
|
+
}
|
|
893
|
+
emit();
|
|
894
|
+
};
|
|
895
|
+
const end = (r) => {
|
|
896
|
+
const c = run;
|
|
897
|
+
if (!c) return;
|
|
898
|
+
run = null;
|
|
899
|
+
if (r.outcome === "closed") c.options.onClose?.(r);
|
|
900
|
+
c.resolve(r);
|
|
901
|
+
emit();
|
|
902
|
+
};
|
|
903
|
+
const result = (outcome) => ({
|
|
904
|
+
outcome,
|
|
905
|
+
index: run?.index ?? 0,
|
|
906
|
+
...run ? { paywall: run.w.id, variant: run.variant } : {},
|
|
907
|
+
...outcome !== "closed" && run ? { plan: run.plans.find((p) => p.id === run.plan) } : {}
|
|
908
|
+
});
|
|
909
|
+
const buy = () => {
|
|
910
|
+
const c = run;
|
|
911
|
+
const plan = c?.plans.find((p) => p.id === c.plan);
|
|
912
|
+
if (!c || !plan || c.busy) return;
|
|
913
|
+
if (!c.options.onPurchase) return end(result("chosen"));
|
|
914
|
+
c.busy = true;
|
|
915
|
+
c.error = void 0;
|
|
916
|
+
emit();
|
|
917
|
+
Promise.resolve().then(() => c.options.onPurchase?.(plan)).then(
|
|
918
|
+
(ok) => {
|
|
919
|
+
c.busy = false;
|
|
920
|
+
if (run !== c) return;
|
|
921
|
+
if (ok === false) return emit();
|
|
922
|
+
env.track(PAYWALL_EVENT, { ...base(), page: c.pages[c.index].id, index: c.index, plan: plan.id, action: "purchased" });
|
|
923
|
+
end(result("purchased"));
|
|
924
|
+
},
|
|
925
|
+
() => {
|
|
926
|
+
c.busy = false;
|
|
927
|
+
c.error = "The purchase did not go through. Please try again.";
|
|
928
|
+
emit();
|
|
929
|
+
}
|
|
930
|
+
);
|
|
931
|
+
};
|
|
932
|
+
return {
|
|
933
|
+
open: (placement, options = {}) => {
|
|
934
|
+
if (run) return run.promise;
|
|
935
|
+
const config = env.config();
|
|
936
|
+
const w = config?.paywalls?.find((x) => x.placement === placement);
|
|
937
|
+
const picked = w ? paywallPages(config, w, env.distinctId()) : null;
|
|
938
|
+
if (!w || !picked?.pages.length) return Promise.resolve({ outcome: "closed", index: 0 });
|
|
939
|
+
const own = new Map((options.plans ?? []).map((p) => [p.id, p]));
|
|
940
|
+
const plans = w.plans.map((p) => own.get(p.id) ?? p);
|
|
941
|
+
let resolve = () => {
|
|
942
|
+
};
|
|
943
|
+
const promise = new Promise((r) => resolve = r);
|
|
944
|
+
const plan = plans.some((p) => p.id === w.highlight) ? w.highlight : plans[0]?.id;
|
|
945
|
+
run = { w, variant: picked.variant, pages: picked.pages, plans, index: 0, plan, busy: false, seen: /* @__PURE__ */ new Set(), options, resolve, promise };
|
|
946
|
+
if (picked.test) env.track("$exposure", { experiment: picked.test, variant: picked.variant });
|
|
947
|
+
show();
|
|
948
|
+
return promise;
|
|
949
|
+
},
|
|
950
|
+
view: () => run ? {
|
|
951
|
+
paywall: run.w,
|
|
952
|
+
variant: run.variant,
|
|
953
|
+
page: run.pages[run.index],
|
|
954
|
+
index: run.index,
|
|
955
|
+
total: run.pages.length,
|
|
956
|
+
last: run.index === run.pages.length - 1,
|
|
957
|
+
plans: run.plans,
|
|
958
|
+
plan: run.plan,
|
|
959
|
+
busy: run.busy,
|
|
960
|
+
error: run.error
|
|
961
|
+
} : null,
|
|
962
|
+
subscribe: (l) => {
|
|
963
|
+
listeners.add(l);
|
|
964
|
+
return () => listeners.delete(l);
|
|
965
|
+
},
|
|
966
|
+
next: () => {
|
|
967
|
+
if (!run || run.busy) return;
|
|
968
|
+
if (run.pages[run.index].kind === "plans" || run.index === run.pages.length - 1) return buy();
|
|
969
|
+
run.index++;
|
|
970
|
+
run.error = void 0;
|
|
971
|
+
show();
|
|
972
|
+
},
|
|
973
|
+
back: () => {
|
|
974
|
+
if (!run || run.busy || !run.index) return;
|
|
975
|
+
run.index--;
|
|
976
|
+
run.error = void 0;
|
|
977
|
+
emit();
|
|
978
|
+
},
|
|
979
|
+
pick: (id) => {
|
|
980
|
+
if (!run || run.busy || !run.plans.some((p) => p.id === id)) return;
|
|
981
|
+
run.plan = id;
|
|
982
|
+
run.error = void 0;
|
|
983
|
+
emit();
|
|
984
|
+
},
|
|
985
|
+
close: () => {
|
|
986
|
+
if (!run || run.busy) return;
|
|
987
|
+
env.track(PAYWALL_EVENT, { ...base(), index: run.index, action: "closed" });
|
|
988
|
+
end(result("closed"));
|
|
989
|
+
}
|
|
990
|
+
};
|
|
991
|
+
};
|
|
992
|
+
|
|
808
993
|
// src/rage.ts
|
|
809
994
|
var RageDetector = class {
|
|
810
995
|
constructor(windowMs = 1e3, radius = 30, count = 3) {
|
|
@@ -1072,6 +1257,7 @@ var createClient = (options, deps) => {
|
|
|
1072
1257
|
const errorQueue = [];
|
|
1073
1258
|
const errorsSeen = /* @__PURE__ */ new Set();
|
|
1074
1259
|
let errorsSent = 0;
|
|
1260
|
+
const paywall = createPaywallHost({ config: () => config, distinctId: () => distinctId(), track: (event, props) => record(event, props) });
|
|
1075
1261
|
const flows = createFlows({
|
|
1076
1262
|
track: (event, props) => record(event, props),
|
|
1077
1263
|
personId: () => consent !== "off" ? identified : void 0,
|
|
@@ -1632,6 +1818,8 @@ var createClient = (options, deps) => {
|
|
|
1632
1818
|
},
|
|
1633
1819
|
openCancelFlow: (o) => flows.openCancelFlow(o),
|
|
1634
1820
|
startOnboarding: (id) => flows.startOnboarding(id),
|
|
1821
|
+
openPaywall: (placement, o) => paywall.open(placement, o),
|
|
1822
|
+
paywall,
|
|
1635
1823
|
registerForPush,
|
|
1636
1824
|
unregisterForPush,
|
|
1637
1825
|
flows,
|
|
@@ -1697,19 +1885,21 @@ var RN = require("react-native");
|
|
|
1697
1885
|
var LIGHT = { bg: "#ffffff", fg: "#1c1d20", muted: "#5d6068", soft: "#f4f5f7", line: "#e4e5e8" };
|
|
1698
1886
|
var DARK = { bg: "#17181b", fg: "#f1f2f4", muted: "#a9acb3", soft: "#232428", line: "#2e2f35" };
|
|
1699
1887
|
var months = (n) => `${n} ${n === 1 ? "month" : "months"}`;
|
|
1700
|
-
var MRRocketFlows = ({ flows, appearance = {} }) => {
|
|
1888
|
+
var MRRocketFlows = ({ flows, paywall, appearance = {} }) => {
|
|
1701
1889
|
const scheme = RN.useColorScheme?.();
|
|
1702
1890
|
const dark = appearance.theme === "dark" || (appearance.theme ?? "auto") === "auto" && scheme === "dark";
|
|
1703
1891
|
const p = { ...dark ? DARK : LIGHT, accent: appearance.accent ?? "#3f76ff", danger: "#d92d20" };
|
|
1704
1892
|
const [, setTick] = (0, import_react.useState)(0);
|
|
1705
1893
|
(0, import_react.useEffect)(() => flows.subscribe(() => setTick((t) => t + 1)), [flows]);
|
|
1894
|
+
(0, import_react.useEffect)(() => paywall?.subscribe(() => setTick((t) => t + 1)), [paywall]);
|
|
1895
|
+
const pw = paywall?.view() ?? null;
|
|
1706
1896
|
const v = flows.view();
|
|
1707
1897
|
const [comment, setComment] = (0, import_react.useState)("");
|
|
1708
1898
|
const screen = v?.type === "cancel" ? v.screen : null;
|
|
1709
1899
|
(0, import_react.useEffect)(() => {
|
|
1710
1900
|
if (!screen) setComment("");
|
|
1711
1901
|
}, [screen]);
|
|
1712
|
-
if (!v) return null;
|
|
1902
|
+
if (!v && !pw) return null;
|
|
1713
1903
|
const text2 = (children, style = {}) => (0, import_react.createElement)(RN.Text, { style: { color: p.fg, fontSize: 15, ...style } }, children);
|
|
1714
1904
|
const muted = (children, style = {}) => text2(children, { color: p.muted, fontSize: 13, ...style });
|
|
1715
1905
|
const title = (children) => (0, import_react.createElement)(RN.Text, { accessibilityRole: "header", style: { color: p.fg, fontSize: 18, fontWeight: "700" } }, children);
|
|
@@ -1768,9 +1958,9 @@ var MRRocketFlows = ({ flows, appearance = {} }) => {
|
|
|
1768
1958
|
card(children, { borderBottomLeftRadius: 0, borderBottomRightRadius: 0, paddingBottom: 34 })
|
|
1769
1959
|
)
|
|
1770
1960
|
);
|
|
1771
|
-
const input = (placeholder) => (0, import_react.createElement)(RN.TextInput, {
|
|
1772
|
-
value
|
|
1773
|
-
onChangeText:
|
|
1961
|
+
const input = (placeholder, value = comment, onChange = setComment) => (0, import_react.createElement)(RN.TextInput, {
|
|
1962
|
+
value,
|
|
1963
|
+
onChangeText: onChange,
|
|
1774
1964
|
placeholder,
|
|
1775
1965
|
placeholderTextColor: p.muted,
|
|
1776
1966
|
multiline: true,
|
|
@@ -1778,6 +1968,71 @@ var MRRocketFlows = ({ flows, appearance = {} }) => {
|
|
|
1778
1968
|
accessibilityLabel: placeholder,
|
|
1779
1969
|
style: { minHeight: 72, borderWidth: 1, borderColor: p.line, borderRadius: 10, padding: 10, color: p.fg, textAlignVertical: "top" }
|
|
1780
1970
|
});
|
|
1971
|
+
const paywallSheet = (w) => {
|
|
1972
|
+
const pg = w.page;
|
|
1973
|
+
const host = paywall;
|
|
1974
|
+
return sheet(
|
|
1975
|
+
[
|
|
1976
|
+
(0, import_react.createElement)(
|
|
1977
|
+
RN.View,
|
|
1978
|
+
{ key: "top", style: { flexDirection: "row", alignItems: "center", gap: 4 } },
|
|
1979
|
+
...w.total > 1 ? Array.from(
|
|
1980
|
+
{ length: w.total },
|
|
1981
|
+
(_, i) => (0, import_react.createElement)(RN.View, { key: i, style: { width: 16, height: 4, borderRadius: 4, backgroundColor: i <= w.index ? p.accent : p.line } })
|
|
1982
|
+
) : [],
|
|
1983
|
+
close("Close", host.close)
|
|
1984
|
+
),
|
|
1985
|
+
title(pg.title),
|
|
1986
|
+
pg.body ? muted(pg.body, { fontSize: 15 }) : null,
|
|
1987
|
+
...pg.kind === "benefits" ? (pg.items ?? []).map(
|
|
1988
|
+
(item, i) => (0, import_react.createElement)(
|
|
1989
|
+
RN.View,
|
|
1990
|
+
{ key: `b${i}`, style: { flexDirection: "row", gap: 10, alignItems: "flex-start" } },
|
|
1991
|
+
text2("\u2713", { color: p.accent, fontWeight: "700" }),
|
|
1992
|
+
text2(item, { flex: 1 })
|
|
1993
|
+
)
|
|
1994
|
+
) : [],
|
|
1995
|
+
...pg.kind === "proof" ? (pg.quotes ?? []).map(
|
|
1996
|
+
(q, i) => (0, import_react.createElement)(RN.View, { key: `q${i}`, style: { backgroundColor: p.soft, borderRadius: 12, padding: 12, gap: 6 } }, text2(q.text), q.by ? muted(q.by) : null)
|
|
1997
|
+
) : [],
|
|
1998
|
+
pg.kind === "plans" ? (0, import_react.createElement)(
|
|
1999
|
+
RN.View,
|
|
2000
|
+
{ key: "plans", accessibilityRole: "radiogroup", style: { gap: 6 } },
|
|
2001
|
+
...w.plans.map(
|
|
2002
|
+
(plan) => (0, import_react.createElement)(
|
|
2003
|
+
RN.Pressable,
|
|
2004
|
+
{
|
|
2005
|
+
key: plan.id,
|
|
2006
|
+
onPress: () => host.pick(plan.id),
|
|
2007
|
+
accessibilityRole: "radio",
|
|
2008
|
+
accessibilityLabel: `${plan.name}, ${plan.price}`,
|
|
2009
|
+
accessibilityState: { checked: w.plan === plan.id },
|
|
2010
|
+
style: {
|
|
2011
|
+
minHeight: 52,
|
|
2012
|
+
paddingHorizontal: 12,
|
|
2013
|
+
borderRadius: 10,
|
|
2014
|
+
borderWidth: 1,
|
|
2015
|
+
borderColor: w.plan === plan.id ? p.accent : p.line,
|
|
2016
|
+
backgroundColor: w.plan === plan.id ? p.soft : p.bg,
|
|
2017
|
+
flexDirection: "row",
|
|
2018
|
+
alignItems: "center",
|
|
2019
|
+
gap: 10
|
|
2020
|
+
}
|
|
2021
|
+
},
|
|
2022
|
+
(0, import_react.createElement)(RN.View, { style: { flex: 1 } }, text2(plan.name, { fontWeight: "600" }), plan.note ? muted(plan.note) : null),
|
|
2023
|
+
text2(plan.price, { fontWeight: "700" })
|
|
2024
|
+
)
|
|
2025
|
+
)
|
|
2026
|
+
) : null,
|
|
2027
|
+
w.error ? text2(w.error, { color: p.danger, fontSize: 13 }) : null,
|
|
2028
|
+
btn(w.busy ? "One moment\u2026" : pg.button || "Continue", "primary", host.next, w.busy || pg.kind === "plans" && !w.plan),
|
|
2029
|
+
w.index ? btn("Back", "quiet", host.back, w.busy) : null
|
|
2030
|
+
],
|
|
2031
|
+
host.close
|
|
2032
|
+
);
|
|
2033
|
+
};
|
|
2034
|
+
if (pw) return paywallSheet(pw);
|
|
2035
|
+
if (!v) return null;
|
|
1781
2036
|
if (v.type === "cancel") {
|
|
1782
2037
|
const top = (0, import_react.createElement)(
|
|
1783
2038
|
RN.View,
|
|
@@ -1790,38 +2045,49 @@ var MRRocketFlows = ({ flows, appearance = {} }) => {
|
|
|
1790
2045
|
);
|
|
1791
2046
|
const error = v.error ? text2(v.error, { color: p.danger, fontSize: 13 }) : null;
|
|
1792
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
|
+
);
|
|
1793
2073
|
if (v.screen === "survey")
|
|
1794
2074
|
body = [
|
|
1795
2075
|
title(v.flow.question || "Why are you leaving?"),
|
|
1796
|
-
(
|
|
1797
|
-
RN.ScrollView,
|
|
1798
|
-
{ style: { maxHeight: 320 }, contentContainerStyle: { gap: 6 }, accessibilityRole: "radiogroup" },
|
|
1799
|
-
...v.flow.reasons.map(
|
|
1800
|
-
(r) => (0, import_react.createElement)(
|
|
1801
|
-
RN.Pressable,
|
|
1802
|
-
{
|
|
1803
|
-
key: r.id,
|
|
1804
|
-
onPress: () => flows.cancel.pick(r.id),
|
|
1805
|
-
accessibilityRole: "radio",
|
|
1806
|
-
accessibilityState: { checked: v.reason === r.id },
|
|
1807
|
-
style: {
|
|
1808
|
-
minHeight: 44,
|
|
1809
|
-
paddingHorizontal: 12,
|
|
1810
|
-
borderRadius: 10,
|
|
1811
|
-
borderWidth: 1,
|
|
1812
|
-
borderColor: v.reason === r.id ? p.accent : p.line,
|
|
1813
|
-
backgroundColor: v.reason === r.id ? p.soft : p.bg,
|
|
1814
|
-
justifyContent: "center"
|
|
1815
|
-
}
|
|
1816
|
-
},
|
|
1817
|
-
text2(r.label, { fontWeight: "500" })
|
|
1818
|
-
)
|
|
1819
|
-
)
|
|
1820
|
-
),
|
|
2076
|
+
choices(v.flow.reasons.map((r) => ({ key: r.id, label: r.label, on: v.reason === r.id, press: () => flows.cancel.pick(r.id) }))),
|
|
1821
2077
|
btn("Continue", "primary", flows.cancel.next, !v.reason),
|
|
1822
2078
|
btn("Never mind, keep my plan", "quiet", flows.cancel.close)
|
|
1823
2079
|
];
|
|
1824
|
-
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) {
|
|
1825
2091
|
const o = v.offer;
|
|
1826
2092
|
const label = v.flow.reasons.find((r) => r.id === v.reason)?.label;
|
|
1827
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;
|
|
@@ -2497,6 +2763,13 @@ var buzz = () => {
|
|
|
2497
2763
|
} catch {
|
|
2498
2764
|
}
|
|
2499
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" };
|
|
2500
2773
|
var teamOf = (t) => (t?.messages ?? []).filter((m) => m.from !== "visitor");
|
|
2501
2774
|
var svg = null;
|
|
2502
2775
|
try {
|
|
@@ -2512,9 +2785,10 @@ var GLYPH = {
|
|
|
2512
2785
|
up: "M8 13V3M3.5 7.5 8 3l4.5 4.5",
|
|
2513
2786
|
next: "M6 3.5 10.5 8 6 12.5",
|
|
2514
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",
|
|
2515
|
-
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"
|
|
2516
2790
|
};
|
|
2517
|
-
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}" };
|
|
2518
2792
|
var glyph = (name, color, size = 20) => {
|
|
2519
2793
|
const Svg = svg?.Svg ?? svg?.default;
|
|
2520
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]);
|
|
@@ -2526,7 +2800,11 @@ var teamMark = (title, bg, fg, size = 36) => (0, import_react2.createElement)(
|
|
|
2526
2800
|
{ style: { width: size, height: size, borderRadius: size / 2, backgroundColor: bg, alignItems: "center", justifyContent: "center" } },
|
|
2527
2801
|
(0, import_react2.createElement)(RN3.Text, { style: { color: fg, fontSize: Math.round(size * 0.42), fontWeight: "700" } }, (title.trim()[0] ?? "?").toUpperCase())
|
|
2528
2802
|
);
|
|
2529
|
-
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 };
|
|
2530
2808
|
var useChat = (client, o, open, view) => {
|
|
2531
2809
|
const [thread, setThread] = (0, import_react2.useState)(null);
|
|
2532
2810
|
const [loaded, setLoaded] = (0, import_react2.useState)(false);
|
|
@@ -2693,12 +2971,14 @@ var MessengerSheet = ({
|
|
|
2693
2971
|
o,
|
|
2694
2972
|
chat,
|
|
2695
2973
|
view,
|
|
2696
|
-
setView
|
|
2974
|
+
setView,
|
|
2975
|
+
brand
|
|
2697
2976
|
}) => {
|
|
2698
2977
|
const k = kit(p);
|
|
2699
2978
|
const [body, setBody] = (0, import_react2.useState)("");
|
|
2700
2979
|
const [email, setEmail] = (0, import_react2.useState)("");
|
|
2701
2980
|
const [pending, setPending] = (0, import_react2.useState)(null);
|
|
2981
|
+
const [photos, setPhotos] = (0, import_react2.useState)([]);
|
|
2702
2982
|
const [query, setQuery] = (0, import_react2.useState)("");
|
|
2703
2983
|
const helpOn = o.help !== false;
|
|
2704
2984
|
const arts = useArticles(client, helpOn, query);
|
|
@@ -2712,52 +2992,135 @@ var MessengerSheet = ({
|
|
|
2712
2992
|
}, [count, view]);
|
|
2713
2993
|
const needEmail = (o.askEmail ?? true) && !thread?.hasEmail && !client.getUser()?.email;
|
|
2714
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;
|
|
2715
3036
|
const send = async () => {
|
|
2716
3037
|
const text2 = body.trim();
|
|
2717
3038
|
const api = client.api();
|
|
2718
|
-
|
|
2719
|
-
|
|
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
|
+
});
|
|
2720
3048
|
setBody("");
|
|
3049
|
+
setPhotos([]);
|
|
2721
3050
|
chat.setError("");
|
|
2722
3051
|
try {
|
|
2723
|
-
|
|
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
|
+
);
|
|
2724
3065
|
} catch (e) {
|
|
2725
3066
|
setBody(text2);
|
|
3067
|
+
setPhotos(files.filter((f) => f.id));
|
|
2726
3068
|
chat.setError(e.message);
|
|
2727
3069
|
} finally {
|
|
2728
3070
|
setPending(null);
|
|
2729
3071
|
}
|
|
2730
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) ?? "";
|
|
2731
3076
|
const ink = inkOn(p.accent);
|
|
2732
3077
|
const dark = p.bg === DARK.bg;
|
|
2733
|
-
const
|
|
2734
|
-
const
|
|
2735
|
-
const
|
|
2736
|
-
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() ?? "";
|
|
2737
3086
|
const bubble = (m, isPending = false) => {
|
|
2738
3087
|
const mine = m.from === "visitor";
|
|
2739
3088
|
return (0, import_react2.createElement)(
|
|
2740
3089
|
RN3.View,
|
|
2741
3090
|
{ key: m.id, style: { alignSelf: mine ? "flex-end" : "flex-start", maxWidth: "82%", gap: 4 } },
|
|
2742
|
-
(
|
|
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)(
|
|
2743
3105
|
RN3.View,
|
|
2744
3106
|
{
|
|
2745
3107
|
style: {
|
|
2746
|
-
backgroundColor: mine ? p.accent :
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
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,
|
|
2750
3113
|
paddingHorizontal: 14,
|
|
2751
3114
|
paddingVertical: 9,
|
|
2752
3115
|
opacity: isPending ? 0.7 : 1
|
|
2753
3116
|
}
|
|
2754
3117
|
},
|
|
2755
|
-
k.text(m.body, { color: mine ? ink :
|
|
3118
|
+
k.text(m.body, { color: mine ? ink : hu.fg })
|
|
2756
3119
|
),
|
|
2757
3120
|
...(m.articles ?? []).map(
|
|
2758
3121
|
(a) => (0, import_react2.createElement)(
|
|
2759
3122
|
RN3.Pressable,
|
|
2760
|
-
{ 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 } },
|
|
2761
3124
|
k.text(`\u{1F4C4} ${a.title}`, { fontSize: 13 })
|
|
2762
3125
|
)
|
|
2763
3126
|
),
|
|
@@ -2770,60 +3133,80 @@ var MessengerSheet = ({
|
|
|
2770
3133
|
k.text(a.title, { flex: 1 }),
|
|
2771
3134
|
glyph("next", p.accent, 14)
|
|
2772
3135
|
);
|
|
2773
|
-
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);
|
|
2774
3137
|
const searchRow = (onPress) => (0, import_react2.createElement)(
|
|
2775
3138
|
RN3.Pressable,
|
|
2776
|
-
{ 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 } },
|
|
2777
3140
|
k.muted("Search for help", { flex: 1, fontSize: 15 }),
|
|
2778
3141
|
glyph("search", p.accent, 16)
|
|
2779
3142
|
);
|
|
2780
|
-
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
|
+
);
|
|
2781
3153
|
const last = thread?.messages.at(-1);
|
|
2782
3154
|
const showHelp = helpOn && arts.any !== false;
|
|
2783
3155
|
let content;
|
|
2784
|
-
if (view === "home")
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
{ key: "home", style: { flex: 1, backgroundColor: canvas }, contentContainerStyle: { paddingBottom: 24 }, keyboardShouldPersistTaps: "handled" },
|
|
2788
|
-
(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.
|
|
2789
3159
|
(0, import_react2.createElement)(
|
|
2790
3160
|
RN3.View,
|
|
2791
|
-
{ style: {
|
|
2792
|
-
|
|
2793
|
-
RN3.Pressable,
|
|
2794
|
-
{ key: "recent", onPress: () => setView("messages"), accessibilityRole: "button" },
|
|
2795
|
-
card([
|
|
2796
|
-
k.muted("Recent message", { fontSize: 12, fontWeight: "600", marginBottom: 10 }),
|
|
2797
|
-
(0, import_react2.createElement)(
|
|
2798
|
-
RN3.View,
|
|
2799
|
-
{ key: "r", style: { flexDirection: "row", alignItems: "center", gap: 12 } },
|
|
2800
|
-
teamMark(title, p.accent, ink),
|
|
2801
|
-
(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 })),
|
|
2802
|
-
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)
|
|
2803
|
-
)
|
|
2804
|
-
])
|
|
2805
|
-
) : 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" } }),
|
|
2806
3163
|
(0, import_react2.createElement)(
|
|
2807
3164
|
RN3.Pressable,
|
|
2808
|
-
{
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
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)
|
|
2815
3187
|
)
|
|
2816
|
-
)
|
|
2817
|
-
|
|
2818
|
-
)
|
|
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)
|
|
2819
3202
|
);
|
|
2820
|
-
else if (view === "messages")
|
|
3203
|
+
} else if (view === "messages")
|
|
2821
3204
|
content = (0, import_react2.createElement)(
|
|
2822
3205
|
RN3.View,
|
|
2823
|
-
{ key: "messages", style: { flex: 1, backgroundColor:
|
|
3206
|
+
{ key: "messages", style: { flex: 1, backgroundColor: hu.bg } },
|
|
2824
3207
|
(0, import_react2.createElement)(
|
|
2825
3208
|
RN3.ScrollView,
|
|
2826
|
-
{ ref: scroll, style: { flex: 1 }, contentContainerStyle: {
|
|
3209
|
+
{ ref: scroll, style: { flex: 1 }, contentContainerStyle: { paddingHorizontal: 16, paddingTop: 4, paddingBottom: 16, gap: 12 } },
|
|
2827
3210
|
bubble({ id: "greeting", from: "team", body: greeting, at: "" }),
|
|
2828
3211
|
!chat.loaded ? k.muted("Loading\u2026") : null,
|
|
2829
3212
|
...(thread?.messages ?? []).map((m) => bubble(m)),
|
|
@@ -2832,23 +3215,57 @@ var MessengerSheet = ({
|
|
|
2832
3215
|
),
|
|
2833
3216
|
(0, import_react2.createElement)(
|
|
2834
3217
|
RN3.View,
|
|
2835
|
-
{ style: { paddingHorizontal: 12, paddingTop: 8, paddingBottom:
|
|
3218
|
+
{ style: { paddingHorizontal: 12, paddingTop: 8, paddingBottom: 6, gap: 8 } },
|
|
2836
3219
|
chat.error ? k.text(chat.error, { color: p.danger, fontSize: 12 }) : null,
|
|
2837
|
-
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.
|
|
2838
3245
|
(0, import_react2.createElement)(
|
|
2839
3246
|
RN3.View,
|
|
2840
|
-
{ style: {
|
|
2841
|
-
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" } }),
|
|
2842
3249
|
(0, import_react2.createElement)(
|
|
2843
|
-
RN3.
|
|
2844
|
-
{
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
accessibilityRole: "button",
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
},
|
|
2851
|
-
|
|
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
|
+
)
|
|
2852
3269
|
)
|
|
2853
3270
|
)
|
|
2854
3271
|
)
|
|
@@ -2858,7 +3275,7 @@ var MessengerSheet = ({
|
|
|
2858
3275
|
for (const a of arts.list ?? []) groups.set(a.collection, [...groups.get(a.collection) ?? [], a]);
|
|
2859
3276
|
content = (0, import_react2.createElement)(
|
|
2860
3277
|
RN3.ScrollView,
|
|
2861
|
-
{ 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" },
|
|
2862
3279
|
search,
|
|
2863
3280
|
arts.list === null ? k.muted("Loading\u2026") : null,
|
|
2864
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,
|
|
@@ -2867,46 +3284,73 @@ var MessengerSheet = ({
|
|
|
2867
3284
|
} else
|
|
2868
3285
|
content = (0, import_react2.createElement)(
|
|
2869
3286
|
RN3.ScrollView,
|
|
2870
|
-
{ key: `a-${view.article}`, style: { flex: 1 }, contentContainerStyle: {
|
|
3287
|
+
{ key: `a-${view.article}`, style: { flex: 1 }, contentContainerStyle: { paddingHorizontal: 16, paddingTop: 4, paddingBottom: 32 } },
|
|
2871
3288
|
(0, import_react2.createElement)(ArticleView, { client, p, id: view.article, onAsk: () => setView("messages") })
|
|
2872
3289
|
);
|
|
2873
3290
|
const current = typeof view === "string" ? view : "help";
|
|
2874
|
-
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)(
|
|
2875
3310
|
RN3.Pressable,
|
|
2876
|
-
{ key:
|
|
2877
|
-
glyph(
|
|
2878
|
-
k.text(name, { fontSize: 12, fontWeight: "600", color: current === v ? p.accent : p.muted }),
|
|
2879
|
-
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)
|
|
2880
3313
|
);
|
|
2881
|
-
const onHome = view === "home";
|
|
2882
|
-
const headFg = onHome ? ink : p.fg;
|
|
2883
3314
|
const head = (0, import_react2.createElement)(
|
|
2884
3315
|
RN3.View,
|
|
2885
|
-
{ key: "head", style: { flexDirection: "row", alignItems: "center", gap:
|
|
2886
|
-
typeof view !== "string" ? (
|
|
2887
|
-
|
|
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)(
|
|
2888
3320
|
RN3.View,
|
|
2889
|
-
{ style: { flex: 1,
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
),
|
|
2893
|
-
(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))
|
|
2894
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;
|
|
2895
3337
|
return (0, import_react2.createElement)(
|
|
2896
3338
|
RN3.Modal,
|
|
2897
3339
|
{ visible: true, animationType: "slide", presentationStyle: "pageSheet", onRequestClose: close },
|
|
2898
3340
|
(0, import_react2.createElement)(
|
|
2899
3341
|
RN3.KeyboardAvoidingView,
|
|
2900
|
-
{ 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 } },
|
|
2901
3343
|
head,
|
|
2902
3344
|
content,
|
|
2903
|
-
(0, import_react2.createElement)(
|
|
3345
|
+
view === "messages" ? null : (0, import_react2.createElement)(
|
|
2904
3346
|
RN3.View,
|
|
2905
|
-
{ 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 } },
|
|
2906
3348
|
tab("home", "Home", "home"),
|
|
2907
|
-
tab("messages", "Messages", "chat",
|
|
3349
|
+
tab("messages", "Messages", "chat", chat.unseen.length),
|
|
2908
3350
|
showHelp ? tab("help", "Help", "help") : null
|
|
2909
|
-
)
|
|
3351
|
+
),
|
|
3352
|
+
powered,
|
|
3353
|
+
(0, import_react2.createElement)(RN3.View, { style: { height: 16 } })
|
|
2910
3354
|
)
|
|
2911
3355
|
);
|
|
2912
3356
|
};
|
|
@@ -2931,6 +3375,9 @@ var MRRocketMessenger = ({ client, appearance, options: given = {} }) => {
|
|
|
2931
3375
|
preview: look.preview,
|
|
2932
3376
|
askEmail: look.askEmail,
|
|
2933
3377
|
haptics: look.sound,
|
|
3378
|
+
corners: look.corners,
|
|
3379
|
+
header: look.header,
|
|
3380
|
+
logo: look.logo,
|
|
2934
3381
|
...defined
|
|
2935
3382
|
} : given;
|
|
2936
3383
|
const p = usePalette({ theme: appearance?.theme ?? look?.theme, accent: appearance?.accent ?? look?.accent });
|
|
@@ -2959,37 +3406,42 @@ var MRRocketMessenger = ({ client, appearance, options: given = {} }) => {
|
|
|
2959
3406
|
const unseen = chat.unseen.length;
|
|
2960
3407
|
const ink = inkOn(p.accent);
|
|
2961
3408
|
const label = options.label?.trim() ?? "";
|
|
2962
|
-
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
|
+
};
|
|
2963
3415
|
const launcher = options.launcher ? (0, import_react2.createElement)(
|
|
2964
|
-
RN3.
|
|
2965
|
-
{
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
}
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
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
|
+
)
|
|
2993
3445
|
) : null;
|
|
2994
3446
|
const toast = banner ? (0, import_react2.createElement)(
|
|
2995
3447
|
RN3.Pressable,
|
|
@@ -2998,15 +3450,25 @@ var MRRocketMessenger = ({ client, appearance, options: given = {} }) => {
|
|
|
2998
3450
|
onPress: () => openAt("messages"),
|
|
2999
3451
|
accessibilityRole: "button",
|
|
3000
3452
|
accessibilityLiveRegion: "polite",
|
|
3001
|
-
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
|
+
}
|
|
3002
3469
|
},
|
|
3003
|
-
|
|
3004
|
-
(0, import_react2.createElement)(
|
|
3005
|
-
RN3.View,
|
|
3006
|
-
{ style: { flex: 1, gap: 2 } },
|
|
3007
|
-
k.muted(`${whoLabel[banner.from]}${unseen > 1 ? ` \xB7 ${unseen} new` : ""}`, { fontWeight: "600", fontSize: 12 }),
|
|
3008
|
-
(0, import_react2.createElement)(RN3.Text, { numberOfLines: 3, style: { color: p.fg, fontSize: 15 } }, banner.body)
|
|
3009
|
-
)
|
|
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)
|
|
3010
3472
|
) : null;
|
|
3011
3473
|
if (!launcher && !toast && !open) return null;
|
|
3012
3474
|
return (0, import_react2.createElement)(
|
|
@@ -3014,7 +3476,7 @@ var MRRocketMessenger = ({ client, appearance, options: given = {} }) => {
|
|
|
3014
3476
|
{ pointerEvents: "box-none", style: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0 } },
|
|
3015
3477
|
launcher,
|
|
3016
3478
|
toast,
|
|
3017
|
-
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
|
|
3018
3480
|
);
|
|
3019
3481
|
};
|
|
3020
3482
|
|
|
@@ -3067,7 +3529,7 @@ var MRRocketProvider = ({ client: given, children, consent, flows = true, appear
|
|
|
3067
3529
|
Ctx.Provider,
|
|
3068
3530
|
{ value: client },
|
|
3069
3531
|
heatmaps ? (0, import_react3.createElement)(RN4.View, { style: { flex: 1 }, onTouchStartCapture: (e) => touch(e, false), onTouchEndCapture: (e) => touch(e, true) }, children) : children,
|
|
3070
|
-
flows ? (0, import_react3.createElement)(MRRocketFlows, { flows: client.flows, appearance }) : null,
|
|
3532
|
+
flows ? (0, import_react3.createElement)(MRRocketFlows, { flows: client.flows, paywall: client.paywall, appearance }) : null,
|
|
3071
3533
|
surveys ? (0, import_react3.createElement)(MRRocketSurveys, { client, appearance }) : null,
|
|
3072
3534
|
feedback ? (0, import_react3.createElement)(MRRocketFeedback, { client, appearance, options: feedback }) : null,
|
|
3073
3535
|
messenger ? (0, import_react3.createElement)(MRRocketMessenger, { client, appearance, options: messenger }) : null
|
|
@@ -3104,6 +3566,10 @@ var usePush = () => {
|
|
|
3104
3566
|
const client = useClient();
|
|
3105
3567
|
return { register: client.registerForPush, unregister: client.unregisterForPush };
|
|
3106
3568
|
};
|
|
3569
|
+
var usePaywall = () => {
|
|
3570
|
+
const client = useClient();
|
|
3571
|
+
return { open: client.openPaywall };
|
|
3572
|
+
};
|
|
3107
3573
|
var useOnboarding = () => {
|
|
3108
3574
|
const client = useClient();
|
|
3109
3575
|
return { start: client.startOnboarding };
|