@mrrocket/sdk 0.17.0 → 0.18.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +7 -1
  2. package/dist/browser/mrrocket-flows.js +9 -2
  3. package/dist/browser/mrrocket-messenger.js +3 -3
  4. package/dist/browser/mrrocket.js +1 -1
  5. package/dist/browser.d.ts +1 -1
  6. package/dist/browser.js +2 -1
  7. package/dist/browser.js.map +1 -1
  8. package/dist/client.d.ts +23 -3
  9. package/dist/client.js +15 -1
  10. package/dist/client.js.map +1 -1
  11. package/dist/context.d.ts +3 -1
  12. package/dist/context.js.map +1 -1
  13. package/dist/core.d.ts +3 -1
  14. package/dist/core.js +1 -1
  15. package/dist/core.js.map +1 -1
  16. package/dist/finish-web.d.ts +18 -0
  17. package/dist/finish-web.js +139 -0
  18. package/dist/finish-web.js.map +1 -0
  19. package/dist/flows-ui.d.ts +1 -10
  20. package/dist/flows-ui.js +105 -6
  21. package/dist/flows-ui.js.map +1 -1
  22. package/dist/flows-web.d.ts +8 -1
  23. package/dist/flows-web.js +8 -0
  24. package/dist/flows-web.js.map +1 -1
  25. package/dist/flows.d.ts +159 -7
  26. package/dist/flows.js +240 -28
  27. package/dist/flows.js.map +1 -1
  28. package/dist/icons.d.ts +24 -0
  29. package/dist/icons.js +61 -0
  30. package/dist/icons.js.map +1 -0
  31. package/dist/index.d.ts +4 -2
  32. package/dist/index.js +5 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/messenger-ui.js +78 -53
  35. package/dist/messenger-ui.js.map +1 -1
  36. package/dist/onboarding-state.d.ts +23 -0
  37. package/dist/onboarding-state.js +43 -0
  38. package/dist/onboarding-state.js.map +1 -0
  39. package/dist/react-native/index.d.cts +177 -15
  40. package/dist/react-native/index.d.ts +177 -15
  41. package/dist/react-native/index.js +776 -270
  42. package/dist/react-native/index.js.map +1 -1
  43. package/package.json +1 -1
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export {\n type ChangelogEntry,\n type ChatMessage,\n type ChatThread,\n type CockpitProviderProps,\n type CockpitUser,\n type FeedbackBoardInfo,\n type FeedbackBoardProps,\n type FeedbackComment,\n type FeedbackList,\n type FeedbackPost,\n type FeedbackRoadmap,\n type FeedbackWidgetProps,\n type MessengerProps,\n type PostStatus,\n type SurveyAnswer,\n type SurveyDefinition,\n type SurveyQuestion,\n type SurveyQuestionKind,\n CockpitProvider,\n Experiment,\n FeedbackBoard,\n FeedbackWidget,\n Messenger,\n Survey,\n Text,\n WIDGET_CSS,\n createApi,\n useCancelFlow,\n useCockpit,\n useFeedback,\n useFlag,\n useFlagVariant,\n useMessenger,\n usePaywall,\n usePush,\n useStyles,\n useVariant,\n} from \"./browser\";\nexport type { PushResult, PushStatus, WebPushOptions } from \"./push-web\";\n/** `identify(id, props, { hash })` (0.16.0); the hash comes from `userHash()` in `@mrrocket/sdk/next/server`. */\nexport type { IdentifyOptions } from \"./identity\";\nexport {\n type CockpitConfig,\n type CockpitEvent,\n type ConsentChoice,\n type ConsentMode,\n type ReplayConfig,\n type ErrorReport,\n type ExperimentConfig,\n type FlagCondition,\n type FlagConditionSet,\n type FlagConfig,\n type FlagPerson,\n type RedirectConfig,\n type RedirectDecision,\n type VariantConfig,\n type VariantId,\n EMBED_PARAM,\n EXPOSURE_PARAM,\n SDK_VERSION,\n ID_COOKIE,\n PREVIEW_PARAM,\n SESSION_COOKIE,\n assign,\n evaluateFlag,\n parsePreview,\n previewHref,\n redirectFor,\n} from \"./core\";\nexport {\n type CancelFlowConfig,\n type CancelFlowOptions,\n type CancelFlowResult,\n type CancelOffer,\n type InAppMessage,\n type OnboardingFlow,\n type OnboardingStep,\n type FlowMedia,\n type TourPlacement,\n videoEmbed,\n} from \"./flows\";\nexport type { FlowsTheme } from \"./flows-ui\";\nexport { PAYWALL_EVENT, type PaywallConfig, type PaywallOptions, type PaywallPage, type PaywallPlan, type PaywallResult } from \"./paywall\";\nexport {\n type AttributionMode,\n type CheckoutReferral,\n type Referral,\n REFERRAL_COOKIE,\n getReferral,\n readReferralCookie,\n referralForCheckout,\n referralLink,\n} from \"./referral\";\n"],"mappings":";AAAA;AAAA,EAmBI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AAIP;AAAA,EAgBI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AACP;AAAA,EAUI;AAAA,OACG;AAEP,SAAS,qBAAsH;AAC/H;AAAA,EAII;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export {\n type ChangelogEntry,\n type ChatMessage,\n type ChatThread,\n type CockpitProviderProps,\n type CockpitUser,\n type FeedbackBoardInfo,\n type FeedbackBoardProps,\n type FeedbackComment,\n type FeedbackList,\n type FeedbackPost,\n type FeedbackRoadmap,\n type FeedbackWidgetProps,\n type MessengerProps,\n type PostStatus,\n type SurveyAnswer,\n type SurveyDefinition,\n type SurveyQuestion,\n type SurveyQuestionKind,\n CockpitProvider,\n Experiment,\n FeedbackBoard,\n FeedbackWidget,\n Messenger,\n Survey,\n Text,\n WIDGET_CSS,\n createApi,\n useCancelFlow,\n useCockpit,\n useFeedback,\n useFlag,\n useFlagVariant,\n useMessenger,\n useOnboarding,\n usePaywall,\n usePush,\n useStyles,\n useVariant,\n} from \"./browser\";\nexport type { PushResult, PushStatus, WebPushOptions } from \"./push-web\";\n/** `identify(id, props, { hash })` (0.16.0); the hash comes from `userHash()` in `@mrrocket/sdk/next/server`. */\nexport type { IdentifyOptions } from \"./identity\";\nexport {\n type CockpitConfig,\n type CockpitEvent,\n type ConsentChoice,\n type ConsentMode,\n type ReplayConfig,\n type ErrorReport,\n type ExperimentConfig,\n type FlagCondition,\n type FlagConditionSet,\n type FlagConfig,\n type FlagPerson,\n type RedirectConfig,\n type RedirectDecision,\n type VariantConfig,\n type VariantId,\n EMBED_PARAM,\n EXPOSURE_PARAM,\n SDK_VERSION,\n ID_COOKIE,\n PREVIEW_PARAM,\n SESSION_COOKIE,\n assign,\n evaluateFlag,\n parsePreview,\n previewHref,\n redirectFor,\n} from \"./core\";\nexport {\n type CancelFlowConfig,\n type CancelFlowOptions,\n type CancelFlowResult,\n type CancelOffer,\n type InAppMessage,\n type OnboardingFlow,\n type OnboardingStep,\n type FlowMedia,\n type TourPlacement,\n type TaskOpens,\n type FlowIcon,\n type FinishAnimation,\n type OnboardingQuestion,\n type OnboardingItemState,\n videoEmbed,\n} from \"./flows\";\nexport type { UseOnboarding } from \"./onboarding-state\";\nexport { FLOW_ICONS, FLOW_ICON_NAMES, type BuiltinIcon } from \"./icons\";\nexport type { FlowsTheme } from \"./flows-ui\";\nexport { PAYWALL_EVENT, type PaywallConfig, type PaywallOptions, type PaywallPage, type PaywallPlan, type PaywallResult } from \"./paywall\";\nexport {\n type AttributionMode,\n type CheckoutReferral,\n type Referral,\n REFERRAL_COOKIE,\n getReferral,\n readReferralCookie,\n referralForCheckout,\n referralLink,\n} from \"./referral\";\n"],"mappings":";AAAA;AAAA,EAmBI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AAIP;AAAA,EAgBI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AACP;AAAA,EAeI;AAAA,OACG;AAEP,SAAS,YAAY,uBAAyC;AAE9D,SAAS,qBAAsH;AAC/H;AAAA,EAII;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;","names":[]}
@@ -13,6 +13,7 @@ var POLL_OPEN_MS = 4e3;
13
13
  var POLL_WAITING_MS = 8e3;
14
14
  var POLL_IDLE_MS = 3e4;
15
15
  var WAITING_FOR_MS = 10 * 6e4;
16
+ var SLOW_SEND_MS = 1500;
16
17
  var EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/;
17
18
  var SEEN_KEY = "ckw_msgr_seen";
18
19
  var readSeen = () => {
@@ -260,7 +261,10 @@ var build = (o) => {
260
261
  let thread = null;
261
262
  let loaded = false;
262
263
  let error = "";
263
- let pending = null;
264
+ const sayable = (e) => e.status === 401 ? "The chat is not available right now. Please try again later." : e.message;
265
+ let outbox = [];
266
+ let outboxSeq = 0;
267
+ let flushing = false;
264
268
  let lastKnown = null;
265
269
  let unseen = [];
266
270
  let previewGone = false;
@@ -644,7 +648,7 @@ var build = (o) => {
644
648
  const list = el("ol", { class: "ckw-msgs", "aria-live": "polite", "aria-label": "Messages" });
645
649
  const needEmail = () => askEmail && !o.user()?.email && !thread?.hasEmail;
646
650
  const emailOk = () => !email.value.trim() || EMAIL.test(email.value.trim());
647
- const canSend = () => (!!text.value.trim() || picked.length > 0) && !pending && emailOk();
651
+ const canSend = () => (!!text.value.trim() || picked.length > 0) && emailOk();
648
652
  const go = (v) => {
649
653
  const changed = JSON.stringify(v) !== JSON.stringify(view);
650
654
  view = v;
@@ -666,6 +670,7 @@ var build = (o) => {
666
670
  return img;
667
671
  };
668
672
  const bubble = (m, isPending = false) => {
673
+ const slow = isPending && Date.now() - Date.parse(m.at) >= SLOW_SEND_MS;
669
674
  const li = el("li", { class: "ckw-msg", "data-from": m.from === "visitor" ? "me" : "them" }, m.body || !m.attachments?.length ? el("div", { class: "ckw-bubble" }, m.body) : null);
670
675
  if (isPending) li.setAttribute("aria-busy", "true");
671
676
  if (m.attachments?.length)
@@ -683,7 +688,7 @@ var build = (o) => {
683
688
  chip.addEventListener("click", () => go({ article: a.id }));
684
689
  li.append(chip);
685
690
  }
686
- li.append(el("span", { class: "ckw-msg-meta" }, m.from === "ai" ? agentFace() : null, `${m.from !== "visitor" ? `${whoLabel[m.from]} \xB7 ` : ""}${isPending ? "Sending\u2026" : timeAgo(m.at)}`));
691
+ li.append(el("span", { class: "ckw-msg-meta" }, m.from === "ai" ? agentFace() : null, `${m.from !== "visitor" ? `${whoLabel[m.from]} \xB7 ` : ""}${slow ? "Sending\u2026" : timeAgo(m.at)}`));
687
692
  return li;
688
693
  };
689
694
  const articleRow = (a) => {
@@ -754,7 +759,7 @@ var build = (o) => {
754
759
  askIn.value = "";
755
760
  askGo.disabled = true;
756
761
  go("messages");
757
- if (canSend()) void submit();
762
+ if (canSend()) submit();
758
763
  });
759
764
  const drawHome = () => {
760
765
  const first = o.user()?.name?.trim().split(/\s+/)[0];
@@ -884,19 +889,19 @@ var build = (o) => {
884
889
  const drawMessages = () => {
885
890
  const msgs = thread?.messages ?? [];
886
891
  const parts = [];
887
- if (!loaded) parts.push(el("div", { class: "ckw-skel" }));
888
- else {
889
- const sig = `${msgs.map((m) => m.id).join(",")}|${pending ? `${pending.body}+${pending.attachments?.length ?? 0}` : ""}|${Math.floor(Date.now() / 6e4)}`;
892
+ {
893
+ const waiting = outbox.map((o2) => o2.message);
894
+ const sig = `${msgs.map((m) => m.id).join(",")}|${waiting.map((m) => `${m.id}:${Date.now() - Date.parse(m.at) >= SLOW_SEND_MS}`).join(",")}|${Math.floor(Date.now() / 6e4)}`;
890
895
  if (sig !== listSig) {
891
896
  listSig = sig;
892
- const greet = el("li", { class: "ckw-msg", "data-from": "them" }, el("div", { class: "ckw-bubble" }, greeting));
893
- const items = [greet, ...msgs.map((m) => bubble(m)), ...pending ? [bubble(pending, true)] : []];
897
+ const greet = msgs.length || waiting.length ? [] : [el("li", { class: "ckw-msg", "data-from": "them" }, el("div", { class: "ckw-bubble" }, greeting))];
898
+ const items = [...greet, ...msgs.map((m) => bubble(m)), ...waiting.map((m) => bubble(m, true))];
894
899
  if (listCount >= 0) items.slice(listCount).forEach((li) => li.setAttribute("data-new", ""));
895
900
  listCount = items.length;
896
901
  list.replaceChildren(...items);
897
902
  }
898
903
  parts.push(list);
899
- const top = !msgs.some((m) => m.from === "visitor") && !pending ? (articles ?? []).slice(0, 3) : [];
904
+ const top = !msgs.some((m) => m.from === "visitor") && !outbox.length ? (articles ?? []).slice(0, 3) : [];
900
905
  if (top.length) {
901
906
  if (suggestions.childNodes.length !== top.length)
902
907
  suggestions.replaceChildren(
@@ -909,9 +914,9 @@ var build = (o) => {
909
914
  parts.push(suggestions);
910
915
  }
911
916
  }
912
- if (thread?.conversation?.status === "solved" && !pending) parts.push(el("p", { class: "ckw-fine ckw-msgr-note" }, "This conversation is closed. Write to start a new one."));
917
+ if (thread?.conversation?.status === "solved" && !outbox.length) parts.push(el("p", { class: "ckw-fine ckw-msgr-note" }, "This conversation is closed. Write to start a new one."));
913
918
  if (body.childNodes.length !== parts.length || parts.some((x, i) => body.childNodes[i] !== x)) body.replaceChildren(...parts);
914
- const count = msgs.length + (pending ? 1 : 0);
919
+ const count = msgs.length + outbox.length;
915
920
  if (count !== drawn) {
916
921
  drawn = count;
917
922
  body.scrollTop = body.scrollHeight;
@@ -942,15 +947,18 @@ var build = (o) => {
942
947
  if (pop.childNodes.length !== kids.length || kids.some((k, i) => pop.childNodes[i] !== k)) pop.replaceChildren(...kids);
943
948
  };
944
949
  const tick = async () => {
945
- ctrl?.abort();
946
- ctrl = new AbortController();
950
+ if (ctrl) return;
951
+ const mine = ctrl = new AbortController();
947
952
  try {
948
- take(await api().chat(ctrl.signal));
953
+ take(await api().chat(mine.signal));
949
954
  error = "";
950
955
  } catch (e) {
951
956
  if (e.name === "AbortError") return;
952
- if (open && view === "messages") error = e.message;
957
+ if (open && view === "messages") error = sayable(e);
958
+ } finally {
959
+ if (ctrl === mine) ctrl = null;
953
960
  }
961
+ if (!loaded) listCount = -1;
954
962
  loaded = true;
955
963
  draw();
956
964
  drawFab();
@@ -969,58 +977,75 @@ var build = (o) => {
969
977
  else clearTimeout(poll);
970
978
  };
971
979
  document.addEventListener("visibilitychange", onVisible);
972
- const submit = async () => {
980
+ const flush = async () => {
981
+ if (flushing) return;
982
+ flushing = true;
983
+ while (outbox.length) {
984
+ const { message, files, email: given } = outbox[0];
985
+ await Promise.all(files.map((p) => p.up));
986
+ const ids = files.filter((p) => p.id).map((p) => p.id);
987
+ try {
988
+ if (!message.body && !ids.length) throw new Error(error || "The file could not be sent.");
989
+ const sent = await api().sendChat({
990
+ body: message.body,
991
+ email: given,
992
+ url: window.location.href.slice(0, 500),
993
+ ...ids.length ? { attachments: ids } : {},
994
+ // A little context for a bug report; the server reads browser and system from the user agent.
995
+ context: { viewport: `${window.innerWidth}\xD7${window.innerHeight}` }
996
+ });
997
+ ctrl?.abort();
998
+ ctrl = null;
999
+ take(sent);
1000
+ outbox.shift();
1001
+ for (const p of files) {
1002
+ localUrls.delete(p.key);
1003
+ URL.revokeObjectURL(p.local);
1004
+ }
1005
+ } catch (e) {
1006
+ const back = outbox.splice(0);
1007
+ text.value = [...back.map((b) => b.message.body), text.value.trim()].filter(Boolean).join("\n");
1008
+ picked = [...back.flatMap((b) => b.files.filter((p) => p.id)), ...picked];
1009
+ for (const b of back) for (const p of b.files) localUrls.delete(p.key);
1010
+ error = sayable(e);
1011
+ }
1012
+ draw();
1013
+ }
1014
+ flushing = false;
1015
+ schedule();
1016
+ };
1017
+ const submit = () => {
973
1018
  const msg = text.value.trim();
974
1019
  const files = picked;
975
- if (!msg && !files.length || pending || !emailOk()) return;
1020
+ if (!msg && !files.length || !emailOk()) return;
976
1021
  chime.unlock();
977
1022
  for (const p of files) localUrls.set(p.key, p.local);
978
- pending = {
979
- id: "pending",
980
- from: "visitor",
981
- body: msg,
982
- at: (/* @__PURE__ */ new Date()).toISOString(),
983
- ...files.length ? { attachments: files.map((p) => ({ id: p.key, name: p.name, type: p.type, size: p.size })) } : {}
984
- };
1023
+ outbox.push({
1024
+ message: {
1025
+ id: `pending-${++outboxSeq}`,
1026
+ from: "visitor",
1027
+ body: msg,
1028
+ at: (/* @__PURE__ */ new Date()).toISOString(),
1029
+ ...files.length ? { attachments: files.map((p) => ({ id: p.key, name: p.name, type: p.type, size: p.size })) } : {}
1030
+ },
1031
+ files,
1032
+ email: needEmail() && email.value.trim() ? email.value.trim() : void 0
1033
+ });
985
1034
  text.value = "";
986
1035
  picked = [];
987
1036
  error = "";
988
1037
  draw();
989
- await Promise.all(files.map((p) => p.up));
990
- const ids = files.filter((p) => p.id).map((p) => p.id);
991
- let sent = false;
992
- try {
993
- if (!msg && !ids.length) throw new Error(error || "The file could not be sent.");
994
- take(
995
- await api().sendChat({
996
- body: msg,
997
- email: needEmail() && email.value.trim() ? email.value.trim() : void 0,
998
- url: window.location.href.slice(0, 500),
999
- ...ids.length ? { attachments: ids } : {},
1000
- // A little context for a bug report; the server reads browser and system from the user agent.
1001
- context: { viewport: `${window.innerWidth}\xD7${window.innerHeight}` }
1002
- })
1003
- );
1004
- sent = true;
1005
- } catch (e) {
1006
- text.value = msg;
1007
- picked = files.filter((p) => p.id);
1008
- error = e.message;
1009
- }
1010
- pending = null;
1011
- for (const p of files) localUrls.delete(p.key);
1012
- draw();
1013
- if (sent) for (const p of files) URL.revokeObjectURL(p.local);
1014
- schedule();
1038
+ setTimeout(draw, SLOW_SEND_MS + 50);
1039
+ void flush();
1015
1040
  };
1016
1041
  form.addEventListener("submit", (e) => {
1017
1042
  e.preventDefault();
1018
- void submit();
1043
+ submit();
1019
1044
  });
1020
1045
  text.addEventListener("keydown", (e) => {
1021
1046
  if (e.key === "Enter" && !e.shiftKey && !e.isComposing) {
1022
1047
  e.preventDefault();
1023
- void submit();
1048
+ submit();
1024
1049
  }
1025
1050
  });
1026
1051
  text.addEventListener("input", () => send.disabled = !canSend());