@ada-support/embed2 1.10.0 → 1.11.1

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.
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Returns a data URI containing the fallback HTML.
3
+ * This is used when the chat iframe fails to load within the timeout period.
4
+ */
5
+ export declare function getFallbackDataURI(): string;
@@ -38,6 +38,7 @@ type ChatFrameProps = PropsFromState & OwnProps & PropsFromDispatch;
38
38
  interface ChatFrameState {
39
39
  isMounted: boolean;
40
40
  closeTransitionTime: number;
41
+ showFallback: boolean;
41
42
  }
42
43
  export declare class ChatFrame extends Component<ChatFrameProps, ChatFrameState> {
43
44
  iframeRef: import("preact").RefObject<any>;
@@ -50,6 +51,10 @@ export declare class ChatFrame extends Component<ChatFrameProps, ChatFrameState>
50
51
  componentDidMount(): void;
51
52
  componentDidUpdate(prevProps: ChatFrameProps): void;
52
53
  componentWillUnmount(): void;
54
+ /**
55
+ * Clears the fallback timeout and ensures cleanup
56
+ */
57
+ clearFallbackTimeout(): void;
53
58
  get darkMode(): boolean;
54
59
  getIframeSrc(): string;
55
60
  get styles(): string;
@@ -59,6 +64,7 @@ export declare class ChatFrame extends Component<ChatFrameProps, ChatFrameState>
59
64
  get chooseStyles(): string;
60
65
  handleScrollLock(): void;
61
66
  collapseIframe(): void;
67
+ handleFallbackMessage(event: MessageEvent): void;
62
68
  /**
63
69
  * Initial body "overflow" style may be set to something other than "auto"
64
70
  * We should remember it, to put back the initial value when we unlock the scrolling
@@ -1 +1,2 @@
1
1
  export declare const ANIMATION_DELAY = 50;
2
+ export declare const CHAT_FALLBACK_DELAY_MS = 5000;
@@ -64,4 +64,5 @@ export interface StartOptionsNoFunction {
64
64
  crossWindowPersistence?: boolean;
65
65
  testMode?: boolean;
66
66
  preload?: boolean;
67
+ showFallbackOnTimeout?: boolean;
67
68
  }
@@ -15919,7 +15919,7 @@ const client = new BrowserClient({
15919
15919
  return event;
15920
15920
  },
15921
15921
  environment: "production",
15922
- release: "1.10.0-3873935",
15922
+ release: "1.11.1-c291df9",
15923
15923
  sampleRate: 0.25,
15924
15924
  autoSessionTracking: false,
15925
15925
  // Integrations don't seem to work with Sentry: https://github.com/getsentry/sentry-javascript/issues/2541
@@ -16368,45 +16368,22 @@ let ActionTypes = /*#__PURE__*/function (ActionTypes) {
16368
16368
  ActionTypes["FOCUS_OFF_ADA_TYPE"] = "FOCUS_OFF_ADA";
16369
16369
  return ActionTypes;
16370
16370
  }({});
16371
- ;// CONCATENATED MODULE: ./src/client/helpers/event-subscriptions/index.ts
16372
- let subscriptions = [];
16373
- const getNextUniqueId = (() => {
16374
- let lastId = 0;
16375
- return () => {
16376
- lastId += 1;
16377
- return lastId;
16378
- };
16379
- })();
16380
- function unsubscribeEvent(id) {
16381
- subscriptions = subscriptions.filter(s => s.id !== id);
16382
- }
16383
- function subscribeEvent(eventKey, callback) {
16384
- const id = getNextUniqueId();
16385
- subscriptions.push({
16386
- eventKey,
16387
- callback,
16388
- id
16389
- });
16390
- return id;
16391
- }
16392
- function publishEvent(eventKey, data) {
16393
- subscriptions.forEach(subscription => {
16394
- // Using startsWith instead of === allows subscribing to a specific event OR a namespace,
16395
- // e.g. ada:campaigns subscribes to ada:campaigns:opened, ada:campaigns:engaged, and
16396
- // ada:campaigns:shown. Broadcasting this way simultaneously reaches subscriptions
16397
- // that have subscribed to a specific event, and those that have subscribed to a namespace
16398
- if (eventKey.startsWith(subscription.eventKey)) {
16399
- // try/catch to guard against missing or bad callbacks
16400
- try {
16401
- subscription.callback(data, {
16402
- eventKey
16403
- });
16404
- } catch {
16405
- // silently fail and proceed with remaining callbacks
16406
- }
16407
- }
16408
- });
16371
+ ;// CONCATENATED MODULE: ./src/services/helpers.ts
16372
+ const NO_OP_FUNCTION = () => {
16373
+ // Do nothing
16374
+ };
16375
+ function isStartOptions(input) {
16376
+ if (!(typeof input === "object")) {
16377
+ return false;
16378
+ }
16379
+ if (input === null) {
16380
+ return false;
16381
+ }
16382
+ return typeof input.handle === "string";
16409
16383
  }
16384
+ // EXTERNAL MODULE: ./node_modules/lodash.memoize/index.js
16385
+ var lodash_memoize = __webpack_require__(7654);
16386
+ var lodash_memoize_default = /*#__PURE__*/__webpack_require__.n(lodash_memoize);
16410
16387
  ;// CONCATENATED MODULE: ./src/common/helpers/http.ts
16411
16388
  /**
16412
16389
  * Vanilla HTTP request. Returns a Promise.
@@ -16451,6 +16428,101 @@ function httpRequest(obj) {
16451
16428
  xhr.send(obj.body);
16452
16429
  });
16453
16430
  }
16431
+ ;// CONCATENATED MODULE: ./src/services/logger/index.ts
16432
+
16433
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
16434
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
16435
+
16436
+
16437
+ const DEFAULT_LOG_SAMPLE_RATE = 0.01;
16438
+ const DD_BASE_URL = "https://browser-http-intake.logs.datadoghq.com/v1/input/";
16439
+ const DD_TOKEN = "pubfe23baedd2ea322bebb5ed2020fa2fa1";
16440
+
16441
+ // We need memoization to ensure consistency of logs
16442
+ const shouldLog = lodash_memoize_default()(sampleRate => {
16443
+ if (!DD_TOKEN || sampleRate === 0) {
16444
+ return false;
16445
+ }
16446
+ return Math.random() < (sampleRate || DEFAULT_LOG_SAMPLE_RATE);
16447
+ });
16448
+
16449
+ /**
16450
+ * Sends log to Datadog
16451
+ */
16452
+ async function log(message, extra, options) {
16453
+ if (!shouldLog(options === null || options === void 0 ? void 0 : options.sampleRate)) {
16454
+ return;
16455
+ }
16456
+ await httpRequest({
16457
+ url: `${DD_BASE_URL}${DD_TOKEN}?ddsource=browser&ddtags=version:1.5.0,env:${"production"}`,
16458
+ method: "POST",
16459
+ body: JSON.stringify(_objectSpread(_objectSpread({
16460
+ message
16461
+ }, extra), {}, {
16462
+ level: (options === null || options === void 0 ? void 0 : options.level) || "info",
16463
+ sampleRate: (options === null || options === void 0 ? void 0 : options.sampleRate) || DEFAULT_LOG_SAMPLE_RATE,
16464
+ service: "embed",
16465
+ env: "production",
16466
+ embedVersion: 2,
16467
+ version: "1.11.1",
16468
+ isNpm: true,
16469
+ commitHash: "c291df9"
16470
+ }))
16471
+ });
16472
+ }
16473
+ ;// CONCATENATED MODULE: ./src/client/helpers/event-subscriptions/index.ts
16474
+
16475
+
16476
+ let subscriptions = [];
16477
+ const getNextUniqueId = (() => {
16478
+ let lastId = 0;
16479
+ return () => {
16480
+ lastId += 1;
16481
+ return lastId;
16482
+ };
16483
+ })();
16484
+ function unsubscribeEvent(id) {
16485
+ subscriptions = subscriptions.filter(s => s.id !== id);
16486
+ }
16487
+ function subscribeEvent(eventKey, callback) {
16488
+ const id = getNextUniqueId();
16489
+
16490
+ // Log chat_frame_timeout event subscriptions
16491
+ if (eventKey === "ada:chat_frame_timeout") {
16492
+ log("Client subscribed to chat_frame_timeout event", {
16493
+ handle: isStartOptions(window.adaSettings) ? window.adaSettings.handle : undefined,
16494
+ hostPage: window.location.href,
16495
+ timestamp: new Date().toISOString()
16496
+ }, {
16497
+ sampleRate: 0.1,
16498
+ level: "info"
16499
+ });
16500
+ }
16501
+ subscriptions.push({
16502
+ eventKey,
16503
+ callback,
16504
+ id
16505
+ });
16506
+ return id;
16507
+ }
16508
+ function publishEvent(eventKey, data) {
16509
+ subscriptions.forEach(subscription => {
16510
+ // Using startsWith instead of === allows subscribing to a specific event OR a namespace,
16511
+ // e.g. ada:campaigns subscribes to ada:campaigns:opened, ada:campaigns:engaged, and
16512
+ // ada:campaigns:shown. Broadcasting this way simultaneously reaches subscriptions
16513
+ // that have subscribed to a specific event, and those that have subscribed to a namespace
16514
+ if (eventKey.startsWith(subscription.eventKey)) {
16515
+ // try/catch to guard against missing or bad callbacks
16516
+ try {
16517
+ subscription.callback(data, {
16518
+ eventKey
16519
+ });
16520
+ } catch {
16521
+ // silently fail and proceed with remaining callbacks
16522
+ }
16523
+ }
16524
+ });
16525
+ }
16454
16526
  ;// CONCATENATED MODULE: ./src/common/helpers/config-info.ts
16455
16527
  const isProduction = "production" === "production";
16456
16528
  ;// CONCATENATED MODULE: ./src/common/helpers/url/constants.ts
@@ -16468,8 +16540,8 @@ const ports = {
16468
16540
  };
16469
16541
  ;// CONCATENATED MODULE: ./src/common/helpers/url/index.ts
16470
16542
 
16471
- function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
16472
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
16543
+ function url_ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
16544
+ function url_objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? url_ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : url_ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
16473
16545
 
16474
16546
 
16475
16547
  const DEFAULT_ADA_DOMAIN = "ada";
@@ -16496,7 +16568,7 @@ function getEmbedURL(_ref) {
16496
16568
  } else {
16497
16569
  host = `http://${handle}.localhost:9001`;
16498
16570
  }
16499
- return `${host}/embed/${frameName}/${"3873935"}/index.html`;
16571
+ return `${host}/embed/${frameName}/${"c291df9"}/index.html`;
16500
16572
  }
16501
16573
  function constructQueryString(query) {
16502
16574
  return Object.keys(query).map(key => {
@@ -16544,7 +16616,7 @@ function getChatURL(_ref3) {
16544
16616
  domain,
16545
16617
  qp
16546
16618
  } = _ref3;
16547
- const queryParams = _objectSpread(_objectSpread({}, qp), {}, {
16619
+ const queryParams = url_objectSpread(url_objectSpread({}, qp), {}, {
16548
16620
  version
16549
16621
  });
16550
16622
  const queryString = constructQueryString(queryParams);
@@ -17036,7 +17108,8 @@ const getInitialState = adaSettings => ({
17036
17108
  chatterInLiveChat: false,
17037
17109
  wasCampaignShownButNowClosed: false,
17038
17110
  proactiveCampaignHadMessages: false,
17039
- deviceToken: null
17111
+ deviceToken: null,
17112
+ showFallbackOnTimeout: false
17040
17113
  });
17041
17114
  ;// CONCATENATED MODULE: ./src/common/helpers/get-intro-for-url.ts
17042
17115
  function getProcessedPath(path) {
@@ -17302,19 +17375,6 @@ const get_browser_language_getBrowserLanguage = () => {
17302
17375
  return browserLanguageString;
17303
17376
  };
17304
17377
  /* harmony default export */ var get_browser_language = (get_browser_language_getBrowserLanguage);
17305
- ;// CONCATENATED MODULE: ./src/services/helpers.ts
17306
- const NO_OP_FUNCTION = () => {
17307
- // Do nothing
17308
- };
17309
- function isStartOptions(input) {
17310
- if (!(typeof input === "object")) {
17311
- return false;
17312
- }
17313
- if (input === null) {
17314
- return false;
17315
- }
17316
- return typeof input.handle === "string";
17317
- }
17318
17378
  ;// CONCATENATED MODULE: ./src/campaigns/campaign.ts
17319
17379
 
17320
17380
  function campaign_ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
@@ -17637,51 +17697,6 @@ function getButtonText(client) {
17637
17697
  }
17638
17698
  return buttonTextMap[languageKey];
17639
17699
  }
17640
- // EXTERNAL MODULE: ./node_modules/lodash.memoize/index.js
17641
- var lodash_memoize = __webpack_require__(7654);
17642
- var lodash_memoize_default = /*#__PURE__*/__webpack_require__.n(lodash_memoize);
17643
- ;// CONCATENATED MODULE: ./src/services/logger/index.ts
17644
-
17645
- function logger_ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
17646
- function logger_objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? logger_ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : logger_ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
17647
-
17648
-
17649
- const DEFAULT_LOG_SAMPLE_RATE = 0.01; // 1% of logs will go through
17650
-
17651
- const DD_BASE_URL = "https://browser-http-intake.logs.datadoghq.com/v1/input/";
17652
- const DD_TOKEN = "pubfe23baedd2ea322bebb5ed2020fa2fa1";
17653
-
17654
- // We need memoization to ensure consistency of logs
17655
- const shouldLog = lodash_memoize_default()(sampleRate => {
17656
- if (!DD_TOKEN || sampleRate === 0) {
17657
- return false;
17658
- }
17659
- return Math.random() < (sampleRate || DEFAULT_LOG_SAMPLE_RATE);
17660
- });
17661
-
17662
- /**
17663
- * Sends log to Datadog
17664
- */
17665
- async function log(message, extra, options) {
17666
- if (!shouldLog(options === null || options === void 0 ? void 0 : options.sampleRate)) {
17667
- return;
17668
- }
17669
- await httpRequest({
17670
- url: `${DD_BASE_URL}${DD_TOKEN}?ddsource=browser&ddtags=version:1.5.0,env:${"production"}`,
17671
- method: "POST",
17672
- body: JSON.stringify(logger_objectSpread(logger_objectSpread({
17673
- message
17674
- }, extra), {}, {
17675
- sampleRate: (options === null || options === void 0 ? void 0 : options.sampleRate) || DEFAULT_LOG_SAMPLE_RATE,
17676
- service: "embed",
17677
- env: "production",
17678
- embedVersion: 2,
17679
- version: "1.10.0",
17680
- isNpm: true,
17681
- commitHash: "3873935"
17682
- }))
17683
- });
17684
- }
17685
17700
  ;// CONCATENATED MODULE: ./src/client/helpers/alternative-bot-rollout/index.ts
17686
17701
 
17687
17702
  function alternative_bot_rollout_ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
@@ -18333,12 +18348,26 @@ const CHAT_MANIFEST_PATH = "https://static.ada.support/chat-manifest.json";
18333
18348
  const loadChatManifest = async () => {
18334
18349
  try {
18335
18350
  const r = await fetch(CHAT_MANIFEST_PATH);
18351
+ if (!r.ok) {
18352
+ log(`Failed to load chat manifest - HTTP ${r.status}`, {
18353
+ hostPage: window.location.href,
18354
+ manifestUrl: CHAT_MANIFEST_PATH
18355
+ }, {
18356
+ level: "error",
18357
+ sampleRate: 1.0
18358
+ });
18359
+ return undefined;
18360
+ }
18336
18361
  return r.json();
18337
18362
  } catch (e) {
18338
18363
  // handling cases where r is undefined / other fetch errors
18339
18364
  // by returning undefined, we tell the server to load the unversioned index.html file for chat
18340
18365
  log("Failed to load chat manifest", {
18341
- hostPage: window.location.href
18366
+ hostPage: window.location.href,
18367
+ manifestUrl: CHAT_MANIFEST_PATH
18368
+ }, {
18369
+ level: "error",
18370
+ sampleRate: 1.0
18342
18371
  });
18343
18372
  return undefined;
18344
18373
  }
@@ -18720,6 +18749,7 @@ function ButtonFrame_mapStateToProps(storeState) {
18720
18749
  /* harmony default export */ var components_ButtonFrame = (connect(ButtonFrame_mapStateToProps)(ButtonFrame));
18721
18750
  ;// CONCATENATED MODULE: ./src/common/constants/timeouts.ts
18722
18751
  const ANIMATION_DELAY = 50;
18752
+ const CHAT_FALLBACK_DELAY_MS = 5000;
18723
18753
  ;// CONCATENATED MODULE: ./src/common/helpers/is-mobile.ts
18724
18754
  const isMobile = /(iPhone)|(iPod)|(android)|(webOS)/i.exec(navigator.userAgent) !== null;
18725
18755
  ;// CONCATENATED MODULE: ./src/client/components/ChatFrame/chatFrameEvents/chatterEvent/index.ts
@@ -18742,6 +18772,134 @@ function storeChatterEventDataInBrowser(client, payload) {
18742
18772
  }
18743
18773
  ;// CONCATENATED MODULE: ./src/client/components/ChatFrame/chatFrameEvents/index.ts
18744
18774
 
18775
+ ;// CONCATENATED MODULE: ./src/client/components/ChatFrame/fallback-html.ts
18776
+ /**
18777
+ * Fallback HTML shown when the chat iframe fails to load within the timeout period.
18778
+ * This is a self-contained HTML page with inline styles and JavaScript.
18779
+ */
18780
+ const FALLBACK_HTML = `<!DOCTYPE html>
18781
+ <html lang="en">
18782
+ <head>
18783
+ <meta charset="utf-8">
18784
+ <meta name="viewport" content="width=device-width, height=device-height">
18785
+ <meta http-equiv="x-ua-compatible" content="IE=edge">
18786
+ <title>Chat - Technical Difficulties</title>
18787
+ <style>
18788
+ * {
18789
+ box-sizing: border-box;
18790
+ }
18791
+ body {
18792
+ margin: 0;
18793
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
18794
+ -webkit-font-smoothing: antialiased;
18795
+ -moz-osx-font-smoothing: grayscale;
18796
+ }
18797
+ .container {
18798
+ display: flex;
18799
+ flex-direction: column;
18800
+ height: 100vh;
18801
+ background-color: #ffffff;
18802
+ }
18803
+ .content-area {
18804
+ flex: 1;
18805
+ display: flex;
18806
+ align-items: center;
18807
+ justify-content: center;
18808
+ padding: 24px 16px;
18809
+ }
18810
+ .message-container {
18811
+ width: 100%;
18812
+ max-width: 351px;
18813
+ }
18814
+ .message-card {
18815
+ display: flex;
18816
+ flex-direction: column;
18817
+ align-items: center;
18818
+ justify-content: center;
18819
+ padding: 24px 16px;
18820
+ border: 1px solid #e8e8eb;
18821
+ border-radius: 16px;
18822
+ background-color: #ffffff;
18823
+ box-shadow: 0px 4px 14px rgba(0, 0, 0, 0.06);
18824
+ }
18825
+ .icon-container {
18826
+ margin-bottom: 8px;
18827
+ }
18828
+ .icon-container svg {
18829
+ fill: rgba(0, 0, 0, 0.65);
18830
+ display: block;
18831
+ }
18832
+ .message-text {
18833
+ margin: 8px 0 0 0;
18834
+ line-height: 20px;
18835
+ font-size: 1rem;
18836
+ text-align: center;
18837
+ color: rgba(0, 0, 0, 0.95);
18838
+ }
18839
+ .close-button {
18840
+ margin-top: 16px;
18841
+ padding: 12px 24px;
18842
+ font-size: 14px;
18843
+ font-weight: 500;
18844
+ color: #ffffff;
18845
+ background-color: #000000;
18846
+ border: none;
18847
+ border-radius: 8px;
18848
+ cursor: pointer;
18849
+ transition: background-color 0.2s ease;
18850
+ font-family: inherit;
18851
+ }
18852
+ .close-button:hover {
18853
+ background-color: #333333;
18854
+ }
18855
+ .close-button:active {
18856
+ background-color: #1a1a1a;
18857
+ }
18858
+ .close-button:focus {
18859
+ outline: 2px solid #4d90fe;
18860
+ outline-offset: 2px;
18861
+ }
18862
+ </style>
18863
+ </head>
18864
+ <body style="margin:0">
18865
+ <div id="app">
18866
+ <div class="container">
18867
+ <div class="content-area">
18868
+ <div class="message-container">
18869
+ <div class="message-card">
18870
+ <div class="icon-container">
18871
+ <svg xmlns="http://www.w3.org/2000/svg" width="42" height="42" viewBox="0 0 24 24" fill="currentColor">
18872
+ <path d="M12,9a1,1,0,0,0-1,1v3a1,1,0,0,0,2,0V10A1,1,0,0,0,12,9Zm7-7H5A3,3,0,0,0,2,5V15a3,3,0,0,0,3,3H16.59l3.7,3.71A1,1,0,0,0,21,22a.84.84,0,0,0,.38-.08A1,1,0,0,0,22,21V5A3,3,0,0,0,19,2Zm1,16.59-2.29-2.3A1,1,0,0,0,17,16H5a1,1,0,0,1-1-1V5A1,1,0,0,1,5,4H19a1,1,0,0,1,1,1ZM12,6a1,1,0,1,0,1,1A1,1,0,0,0,12,6Z"></path>
18873
+ </svg>
18874
+ </div>
18875
+ <p role="alert" data-testid="default-outage-message" class="message-text">
18876
+ The chat is experiencing technical difficulties and we're working to fix them as soon as possible. Please check back for an update in a few minutes.
18877
+ </p>
18878
+ <button class="close-button" id="closeButton" aria-label="Close chat window">Close</button>
18879
+ </div>
18880
+ </div>
18881
+ </div>
18882
+ </div>
18883
+ </div>
18884
+ <script>
18885
+ document.getElementById('closeButton').addEventListener('click', function() {
18886
+ try {
18887
+ window.parent.postMessage('ADA_FALLBACK_CLOSE', '*');
18888
+ } catch (error) {
18889
+ console.error('Failed to close chat:', error);
18890
+ }
18891
+ });
18892
+ </script>
18893
+ </body>
18894
+ </html>`;
18895
+
18896
+ /**
18897
+ * Returns a data URI containing the fallback HTML.
18898
+ * This is used when the chat iframe fails to load within the timeout period.
18899
+ */
18900
+ function getFallbackDataURI() {
18901
+ return `data:text/html;charset=utf-8,${encodeURIComponent(FALLBACK_HTML)}`;
18902
+ }
18745
18903
  ;// CONCATENATED MODULE: ./src/client/components/ChatFrame/index.tsx
18746
18904
 
18747
18905
 
@@ -18760,6 +18918,7 @@ function storeChatterEventDataInBrowser(client, payload) {
18760
18918
 
18761
18919
 
18762
18920
 
18921
+
18763
18922
  class ChatFrame extends d {
18764
18923
  constructor() {
18765
18924
  super(...arguments);
@@ -18771,7 +18930,8 @@ class ChatFrame extends d {
18771
18930
  _defineProperty(this, "url", this.getIframeSrc());
18772
18931
  _defineProperty(this, "state", {
18773
18932
  isMounted: false,
18774
- closeTransitionTime: 200
18933
+ closeTransitionTime: 200,
18934
+ showFallback: false
18775
18935
  });
18776
18936
  } // This url must be a static property - if getIframeSrc is called repeatedly, the resulting URL may change improperly
18777
18937
  // For example, the skipGreeting query parameter was being changed from true to false, causing the AFM-384 bug
@@ -18792,9 +18952,14 @@ class ChatFrame extends d {
18792
18952
  this.bindChatEventHandlers();
18793
18953
  this.handleScrollLock();
18794
18954
 
18955
+ // Listen for messages from the fallback iframe
18956
+ window.addEventListener("message", this.handleFallbackMessage.bind(this));
18957
+
18795
18958
  // We use this log to track number of chat impressions (times when users see chat iFrame render first time).
18796
18959
  log("Chat frame mount", {
18797
18960
  handle,
18961
+ chatUrl: this.url,
18962
+ embedVersion: "c291df9".slice(0, 7),
18798
18963
  embedSettings: adaSettings
18799
18964
  });
18800
18965
 
@@ -18809,8 +18974,23 @@ class ChatFrame extends d {
18809
18974
  const {
18810
18975
  isDrawerOpen
18811
18976
  } = this.props;
18977
+ const {
18978
+ showFallback
18979
+ } = this.state;
18812
18980
  if (isDrawerOpen !== prevProps.isDrawerOpen) {
18813
18981
  this.handleScrollLock();
18982
+
18983
+ // When opening after fallback was shown, reset state and reload iframe
18984
+ if (isDrawerOpen && !prevProps.isDrawerOpen && showFallback) {
18985
+ this.setState({
18986
+ showFallback: false
18987
+ });
18988
+
18989
+ // Force reload by updating src (frameLoaded will handle timeout setup)
18990
+ if (this.iframeRef.current) {
18991
+ this.iframeRef.current.src = this.url;
18992
+ }
18993
+ }
18814
18994
  }
18815
18995
  }
18816
18996
  componentWillUnmount() {
@@ -18818,6 +18998,17 @@ class ChatFrame extends d {
18818
18998
  messageService
18819
18999
  } = this.props;
18820
19000
  messageService.unregisterChannel("chat");
19001
+ this.clearFallbackTimeout();
19002
+ }
19003
+
19004
+ /**
19005
+ * Clears the fallback timeout and ensures cleanup
19006
+ */
19007
+ clearFallbackTimeout() {
19008
+ if (this.chatRenderTimeout) {
19009
+ clearTimeout(this.chatRenderTimeout);
19010
+ this.chatRenderTimeout = undefined;
19011
+ }
18821
19012
  }
18822
19013
  get darkMode() {
18823
19014
  var _client$ui_settings, _client$ui_settings$c;
@@ -18852,7 +19043,7 @@ class ChatFrame extends d {
18852
19043
  const hostPageUrlParams = new URL(window.location.href).searchParams;
18853
19044
  const smsToken = hostPageUrlParams.get("adaSMSToken");
18854
19045
  const queryParams = {
18855
- embedVersion: "3873935".slice(0, 7),
19046
+ embedVersion: "c291df9".slice(0, 7),
18856
19047
  greeting,
18857
19048
  language,
18858
19049
  skipGreeting,
@@ -19062,6 +19253,19 @@ class ChatFrame extends d {
19062
19253
  this.iframeRef.current.style.width = 0;
19063
19254
  this.iframeRef.current.style.height = 0;
19064
19255
  }
19256
+ handleFallbackMessage(event) {
19257
+ var _this$iframeRef$curre;
19258
+ // Only accept messages from our own iframe
19259
+ if (!event.source || event.source !== ((_this$iframeRef$curre = this.iframeRef.current) === null || _this$iframeRef$curre === void 0 ? void 0 : _this$iframeRef$curre.contentWindow)) {
19260
+ return;
19261
+ }
19262
+ if (event.data === "ADA_FALLBACK_CLOSE") {
19263
+ const {
19264
+ toggleChat
19265
+ } = this.props;
19266
+ toggleChat();
19267
+ }
19268
+ }
19065
19269
 
19066
19270
  /**
19067
19271
  * Initial body "overflow" style may be set to something other than "auto"
@@ -19138,7 +19342,7 @@ class ChatFrame extends d {
19138
19342
  if (!this.channel) {
19139
19343
  throw new AdaEmbedError("`this.channel` is not defined");
19140
19344
  }
19141
- clearTimeout(this.chatRenderTimeout);
19345
+ this.clearFallbackTimeout();
19142
19346
  this.channel.isConnected = true;
19143
19347
  setConnectionState({
19144
19348
  chat: ConnectionState.Done
@@ -19406,33 +19610,81 @@ class ChatFrame extends d {
19406
19610
  });
19407
19611
  }
19408
19612
  frameLoaded() {
19613
+ var _this$iframeRef$curre2;
19409
19614
  const {
19410
19615
  handle,
19411
- adaSettings
19616
+ adaSettings,
19617
+ isDrawerOpen
19412
19618
  } = this.props;
19619
+ const {
19620
+ showFallback
19621
+ } = this.state;
19413
19622
  // SUP-1141- TODO: Remove chatFrameTimeoutCallback
19414
19623
  const {
19415
- chatFrameTimeoutCallback
19624
+ chatFrameTimeoutCallback,
19625
+ showFallbackOnTimeout
19416
19626
  } = adaSettings;
19627
+
19628
+ // Don't start timeout if drawer is closed
19629
+ if (!isDrawerOpen) {
19630
+ return;
19631
+ }
19632
+
19633
+ // Don't start timeout if already showing fallback (prevents redundant timeouts)
19634
+ if (showFallback) {
19635
+ return;
19636
+ }
19637
+
19638
+ // Don't start timeout if loading fallback data URI (would create infinite loop)
19639
+ const currentSrc = (_this$iframeRef$curre2 = this.iframeRef.current) === null || _this$iframeRef$curre2 === void 0 ? void 0 : _this$iframeRef$curre2.src;
19640
+ if (currentSrc !== null && currentSrc !== void 0 && currentSrc.startsWith("data:text/html")) {
19641
+ return;
19642
+ }
19643
+
19644
+ // Clear any existing timeout before starting new one (prevents orphaned timeouts)
19645
+ this.clearFallbackTimeout();
19417
19646
  this.chatRenderTimeout = window.setTimeout(() => {
19418
19647
  var _this$channel;
19419
- log("Chat frame took over 5 seconds to respond", {
19648
+ log(`Chat frame took over ${CHAT_FALLBACK_DELAY_MS / 1000} seconds to respond`, {
19420
19649
  handle,
19650
+ chatUrl: this.url,
19651
+ embedVersion: "c291df9".slice(0, 7),
19421
19652
  embedSettings: adaSettings
19653
+ }, {
19654
+ level: "error",
19655
+ sampleRate: 1.0
19422
19656
  });
19423
- if (!((_this$channel = this.channel) !== null && _this$channel !== void 0 && _this$channel.isConnected)) {
19657
+ const {
19658
+ isDrawerOpen: drawerIsOpen
19659
+ } = this.props;
19660
+
19661
+ // Only show fallback if drawer is STILL open and chat hasn't connected
19662
+ if (!((_this$channel = this.channel) !== null && _this$channel !== void 0 && _this$channel.isConnected) && drawerIsOpen) {
19663
+ log("Chat frame took over 5 seconds to respond", {
19664
+ handle,
19665
+ embedSettings: adaSettings
19666
+ });
19667
+
19424
19668
  // SUP-1141- TODO: Remove chatFrameTimeoutCallback
19425
19669
  if (chatFrameTimeoutCallback) {
19426
19670
  chatFrameTimeoutCallback();
19427
19671
  }
19428
19672
  publishEvent("ada:chat_frame_timeout", undefined);
19673
+ if (showFallbackOnTimeout) {
19674
+ this.setState({
19675
+ showFallback: true
19676
+ });
19677
+ }
19429
19678
  }
19430
- }, 5000);
19679
+ }, CHAT_FALLBACK_DELAY_MS);
19431
19680
  }
19432
19681
  render() {
19433
19682
  const {
19434
19683
  client
19435
19684
  } = this.props;
19685
+ const {
19686
+ showFallback
19687
+ } = this.state;
19436
19688
  const iFrameName = `ada-chat-frame`;
19437
19689
  const sandboxParams = "allow-downloads allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-top-navigation allow-forms allow-modals";
19438
19690
  const roleDescription = () => {
@@ -19441,8 +19693,11 @@ class ChatFrame extends d {
19441
19693
  }
19442
19694
  return `${client.handle.charAt(0).toUpperCase() + client.handle.slice(1)} Chat Window`;
19443
19695
  };
19696
+
19697
+ // Use fallback URL if showFallback is true, otherwise use normal chat URL
19698
+ const iframeSrc = showFallback ? getFallbackDataURI() : this.url;
19444
19699
  return v("iframe", {
19445
- src: this.url,
19700
+ src: iframeSrc,
19446
19701
  scrolling: "no",
19447
19702
  style: this.chooseStyles,
19448
19703
  frameBorder: "0",
@@ -19498,6 +19753,8 @@ function ChatFrame_mapDispatchToProps(dispatch) {
19498
19753
  toggleChat: bindActionCreators(ActionCreators.TOGGLE_CHAT_ACTION, dispatch)
19499
19754
  };
19500
19755
  }
19756
+
19757
+ // eslint-disable-next-line import/no-default-export
19501
19758
  /* harmony default export */ var components_ChatFrame = (connect(ChatFrame_mapStateToProps, ChatFrame_mapDispatchToProps)(ChatFrame));
19502
19759
  ;// CONCATENATED MODULE: ./src/client/components/IntroFrame/index.tsx
19503
19760
 
@@ -5,4 +5,5 @@ export declare const shouldLog: ((sampleRate?: number) => boolean) & import("lod
5
5
  */
6
6
  export declare function log(message: string, extra?: Record<string, unknown>, options?: {
7
7
  sampleRate?: number;
8
+ level?: "error" | "warn" | "info" | "debug";
8
9
  }): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ada-support/embed2",
3
- "version": "1.10.0",
3
+ "version": "1.11.1",
4
4
  "description": "",
5
5
  "main": "dist/npm-entry",
6
6
  "typings": "dist/npm-entry/index-npm.d.ts",
@@ -52,8 +52,8 @@
52
52
  "@babel/plugin-transform-runtime": "^7.16.10",
53
53
  "@babel/preset-env": "^7.23.2",
54
54
  "@babel/preset-typescript": "^7.23.2",
55
+ "@testing-library/jest-dom": "^5",
55
56
  "@testing-library/preact": "^2.0.1",
56
- "@types/enzyme": "^3.10.18",
57
57
  "@types/jest": "^27.0.2",
58
58
  "@types/json-stable-stringify": "^1.0.33",
59
59
  "@types/lodash.debounce": "^4.0.6",
@@ -62,15 +62,12 @@
62
62
  "@types/uniqid": "^5.3.2",
63
63
  "@types/webpack": "^5.28.0",
64
64
  "@types/webpack-bundle-analyzer": "^4.4.1",
65
- "@types/webpack-dev-server": "^4.7.2",
66
65
  "babel-loader": "9.1.2",
67
66
  "babel-preset-preact": "^2.0.0",
68
67
  "browserslist": "^4.17.4",
69
- "cypress": "^9.6.1",
68
+ "cypress": "^13.0.0",
70
69
  "depcheck": "^1.4.3",
71
70
  "dotenv": "^8.2.0",
72
- "enzyme": "^3.11.0",
73
- "enzyme-adapter-preact-pure": "^4.0.1",
74
71
  "eslint": "^8.8.0",
75
72
  "eslint-config-prettier": "^8.5.0",
76
73
  "eslint-plugin-chai-friendly": "^0.7.2",
@@ -85,14 +82,14 @@
85
82
  "mock-xmlhttprequest": "^7.0.4",
86
83
  "npm-run-all": "^4.1.5",
87
84
  "prettier": "2.7.1",
88
- "start-server-and-test": "^2.0.4",
85
+ "start-server-and-test": "^2.1.2",
89
86
  "ts-node": "^10.2.1",
90
87
  "tsconfig-paths": "^3.11.0",
91
88
  "typescript": "^5.6.3",
92
89
  "webpack": "^5.76.0",
93
90
  "webpack-bundle-analyzer": "^4.5.0",
94
91
  "webpack-cli": "^5.1.4",
95
- "webpack-dev-server": "^4.3.1",
92
+ "webpack-dev-server": "^5.2.2",
96
93
  "webpack-merge": "^5.8.0"
97
94
  },
98
95
  "dependencies": {
@@ -113,7 +110,8 @@
113
110
  "minimist": ">=1.2.6",
114
111
  "terser": ">=5.14.2",
115
112
  "@babel/traverse": ">=7.23.2",
116
- "semver": ">=7.5.2"
113
+ "semver": ">=7.5.2",
114
+ "form-data": ">=4.0.4"
117
115
  },
118
116
  "files": [
119
117
  "dist/npm-entry"
@@ -125,4 +123,4 @@
125
123
  "publishConfig": {
126
124
  "access": "public"
127
125
  }
128
- }
126
+ }