@microsoft/omnichannel-chat-widget 0.1.0-main.15c1e2b → 0.1.0-main.18d1166

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 (69) hide show
  1. package/README.md +7 -34
  2. package/lib/cjs/common/Constants.js +3 -0
  3. package/lib/cjs/common/telemetry/TelemetryConstants.js +31 -4
  4. package/lib/cjs/common/utils.js +23 -2
  5. package/lib/cjs/components/confirmationpanestateful/ConfirmationPaneStateful.js +11 -18
  6. package/lib/cjs/components/headerstateful/HeaderStateful.js +5 -4
  7. package/lib/cjs/components/livechatwidget/common/ActivitySubscriber/BotAuthActivitySubscriber.js +139 -0
  8. package/lib/cjs/components/livechatwidget/common/agentEndConversationHelper.js +36 -0
  9. package/lib/cjs/components/livechatwidget/common/createAdapter.js +2 -0
  10. package/lib/cjs/components/livechatwidget/common/defaultProps/dummyDefaultProps.js +5 -2
  11. package/lib/cjs/components/livechatwidget/common/endChat.js +43 -63
  12. package/lib/cjs/components/livechatwidget/common/initWebChatComposer.js +11 -43
  13. package/lib/cjs/components/livechatwidget/common/reconnectChatHelper.js +3 -3
  14. package/lib/cjs/components/livechatwidget/common/setPostChatContextAndLoadSurvey.js +254 -2
  15. package/lib/cjs/components/livechatwidget/common/startChat.js +35 -50
  16. package/lib/cjs/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +64 -27
  17. package/lib/cjs/components/loadingpanestateful/LoadingPaneStateful.js +8 -1
  18. package/lib/cjs/components/postchatsurveypanestateful/PostChatSurveyPaneStateful.js +3 -1
  19. package/lib/cjs/components/prechatsurveypanestateful/PreChatSurveyPaneStateful.js +1 -1
  20. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/typingIndicatorMiddleware.js +7 -3
  21. package/lib/cjs/contexts/common/ConversationEndEntity.js +12 -0
  22. package/lib/cjs/contexts/common/LiveChatWidgetActionType.js +10 -7
  23. package/lib/cjs/contexts/common/LiveChatWidgetContextInitialState.js +4 -1
  24. package/lib/cjs/contexts/createReducer.js +28 -2
  25. package/lib/cjs/hooks/useDebounce.js +28 -0
  26. package/lib/cjs/hooks/useWindowDimensions.js +30 -0
  27. package/lib/cjs/plugins/newMessageEventHandler.js +10 -0
  28. package/lib/esm/common/Constants.js +3 -0
  29. package/lib/esm/common/telemetry/TelemetryConstants.js +31 -4
  30. package/lib/esm/common/utils.js +20 -0
  31. package/lib/esm/components/confirmationpanestateful/ConfirmationPaneStateful.js +11 -18
  32. package/lib/esm/components/headerstateful/HeaderStateful.js +5 -4
  33. package/lib/esm/components/livechatwidget/common/ActivitySubscriber/BotAuthActivitySubscriber.js +134 -0
  34. package/lib/esm/components/livechatwidget/common/agentEndConversationHelper.js +30 -0
  35. package/lib/esm/components/livechatwidget/common/createAdapter.js +2 -0
  36. package/lib/esm/components/livechatwidget/common/defaultProps/dummyDefaultProps.js +5 -2
  37. package/lib/esm/components/livechatwidget/common/endChat.js +45 -65
  38. package/lib/esm/components/livechatwidget/common/initWebChatComposer.js +13 -45
  39. package/lib/esm/components/livechatwidget/common/reconnectChatHelper.js +3 -3
  40. package/lib/esm/components/livechatwidget/common/setPostChatContextAndLoadSurvey.js +254 -3
  41. package/lib/esm/components/livechatwidget/common/startChat.js +35 -50
  42. package/lib/esm/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +64 -27
  43. package/lib/esm/components/loadingpanestateful/LoadingPaneStateful.js +8 -1
  44. package/lib/esm/components/postchatsurveypanestateful/PostChatSurveyPaneStateful.js +3 -1
  45. package/lib/esm/components/prechatsurveypanestateful/PreChatSurveyPaneStateful.js +1 -1
  46. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/typingIndicatorMiddleware.js +5 -3
  47. package/lib/esm/contexts/common/ConversationEndEntity.js +5 -0
  48. package/lib/esm/contexts/common/LiveChatWidgetActionType.js +10 -7
  49. package/lib/esm/contexts/common/LiveChatWidgetContextInitialState.js +4 -1
  50. package/lib/esm/contexts/createReducer.js +28 -2
  51. package/lib/esm/hooks/useDebounce.js +22 -0
  52. package/lib/esm/hooks/useWindowDimensions.js +23 -0
  53. package/lib/esm/plugins/newMessageEventHandler.js +10 -0
  54. package/lib/types/common/Constants.d.ts +3 -0
  55. package/lib/types/common/telemetry/TelemetryConstants.d.ts +27 -6
  56. package/lib/types/common/utils.d.ts +1 -0
  57. package/lib/types/components/confirmationpanestateful/interfaces/IConfirmationPaneStatefulParams.d.ts +0 -7
  58. package/lib/types/components/livechatwidget/common/ActivitySubscriber/BotAuthActivitySubscriber.d.ts +9 -0
  59. package/lib/types/components/livechatwidget/common/agentEndConversationHelper.d.ts +6 -0
  60. package/lib/types/components/livechatwidget/common/initWebChatComposer.d.ts +1 -1
  61. package/lib/types/components/livechatwidget/common/setPostChatContextAndLoadSurvey.d.ts +6 -1
  62. package/lib/types/components/livechatwidget/common/startChat.d.ts +2 -3
  63. package/lib/types/components/livechatwidget/interfaces/ILiveChatWidgetControlProps.d.ts +1 -0
  64. package/lib/types/contexts/common/ConversationEndEntity.d.ts +4 -0
  65. package/lib/types/contexts/common/ILiveChatWidgetContext.d.ts +5 -1
  66. package/lib/types/contexts/common/LiveChatWidgetActionType.d.ts +10 -7
  67. package/lib/types/hooks/useDebounce.d.ts +3 -0
  68. package/lib/types/hooks/useWindowDimensions.d.ts +4 -0
  69. package/package.json +3 -3
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = useDebounce;
7
+ var _react = require("react");
8
+ function useDebounce(func) {
9
+ let delay = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1000;
10
+ const timer = (0, _react.useRef)();
11
+ (0, _react.useEffect)(() => {
12
+ return () => {
13
+ if (!timer.current) return;
14
+ clearTimeout(timer.current);
15
+ };
16
+ }, []);
17
+ const debouncedFunction = function () {
18
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
19
+ args[_key] = arguments[_key];
20
+ }
21
+ const newTimer = setTimeout(() => {
22
+ func(...args);
23
+ }, delay);
24
+ clearTimeout(timer.current);
25
+ timer.current = newTimer;
26
+ };
27
+ return debouncedFunction;
28
+ }
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = useWindowDimensions;
7
+ var _react = require("react");
8
+ var _useDebounce = _interopRequireDefault(require("./useDebounce"));
9
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10
+ function getWindowDimensions() {
11
+ const {
12
+ innerWidth: width,
13
+ innerHeight: height
14
+ } = window;
15
+ return {
16
+ width,
17
+ height
18
+ };
19
+ }
20
+ function useWindowDimensions() {
21
+ let delay = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 200;
22
+ const [windowDimensions, setWindowDimensions] = (0, _react.useState)(getWindowDimensions());
23
+ const handleResize = () => setWindowDimensions(getWindowDimensions());
24
+ const debouncedHandleResize = (0, _useDebounce.default)(handleResize, delay);
25
+ (0, _react.useEffect)(() => {
26
+ window.addEventListener("resize", debouncedHandleResize);
27
+ return () => window.removeEventListener("resize", debouncedHandleResize);
28
+ }, []);
29
+ return windowDimensions;
30
+ }
@@ -15,6 +15,7 @@ const createOnNewAdapterActivityHandler = (chatId, userId) => {
15
15
  const isHistoryMessage = isActivityMessage && ((activity === null || activity === void 0 ? void 0 : (_activity$channelData = activity.channelData) === null || _activity$channelData === void 0 ? void 0 : (_activity$channelData2 = _activity$channelData.tags) === null || _activity$channelData2 === void 0 ? void 0 : _activity$channelData2.includes(_Constants.Constants.historyMessageTag)) || (activity === null || activity === void 0 ? void 0 : (_activity$channelData3 = activity.channelData) === null || _activity$channelData3 === void 0 ? void 0 : _activity$channelData3.fromList));
16
16
  raiseMessageEvent(activity, isHistoryMessage);
17
17
  };
18
+ let isHistoryMessageReceivedEventRasied = false;
18
19
  const raiseMessageEvent = (activity, isHistoryMessage) => {
19
20
  if ((activity === null || activity === void 0 ? void 0 : activity.type) === _Constants.Constants.message) {
20
21
  var _text, _text2, _activity$channelData4, _activity$from;
@@ -73,6 +74,15 @@ const createOnNewAdapterActivityHandler = (chatId, userId) => {
73
74
  Description: "New message received",
74
75
  Data: payload
75
76
  });
77
+ } else {
78
+ if (!isHistoryMessageReceivedEventRasied) {
79
+ isHistoryMessageReceivedEventRasied = true;
80
+ _TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
81
+ Event: _TelemetryConstants.TelemetryEvent.HistoryMessageReceived,
82
+ Description: "History message received",
83
+ Data: payload
84
+ });
85
+ }
76
86
  }
77
87
  }
78
88
  }
@@ -21,6 +21,8 @@ _defineProperty(Constants, "truePascal", "True");
21
21
  _defineProperty(Constants, "true", "true");
22
22
  _defineProperty(Constants, "false", "false");
23
23
  _defineProperty(Constants, "maximumUnreadMessageCount", 99);
24
+ _defineProperty(Constants, "userParticipantTypeTag", "User");
25
+ _defineProperty(Constants, "botParticipantTypeTag", "Bot");
24
26
  _defineProperty(Constants, "channelIdKey", "ChannelId-");
25
27
  _defineProperty(Constants, "ChannelId", "lcw");
26
28
  _defineProperty(Constants, "CustomerTag", "FromCustomer");
@@ -123,6 +125,7 @@ _defineProperty(HtmlAttributeNames, "noreferrerTag", "noreferrer");
123
125
  _defineProperty(HtmlAttributeNames, "adaptiveCardClassName", "ac-adaptiveCard");
124
126
  _defineProperty(HtmlAttributeNames, "adaptiveCardTextBlockClassName", "ac-textBlock");
125
127
  _defineProperty(HtmlAttributeNames, "adaptiveCardToggleInputClassName", "ac-toggleInput");
128
+ _defineProperty(HtmlAttributeNames, "adaptiveCardActionSetClassName", "ac-actionSet");
126
129
  export class WebChatMiddlewareConstants {}
127
130
  _defineProperty(WebChatMiddlewareConstants, "nextVisibleActivity", "nextVisibleActivity");
128
131
  _defineProperty(WebChatMiddlewareConstants, "timeBetweenTimestampGroups", 300000);
@@ -51,6 +51,10 @@ export let BroadcastEvent;
51
51
  BroadcastEvent["RaiseErrorEvent"] = "RaiseErrorEvent";
52
52
  BroadcastEvent["NetworkDisconnected"] = "NetworkDisconnected";
53
53
  BroadcastEvent["NetworkReconnected"] = "NetworkReconnected";
54
+ BroadcastEvent["SigninCardReceived"] = "SignInCardReceived";
55
+ BroadcastEvent["BotAuthConfigRequest"] = "BotAuthConfigRequest";
56
+ BroadcastEvent["BotAuthConfigResponse"] = "BotAuthConfigResponse";
57
+ BroadcastEvent["HideChatVisibilityChangeEvent"] = "hideChatVisibilityChangeEvent";
54
58
  })(BroadcastEvent || (BroadcastEvent = {}));
55
59
  export let TelemetryEvent;
56
60
  (function (TelemetryEvent) {
@@ -86,11 +90,10 @@ export let TelemetryEvent;
86
90
  TelemetryEvent["GetConversationDetailsCallFailed"] = "GetConversationDetailsCallFailed";
87
91
  TelemetryEvent["EndChatSDKCallFailed"] = "EndChatSDKCallFailed";
88
92
  TelemetryEvent["GetChatReconnectContextSDKCallFailed"] = "GetChatReconnectContextSDKCallFailed";
89
- TelemetryEvent["PostChatContextCallSucceed"] = "PostChatContextCallSucceed";
90
- TelemetryEvent["PostChatContextCallFailed"] = "PostChatContextCallFailed";
91
93
  TelemetryEvent["ParseAdaptiveCardFailed"] = "ParseAdaptiveCardFailed";
92
94
  TelemetryEvent["ClientDataStoreProviderFailed"] = "ClientDataStoreProviderFailed";
93
95
  TelemetryEvent["InMemoryDataStoreFailed"] = "InMemoryDataStoreFailed";
96
+ TelemetryEvent["ChatVisibilityChanged"] = "ChatVisibilityChanged";
94
97
  TelemetryEvent["WebChatLoaded"] = "WebChatLoaded";
95
98
  TelemetryEvent["LCWChatButtonClicked"] = "LCWChatButtonClicked";
96
99
  TelemetryEvent["LCWChatButtonShow"] = "LCWChatButtonShow";
@@ -98,6 +101,7 @@ export let TelemetryEvent;
98
101
  TelemetryEvent["WidgetLoadComplete"] = "WidgetLoadComplete";
99
102
  TelemetryEvent["WidgetLoadFailed"] = "WidgetLoadFailed";
100
103
  TelemetryEvent["StartChatMethodException"] = "StartChatMethodException";
104
+ TelemetryEvent["CloseChatCall"] = "CloseChatCall";
101
105
  TelemetryEvent["CloseChatMethodException"] = "CloseChatMethodException";
102
106
  TelemetryEvent["PrechatSurveyLoaded"] = "PrechatSurveyLoaded";
103
107
  TelemetryEvent["PrechatSubmitted"] = "PrechatSubmitted";
@@ -111,6 +115,7 @@ export let TelemetryEvent;
111
115
  TelemetryEvent["DownloadTranscriptResponseNullOrUndefined"] = "DownloadTranscriptResponseNullOrUndefined";
112
116
  TelemetryEvent["EmailTranscriptSent"] = "EmailTranscriptSent";
113
117
  TelemetryEvent["EmailTranscriptFailed"] = "EmailTranscriptFailed";
118
+ TelemetryEvent["ErrorUIPaneLoaded"] = "ErrorUIPaneLoaded";
114
119
  TelemetryEvent["DownloadTranscriptFailed"] = "DownloadTranscriptFailed";
115
120
  TelemetryEvent["StartChatFailed"] = "StartChatFailed";
116
121
  TelemetryEvent["IC3ThreadUpdateEventReceived"] = "IC3ThreadUpdateEventReceived";
@@ -119,8 +124,6 @@ export let TelemetryEvent;
119
124
  TelemetryEvent["LoadingPaneLoaded"] = "LoadingPaneLoaded";
120
125
  TelemetryEvent["EmailTranscriptLoaded"] = "EmailTranscriptLoaded";
121
126
  TelemetryEvent["OutOfOfficePaneLoaded"] = "OutOfOfficePaneLoaded";
122
- TelemetryEvent["PostChatSurveyLoadingPaneLoaded"] = "PostChatSurveyLoadingPaneLoaded";
123
- TelemetryEvent["PostChatSurveyLoaded"] = "PostChatSurveyLoaded";
124
127
  TelemetryEvent["ConfirmationPaneLoaded"] = "ConfirmationPaneLoaded";
125
128
  TelemetryEvent["ProactiveChatPaneLoaded"] = "ProactiveChatPaneLoaded";
126
129
  TelemetryEvent["ReconnectChatPaneLoaded"] = "ReconnectChatPaneLoaded";
@@ -133,6 +136,7 @@ export let TelemetryEvent;
133
136
  TelemetryEvent["SuppressBotMagicCodeSucceeded"] = "SuppressBotMagicCodeSucceeded";
134
137
  TelemetryEvent["SuppressBotMagicCodeFailed"] = "SuppressBotMagicCodeFailed";
135
138
  TelemetryEvent["GetConversationDetailsException"] = "GetConversationDetailsException";
139
+ TelemetryEvent["AppStatesException"] = "AppStatesException";
136
140
  TelemetryEvent["BrowserUnloadEventStarted"] = "BrowserUnloadEventStarted";
137
141
  TelemetryEvent["GetAuthTokenCalled"] = "GetAuthTokenCalled";
138
142
  TelemetryEvent["GetAuthTokenFailed"] = "GetAuthTokenFailed";
@@ -140,6 +144,11 @@ export let TelemetryEvent;
140
144
  TelemetryEvent["CustomerVoiceResponsePageLoaded"] = "CustomerVoiceResponsePageLoaded";
141
145
  TelemetryEvent["CustomerVoiceFormResponseSubmitted"] = "CustomerVoiceFormResponseSubmitted";
142
146
  TelemetryEvent["CustomerVoiceFormResponseError"] = "CustomerVoiceFormResponseError";
147
+ TelemetryEvent["BotAuthActivityEmptySasUrl"] = "BotAuthActivityEmptySasUrl";
148
+ TelemetryEvent["SetBotAuthProviderFetchConfig"] = "SetBotAuthProviderFetchConfig";
149
+ TelemetryEvent["SetBotAuthProviderHideCard"] = "SetBotAuthProviderHideCard";
150
+ TelemetryEvent["SetBotAuthProviderDisplayCard"] = "SetBotAuthProviderDisplayCard";
151
+ TelemetryEvent["SetBotAuthProviderNotFound"] = "SetBotAuthProviderNotFound";
143
152
  TelemetryEvent["ProcessingHTMLTextMiddlewareFailed"] = "ProcessingHTMLTextMiddlewareFailed";
144
153
  TelemetryEvent["ProcessingSanitizationMiddlewareFailed"] = "ProcessingSanitizationMiddlewareFailed";
145
154
  TelemetryEvent["FormatTagsMiddlewareJSONStringifyFailed"] = "FormatTagsMiddlewareJSONStringifyFailed";
@@ -149,6 +158,8 @@ export let TelemetryEvent;
149
158
  TelemetryEvent["DataMaskingRuleApplyFailed"] = "DataMaskingRuleApplyFailed";
150
159
  TelemetryEvent["IC3ClientEvent"] = "IC3ClientEvent";
151
160
  TelemetryEvent["ConversationEndedThreadEventReceived"] = "ConversationEndedThreadEventReceived";
161
+ TelemetryEvent["ConversationEndedByCustomer"] = "ConversationEndedByCustomer";
162
+ TelemetryEvent["ConversationEndedByAgent"] = "ConversationEndedByAgent";
152
163
  TelemetryEvent["InvalidConfiguration"] = "InvalidConfiguration";
153
164
  TelemetryEvent["SendTypingIndicatorSucceeded"] = "SendTypingIndicatorSucceeded";
154
165
  TelemetryEvent["SendTypingIndicatorFailed"] = "SendTypingIndicatorFailed";
@@ -167,9 +178,19 @@ export let TelemetryEvent;
167
178
  TelemetryEvent["MessageSent"] = "MessageSent";
168
179
  TelemetryEvent["MessageReceived"] = "MessageReceived";
169
180
  TelemetryEvent["SystemMessageReceived"] = "SystemMessageReceived";
181
+ TelemetryEvent["HistoryMessageReceived"] = "HistoryMessageReceived";
170
182
  TelemetryEvent["CustomContextReceived"] = "CustomContextReceived";
171
183
  TelemetryEvent["NetworkDisconnected"] = "NetworkDisconnected";
172
184
  TelemetryEvent["NetworkReconnected"] = "NetworkReconnected";
185
+ TelemetryEvent["LinkModePostChatWorkflowStarted"] = "LinkModePostChatWorkflowStarted";
186
+ TelemetryEvent["EmbedModePostChatWorkflowStarted"] = "EmbedModePostChatWorkflowStarted";
187
+ TelemetryEvent["PostChatWorkflowFromCustomer"] = "PostChatWorkflowFromCustomer";
188
+ TelemetryEvent["PostChatWorkflowFromAgent"] = "PostChatWorkflowFromAgent";
189
+ TelemetryEvent["PostChatWorkflowFromBot"] = "PostChatWorkflowFromBot";
190
+ TelemetryEvent["PostChatContextCallSucceed"] = "PostChatContextCallSucceed";
191
+ TelemetryEvent["PostChatContextCallFailed"] = "PostChatContextCallFailed";
192
+ TelemetryEvent["PostChatSurveyLoadingPaneLoaded"] = "PostChatSurveyLoadingPaneLoaded";
193
+ TelemetryEvent["PostChatSurveyLoaded"] = "PostChatSurveyLoaded";
173
194
  })(TelemetryEvent || (TelemetryEvent = {}));
174
195
  export class TelemetryConstants {
175
196
  static map(eventTypeOrScenarioType) {
@@ -224,6 +245,12 @@ export class TelemetryConstants {
224
245
  case TelemetryEvent.CustomerVoiceResponsePageLoaded:
225
246
  case TelemetryEvent.CustomerVoiceFormResponseSubmitted:
226
247
  case TelemetryEvent.CustomerVoiceFormResponseError:
248
+ case TelemetryEvent.LinkModePostChatWorkflowStarted:
249
+ case TelemetryEvent.EmbedModePostChatWorkflowStarted:
250
+ case TelemetryEvent.PostChatWorkflowFromCustomer:
251
+ case TelemetryEvent.PostChatWorkflowFromAgent:
252
+ case TelemetryEvent.PostChatWorkflowFromBot:
253
+ case TelemetryEvent.AppStatesException:
227
254
  return ScenarioType.ACTIONS;
228
255
  case TelemetryEvent.StartChatSDKCall:
229
256
  case TelemetryEvent.StartChatEventRecevied:
@@ -1,3 +1,4 @@
1
+ var _this = this;
1
2
  import { AriaTelemetryConstants, Constants, LocaleConstants } from "./Constants";
2
3
  import { DataStoreManager } from "./contextDataStore/DataStoreManager";
3
4
  import { KeyCodes } from "./KeyCodes";
@@ -304,4 +305,23 @@ export const addDelayInMs = ms => {
304
305
  };
305
306
  export const getBroadcastChannelName = (widgetId, widgetInstanceId) => {
306
307
  return widgetInstanceId && !isNullOrEmptyString(widgetInstanceId) ? `${widgetInstanceId}_${widgetId}` : widgetId;
308
+ };
309
+
310
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
311
+ export const debounceLeading = function (fn) {
312
+ let ms = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 3000;
313
+ let timeoutId;
314
+ return function () {
315
+ // eslint-disable-line @typescript-eslint/no-explicit-any
316
+
317
+ if (!timeoutId) {
318
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
319
+ args[_key] = arguments[_key];
320
+ }
321
+ fn.apply(_this, args);
322
+ }
323
+ timeoutId = setTimeout(() => {
324
+ timeoutId = null;
325
+ }, ms);
326
+ };
307
327
  };
@@ -4,21 +4,17 @@ import React, { useEffect } from "react";
4
4
  import { findAllFocusableElement, findParentFocusableElementsWithoutChildContainer, preventFocusToMoveOutOfElement, setFocusOnElement, setFocusOnSendBox, setTabIndices } from "../../common/utils";
5
5
  import { DimLayer } from "../dimlayer/DimLayer";
6
6
  import { LiveChatWidgetActionType } from "../../contexts/common/LiveChatWidgetActionType";
7
- import { NotificationHandler } from "../webchatcontainerstateful/webchatcontroller/notification/NotificationHandler";
8
- import { NotificationScenarios } from "../webchatcontainerstateful/webchatcontroller/enums/NotificationScenarios";
9
7
  import { TelemetryHelper } from "../../common/telemetry/TelemetryHelper";
10
8
  import useChatAdapterStore from "../../hooks/useChatAdapterStore";
11
9
  import useChatContextStore from "../../hooks/useChatContextStore";
10
+ import { ConversationEndEntity } from "../../contexts/common/ConversationEndEntity";
12
11
 
13
12
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
13
  export const ConfirmationPaneStateful = props => {
15
14
  const initialTabIndexMap = new Map();
16
15
  let elements = [];
17
16
  const [state, dispatch] = useChatContextStore();
18
- const {
19
- prepareEndChat
20
- } = props;
21
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
22
18
  const [adapter] = useChatAdapterStore();
23
19
  const controlProps = {
24
20
  id: "oc-lcw-confirmation-pane",
@@ -32,18 +28,15 @@ export const ConfirmationPaneStateful = props => {
32
28
  type: LiveChatWidgetActionType.SET_SHOW_CONFIRMATION,
33
29
  payload: false
34
30
  });
35
- try {
36
- setTabIndices(elements, initialTabIndexMap, true);
37
- await prepareEndChat(adapter, state);
38
- } catch (ex) {
39
- TelemetryHelper.logSDKEvent(LogLevel.ERROR, {
40
- Event: TelemetryEvent.GetConversationDetailsCallFailed,
41
- ExceptionDetails: {
42
- exception: `Get Conversation Details Call Failed : ${ex}`
43
- }
44
- });
45
- NotificationHandler.notifyError(NotificationScenarios.Connection, "Get Conversation Details Call Failed: " + ex);
46
- }
31
+ setTabIndices(elements, initialTabIndexMap, true);
32
+ dispatch({
33
+ type: LiveChatWidgetActionType.SET_CONVERSATION_ENDED_BY,
34
+ payload: ConversationEndEntity.Customer
35
+ });
36
+ TelemetryHelper.logActionEvent(LogLevel.INFO, {
37
+ Event: TelemetryEvent.ConversationEndedByCustomer,
38
+ Description: "Conversation is ended by customer."
39
+ });
47
40
  },
48
41
  onCancel: () => {
49
42
  TelemetryHelper.logActionEvent(LogLevel.INFO, {
@@ -7,6 +7,7 @@ import { TelemetryHelper } from "../../common/telemetry/TelemetryHelper";
7
7
  import { defaultOutOfOfficeHeaderStyleProps } from "./common/styleProps/defaultOutOfOfficeHeaderStyleProps";
8
8
  import useChatAdapterStore from "../../hooks/useChatAdapterStore";
9
9
  import useChatContextStore from "../../hooks/useChatContextStore";
10
+ import { ConversationEndEntity } from "../../contexts/common/ConversationEndEntity";
10
11
  export const HeaderStateful = props => {
11
12
  var _state$domainStates$l, _state$domainStates$l2, _headerProps$controlP, _headerProps$controlP2, _headerProps$controlP3, _outOfOfficeHeaderPro;
12
13
  const [state, dispatch] = useChatContextStore();
@@ -21,7 +22,7 @@ export const HeaderStateful = props => {
21
22
  const [outOfOperatingHours, setOutOfOperatingHours] = useState(((_state$domainStates$l = state.domainStates.liveChatConfig) === null || _state$domainStates$l === void 0 ? void 0 : (_state$domainStates$l2 = _state$domainStates$l.LiveWSAndLiveChatEngJoin) === null || _state$domainStates$l2 === void 0 ? void 0 : _state$domainStates$l2.OutOfOperatingHours) === "True");
22
23
  const outOfOfficeStyleProps = Object.assign({}, defaultOutOfOfficeHeaderStyleProps, outOfOfficeHeaderProps === null || outOfOfficeHeaderProps === void 0 ? void 0 : outOfOfficeHeaderProps.styleProps);
23
24
  const conversationState = useRef(state.appStates.conversationState);
24
- const conversationEndedByAgent = useRef(state.appStates.conversationEndedByAgent);
25
+ const conversationEndedBy = useRef(state.appStates.conversationEndedBy);
25
26
  const controlProps = {
26
27
  id: "oc-lcw-header",
27
28
  dir: state.domainStates.globalDir,
@@ -41,7 +42,7 @@ export const HeaderStateful = props => {
41
42
  Event: TelemetryEvent.HeaderCloseButtonClicked,
42
43
  Description: "Header Close button clicked."
43
44
  });
44
- if (conversationState.current === ConversationState.Active || conversationEndedByAgent.current) {
45
+ if (conversationState.current === ConversationState.Active || conversationEndedBy.current === ConversationEndEntity.Agent) {
45
46
  dispatch({
46
47
  type: LiveChatWidgetActionType.SET_SHOW_CONFIRMATION,
47
48
  payload: true
@@ -63,7 +64,7 @@ export const HeaderStateful = props => {
63
64
  ...(headerProps === null || headerProps === void 0 ? void 0 : headerProps.controlProps),
64
65
  hideTitle: state.appStates.conversationState === ConversationState.Loading && !state.appStates.isStartChatFailing || state.appStates.conversationState === ConversationState.PostchatLoading || (headerProps === null || headerProps === void 0 ? void 0 : (_headerProps$controlP = headerProps.controlProps) === null || _headerProps$controlP === void 0 ? void 0 : _headerProps$controlP.hideTitle),
65
66
  hideIcon: state.appStates.conversationState === ConversationState.Loading && !state.appStates.isStartChatFailing || state.appStates.conversationState === ConversationState.PostchatLoading || (headerProps === null || headerProps === void 0 ? void 0 : (_headerProps$controlP2 = headerProps.controlProps) === null || _headerProps$controlP2 === void 0 ? void 0 : _headerProps$controlP2.hideIcon),
66
- hideCloseButton: state.appStates.conversationState === ConversationState.Loading && !state.appStates.isStartChatFailing || state.appStates.conversationState === ConversationState.Prechat || state.appStates.conversationState === ConversationState.ReconnectChat || (headerProps === null || headerProps === void 0 ? void 0 : (_headerProps$controlP3 = headerProps.controlProps) === null || _headerProps$controlP3 === void 0 ? void 0 : _headerProps$controlP3.hideCloseButton)
67
+ hideCloseButton: state.appStates.conversationState === ConversationState.Loading && !state.appStates.isStartChatFailing || state.appStates.conversationState === ConversationState.PostchatLoading || state.appStates.conversationState === ConversationState.Prechat || state.appStates.conversationState === ConversationState.ReconnectChat || (headerProps === null || headerProps === void 0 ? void 0 : (_headerProps$controlP3 = headerProps.controlProps) === null || _headerProps$controlP3 === void 0 ? void 0 : _headerProps$controlP3.hideCloseButton)
67
68
  };
68
69
  const outOfOfficeControlProps = {
69
70
  id: "oc-lcw-header",
@@ -87,7 +88,7 @@ export const HeaderStateful = props => {
87
88
  if (state.appStates.conversationState) {
88
89
  conversationState.current = state.appStates.conversationState;
89
90
  }
90
- conversationEndedByAgent.current = state.appStates.conversationEndedByAgent;
91
+ conversationEndedBy.current = state.appStates.conversationEndedBy;
91
92
  }, [state.appStates]);
92
93
  return /*#__PURE__*/React.createElement(Header, {
93
94
  componentOverrides: headerProps === null || headerProps === void 0 ? void 0 : headerProps.componentOverrides,
@@ -0,0 +1,134 @@
1
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
2
+ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
3
+ function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
4
+ /* eslint-disable @typescript-eslint/no-unused-vars */
5
+ /* eslint-disable @typescript-eslint/no-explicit-any */
6
+ import { BroadcastService } from "@microsoft/omnichannel-chat-components";
7
+ import { BroadcastEvent, LogLevel, TelemetryEvent } from "../../../../common/telemetry/TelemetryConstants";
8
+ import { TelemetryHelper } from "../../../../common/telemetry/TelemetryHelper";
9
+ const supportedSignInCardContentTypes = ["application/vnd.microsoft.card.signin", "application/vnd.microsoft.card.oauth"];
10
+ const botOauthUrlRegex = /[\S]+.botframework.com\/api\/oauth\/signin\?signin=([\S]+)/;
11
+ const delay = t => new Promise(resolve => setTimeout(resolve, t));
12
+ const fetchBotAuthConfigRetries = 3;
13
+ const fetchBotAuthConfigRetryInterval = 1000;
14
+ let response;
15
+ const extractSignInId = signInUrl => {
16
+ const result = botOauthUrlRegex.exec(signInUrl);
17
+ if (result && result[1]) {
18
+ return result[1];
19
+ }
20
+ return "";
21
+ };
22
+ const extractSasUrl = async attachment => {
23
+ let sasUrl = undefined;
24
+ if (attachment && attachment.content && attachment.content.tokenPostResource && attachment.content.tokenPostResource.sasUrl) {
25
+ sasUrl = attachment.content.tokenPostResource.sasUrl;
26
+ }
27
+ if (!sasUrl) {
28
+ const signInId = extractSignInId(attachment.content.buttons[0].value);
29
+ const getTestUrlEndpoint = `https://token.botframework.com/api/sas/gettesturl?signInId=${signInId}`;
30
+ try {
31
+ const response = await window.fetch(getTestUrlEndpoint);
32
+ if (response.status === 200) {
33
+ const responseJson = await response.json();
34
+ sasUrl = responseJson.sasUrl;
35
+ }
36
+ } catch {
37
+ sasUrl = undefined;
38
+ }
39
+ }
40
+ return sasUrl;
41
+ };
42
+ const fetchBotAuthConfig = async retries => {
43
+ TelemetryHelper.logLoadingEvent(LogLevel.INFO, {
44
+ Event: TelemetryEvent.SetBotAuthProviderFetchConfig
45
+ });
46
+ const botAuthConfigRequestEvent = {
47
+ eventName: BroadcastEvent.BotAuthConfigRequest
48
+ };
49
+ BroadcastService.postMessage(botAuthConfigRequestEvent);
50
+ const listener = BroadcastService.getMessageByEventName(BroadcastEvent.BotAuthConfigResponse).subscribe(data => {
51
+ var _data$payload, _data$payload2;
52
+ response = ((_data$payload = data.payload) === null || _data$payload === void 0 ? void 0 : _data$payload.response) !== undefined ? (_data$payload2 = data.payload) === null || _data$payload2 === void 0 ? void 0 : _data$payload2.response : response;
53
+ listener.unsubscribe();
54
+ });
55
+ if (response !== undefined) {
56
+ //return response;
57
+ return response;
58
+ }
59
+ if (retries === 1) {
60
+ // Base Case
61
+ throw new Error();
62
+ }
63
+ await delay(fetchBotAuthConfigRetryInterval);
64
+ return await fetchBotAuthConfig(--retries);
65
+ };
66
+ export class BotAuthActivitySubscriber {
67
+ constructor() {
68
+ _defineProperty(this, "observer", void 0);
69
+ _defineProperty(this, "signInCardSeen", void 0);
70
+ this.signInCardSeen = new Set();
71
+ }
72
+ applicable(activity) {
73
+ var _activity$attachments;
74
+ return (activity === null || activity === void 0 ? void 0 : (_activity$attachments = activity.attachments) === null || _activity$attachments === void 0 ? void 0 : _activity$attachments.length) > 0 && activity.attachments[0] && supportedSignInCardContentTypes.indexOf(activity.attachments[0].contentType) >= 0;
75
+ }
76
+ async apply(activity) {
77
+ this.observer.next(false); // Hides card
78
+ const attachment = activity.attachments[0];
79
+ const signInUrl = attachment.content.buttons[0].value;
80
+ const signInId = extractSignInId(signInUrl);
81
+ if (!signInId) {
82
+ return;
83
+ }
84
+ if (this.signInCardSeen.has(signInId)) {
85
+ // Prevents duplicate auth
86
+ return;
87
+ }
88
+ this.signInCardSeen.add(signInId);
89
+ const sasUrl = await extractSasUrl(attachment);
90
+ const event = {
91
+ eventName: BroadcastEvent.SigninCardReceived,
92
+ payload: {
93
+ sasUrl
94
+ }
95
+ };
96
+ if (!sasUrl) {
97
+ TelemetryHelper.logLoadingEvent(LogLevel.INFO, {
98
+ Event: TelemetryEvent.BotAuthActivityEmptySasUrl,
99
+ Description: "SaS Url is empty"
100
+ });
101
+ return activity;
102
+ } else {
103
+ BroadcastService.postMessage(event);
104
+ }
105
+ try {
106
+ const response = await fetchBotAuthConfig(fetchBotAuthConfigRetries);
107
+ if (response === false) {
108
+ TelemetryHelper.logLoadingEvent(LogLevel.INFO, {
109
+ Event: TelemetryEvent.SetBotAuthProviderHideCard
110
+ });
111
+ } else {
112
+ TelemetryHelper.logLoadingEvent(LogLevel.INFO, {
113
+ Event: TelemetryEvent.SetBotAuthProviderDisplayCard
114
+ });
115
+ return activity;
116
+ }
117
+ } catch {
118
+ TelemetryHelper.logLoadingEvent(LogLevel.INFO, {
119
+ Event: TelemetryEvent.SetBotAuthProviderNotFound
120
+ });
121
+ //this is to ensure listener continues waiting for response
122
+ if (this.signInCardSeen.has(signInId)) {
123
+ this.signInCardSeen.delete(signInId);
124
+ }
125
+ return activity;
126
+ }
127
+ }
128
+ async next(activity) {
129
+ if (this.applicable(activity)) {
130
+ return await this.apply(activity);
131
+ }
132
+ return activity;
133
+ }
134
+ }
@@ -0,0 +1,30 @@
1
+ import { LogLevel, TelemetryEvent } from "../../../common/telemetry/TelemetryConstants";
2
+ import { TelemetryHelper } from "../../../common/telemetry/TelemetryHelper";
3
+ import { ConversationEndEntity } from "../../../contexts/common/ConversationEndEntity";
4
+ import { LiveChatWidgetActionType } from "../../../contexts/common/LiveChatWidgetActionType";
5
+ import { checkPostChatEnabled } from "./setPostChatContextAndLoadSurvey";
6
+ const handleAgentEndConversation = (props, state, dispatch) => {
7
+ const isPostChatEnabled = checkPostChatEnabled(props, state);
8
+ if (isPostChatEnabled) {
9
+ if (!state.appStates.postChatWorkflowInProgress) {
10
+ dispatch({
11
+ type: LiveChatWidgetActionType.SET_CONVERSATION_ENDED_BY,
12
+ payload: ConversationEndEntity.Agent
13
+ });
14
+ TelemetryHelper.logActionEvent(LogLevel.INFO, {
15
+ Event: TelemetryEvent.ConversationEndedByAgent,
16
+ Description: "Conversation is ended from agent side"
17
+ });
18
+ }
19
+ } else {
20
+ dispatch({
21
+ type: LiveChatWidgetActionType.SET_CONVERSATION_ENDED_BY,
22
+ payload: ConversationEndEntity.Agent
23
+ });
24
+ TelemetryHelper.logActionEvent(LogLevel.INFO, {
25
+ Event: TelemetryEvent.ConversationEndedByAgent,
26
+ Description: "Conversation is ended from agent side"
27
+ });
28
+ }
29
+ };
30
+ export { handleAgentEndConversation };
@@ -4,6 +4,7 @@ import { NotificationScenarios } from "../../webchatcontainerstateful/webchatcon
4
4
  import { defaultMiddlewareLocalizedTexts } from "../../webchatcontainerstateful/common/defaultProps/defaultMiddlewareLocalizedTexts";
5
5
  import { ChatAdapterShim } from "./ChatAdapterShim";
6
6
  import { PauseActivitySubscriber } from "./ActivitySubscriber/PauseActivitySubscriber";
7
+ import { BotAuthActivitySubscriber } from "./ActivitySubscriber/BotAuthActivitySubscriber";
7
8
 
8
9
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
10
  export const createAdapter = async chatSDK => {
@@ -29,6 +30,7 @@ export const createAdapter = async chatSDK => {
29
30
  if (chatSDK.isMockModeOn !== true) {
30
31
  adapter = new ChatAdapterShim(adapter);
31
32
  adapter.addSubscriber(new PauseActivitySubscriber());
33
+ adapter.addSubscriber(new BotAuthActivitySubscriber());
32
34
  return adapter.chatAdapter;
33
35
  }
34
36
  return adapter;
@@ -421,7 +421,8 @@ export const dummyDefaultProps = {
421
421
  alignItems: "center",
422
422
  margin: "0px 14px 0px 14px",
423
423
  textOverflow: "ellipsis !important",
424
- width: "max-content",
424
+ width: "90px",
425
+ whiteSpace: "nowrap",
425
426
  cursor: "pointer"
426
427
  },
427
428
  classNames: {
@@ -578,6 +579,7 @@ export const dummyDefaultProps = {
578
579
  hideCallingContainer: false,
579
580
  hideChatButton: false,
580
581
  hideConfirmationPane: false,
582
+ hideErrorUIPane: false,
581
583
  hideFooter: false,
582
584
  hideHeader: false,
583
585
  hideLoadingPane: false,
@@ -1025,7 +1027,8 @@ export const dummyDefaultProps = {
1025
1027
  margin: "0px 0px 20px 0px",
1026
1028
  display: "flex",
1027
1029
  order: 1,
1028
- alignSelf: "auto"
1030
+ alignSelf: "auto",
1031
+ overflow: "visible"
1029
1032
  },
1030
1033
  iconImageProps: {
1031
1034
  src: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCjxnIGZpbHRlcj0idXJsKCNmaWx0ZXIwX2lpKSI+DQo8cGF0aCBkPSJNMTUuMTk3MSAxNi4yNzI1VjI1Ljg1MjRDMTUuMTk3MSAyNy4zODExIDE1Ljg0MDEgMjcuNTIwMSAxNi45ODMyIDI3LjUyMDFMMjYuNzA4NCAyNy41NjQ5TDMxLjAwMzkgMzIuMzEyM1YyNy41NjQ5SDMxLjg5N0MzMi4xNzQzIDI3LjU2MzcgMzIuNDQ4NyAyNy41MDc3IDMyLjcwNDUgMjcuNDAwMUMzMi45NjAzIDI3LjI5MjQgMzMuMTkyNSAyNy4xMzUzIDMzLjM4NzggMjYuOTM3NUMzMy41ODMxIDI2LjczOTggMzMuNzM3NyAyNi41MDU0IDMzLjg0MjcgMjYuMjQ3N0MzMy45NDc4IDI1Ljk5IDM0LjAwMTMgMjUuNzE0IDM0LjAwMDEgMjUuNDM1NVYxNi4zMDM4QzM0LjAwMTMgMTYuMDI1NCAzMy45NDc4IDE1Ljc0OTQgMzMuODQyNyAxNS40OTE3QzMzLjczNzcgMTUuMjM0IDMzLjU4MzEgMTQuOTk5NiAzMy4zODc4IDE0LjgwMThDMzMuMTkyNSAxNC42MDQxIDMyLjk2MDMgMTQuNDQ2OSAzMi43MDQ1IDE0LjMzOTNDMzIuNDQ4NyAxNC4yMzE2IDMyLjE3NDMgMTQuMTc1NiAzMS44OTcgMTQuMTc0NEwxNy4zMDQ3IDE0LjE0MzFDMTcuMDI2OSAxNC4xNDM3IDE2Ljc1MiAxNC4xOTkyIDE2LjQ5NTcgMTQuMzA2NkMxNi4yMzk0IDE0LjQxNCAxNi4wMDY3IDE0LjU3MTEgMTUuODEwOSAxNC43Njg5QzE1LjYxNTIgMTQuOTY2NyAxNS40NjAyIDE1LjIwMTMgMTUuMzU0OCAxNS40NTkzQzE1LjI0OTUgMTUuNzE3MyAxNS4xOTU5IDE1Ljk5MzYgMTUuMTk3MSAxNi4yNzI1WiIgZmlsbD0iI0Q2RDZENiIvPg0KPC9nPg0KPGcgZmlsdGVyPSJ1cmwoI2ZpbHRlcjFfZGlpKSI+DQo8cGF0aCBkPSJNMjcuODczNSA2LjY5ODg3VjE4Ljg0MDlDMjcuODczNSAyMC43Nzg1IDI3LjA1NzIgMjAuOTU0NiAyNS42MDU4IDIwLjk1NDZMMTMuMjU4IDIxLjAxMTRMNy44MDQxNCAyNy4wMjg1VjIxLjAxMTRINi42NzAyN0M2LjMxODEyIDIxLjAwOTkgNS45Njk3MSAyMC45Mzg5IDUuNjQ0OTMgMjAuODAyNUM1LjMyMDE2IDIwLjY2NjEgNS4wMjUzOCAyMC40NjY4IDQuNzc3NDIgMjAuMjE2MkM0LjUyOTQ2IDE5Ljk2NTYgNC4zMzMxOSAxOS42Njg1IDQuMTk5OCAxOS4zNDE5QzQuMDY2NDIgMTkuMDE1MiAzLjk5ODUzIDE4LjY2NTUgNC4wMDAwMiAxOC4zMTI1VjYuNzM4NjRDMy45OTg1MyA2LjM4NTcxIDQuMDY2NDIgNi4wMzU5NSA0LjE5OTggNS43MDkzMUM0LjMzMzE5IDUuMzgyNjcgNC41Mjk0NiA1LjA4NTU3IDQuNzc3NDIgNC44MzQ5NUM1LjAyNTM4IDQuNTg0MzQgNS4zMjAxNiA0LjM4NTEzIDUuNjQ0OTMgNC4yNDg2OUM1Ljk2OTcxIDQuMTEyMjUgNi4zMTgxMiA0LjA0MTI2IDYuNjcwMjcgNC4wMzk3N0wyNS4xOTc2IDRDMjUuNTUwMiA0LjAwMDc0IDI1Ljg5OTMgNC4wNzExOCAyNi4yMjQ3IDQuMjA3MjlDMjYuNTUwMSA0LjM0MzM5IDI2Ljg0NTYgNC41NDI0OSAyNy4wOTQxIDQuNzkzMThDMjcuMzQyNyA1LjA0Mzg2IDI3LjUzOTUgNS4zNDEyMiAyNy42NzMyIDUuNjY4MjNDMjcuODA3IDUuOTk1MjMgMjcuODc1IDYuMzQ1NDYgMjcuODczNSA2LjY5ODg3WiIgZmlsbD0idXJsKCNwYWludDBfbGluZWFyKSIvPg0KPC9nPg0KPGRlZnM+DQo8ZmlsdGVyIGlkPSJmaWx0ZXIwX2lpIiB4PSIxNS4xOTcxIiB5PSIxNC4xNDMxIiB3aWR0aD0iMTguODAzMSIgaGVpZ2h0PSIxOC4xNjkzIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+DQo8ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPg0KPGZlQmxlbmQgbW9kZT0ibm9ybWFsIiBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHJlc3VsdD0ic2hhcGUiLz4NCjxmZUNvbG9yTWF0cml4IGluPSJTb3VyY2VBbHBoYSIgdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDEyNyAwIiByZXN1bHQ9ImhhcmRBbHBoYSIvPg0KPGZlT2Zmc2V0IGR4PSItMC4yIiBkeT0iLTAuMiIvPg0KPGZlQ29tcG9zaXRlIGluMj0iaGFyZEFscGhhIiBvcGVyYXRvcj0iYXJpdGhtZXRpYyIgazI9Ii0xIiBrMz0iMSIvPg0KPGZlQ29sb3JNYXRyaXggdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMSAwIDAgMCAwIDEgMCAwIDAgMCAxIDAgMCAwIDAuMTcgMCIvPg0KPGZlQmxlbmQgbW9kZT0ibm9ybWFsIiBpbjI9InNoYXBlIiByZXN1bHQ9ImVmZmVjdDFfaW5uZXJTaGFkb3ciLz4NCjxmZUNvbG9yTWF0cml4IGluPSJTb3VyY2VBbHBoYSIgdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDEyNyAwIiByZXN1bHQ9ImhhcmRBbHBoYSIvPg0KPGZlT2Zmc2V0IGR4PSItMC41IiBkeT0iLTAuNSIvPg0KPGZlQ29tcG9zaXRlIGluMj0iaGFyZEFscGhhIiBvcGVyYXRvcj0iYXJpdGhtZXRpYyIgazI9Ii0xIiBrMz0iMSIvPg0KPGZlQ29sb3JNYXRyaXggdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMSAwIDAgMCAwIDEgMCAwIDAgMCAxIDAgMCAwIDAuMTYgMCIvPg0KPGZlQmxlbmQgbW9kZT0ibm9ybWFsIiBpbjI9ImVmZmVjdDFfaW5uZXJTaGFkb3ciIHJlc3VsdD0iZWZmZWN0Ml9pbm5lclNoYWRvdyIvPg0KPC9maWx0ZXI+DQo8ZmlsdGVyIGlkPSJmaWx0ZXIxX2RpaSIgeD0iMCIgeT0iMCIgd2lkdGg9IjM5Ljg3MzYiIGhlaWdodD0iMzkuMDI4NSIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiPg0KPGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz4NCjxmZUNvbG9yTWF0cml4IGluPSJTb3VyY2VBbHBoYSIgdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDEyNyAwIi8+DQo8ZmVPZmZzZXQgZHg9IjQiIGR5PSI0Ii8+DQo8ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSI0Ii8+DQo8ZmVDb2xvck1hdHJpeCB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMC4yNSAwIi8+DQo8ZmVCbGVuZCBtb2RlPSJub3JtYWwiIGluMj0iQmFja2dyb3VuZEltYWdlRml4IiByZXN1bHQ9ImVmZmVjdDFfZHJvcFNoYWRvdyIvPg0KPGZlQmxlbmQgbW9kZT0ibm9ybWFsIiBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2Ryb3BTaGFkb3ciIHJlc3VsdD0ic2hhcGUiLz4NCjxmZUNvbG9yTWF0cml4IGluPSJTb3VyY2VBbHBoYSIgdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDEyNyAwIiByZXN1bHQ9ImhhcmRBbHBoYSIvPg0KPGZlT2Zmc2V0IGR4PSItMiIgZHk9Ii0yIi8+DQo8ZmVDb21wb3NpdGUgaW4yPSJoYXJkQWxwaGEiIG9wZXJhdG9yPSJhcml0aG1ldGljIiBrMj0iLTEiIGszPSIxIi8+DQo8ZmVDb2xvck1hdHJpeCB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMCAwIDAgMCAxIDAgMCAwIDAgMSAwIDAgMCAwIDEgMCAwIDAgMC4xNyAwIi8+DQo8ZmVCbGVuZCBtb2RlPSJub3JtYWwiIGluMj0ic2hhcGUiIHJlc3VsdD0iZWZmZWN0Ml9pbm5lclNoYWRvdyIvPg0KPGZlQ29sb3JNYXRyaXggaW49IlNvdXJjZUFscGhhIiB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMTI3IDAiIHJlc3VsdD0iaGFyZEFscGhhIi8+DQo8ZmVPZmZzZXQgZHg9Ii0xIiBkeT0iLTEiLz4NCjxmZUNvbXBvc2l0ZSBpbjI9ImhhcmRBbHBoYSIgb3BlcmF0b3I9ImFyaXRobWV0aWMiIGsyPSItMSIgazM9IjEiLz4NCjxmZUNvbG9yTWF0cml4IHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwIDAgMCAwIDEgMCAwIDAgMCAxIDAgMCAwIDAgMSAwIDAgMCAwLjE2IDAiLz4NCjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW4yPSJlZmZlY3QyX2lubmVyU2hhZG93IiByZXN1bHQ9ImVmZmVjdDNfaW5uZXJTaGFkb3ciLz4NCjwvZmlsdGVyPg0KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyIiB4MT0iLTAuMzk1MDAxIiB5MT0iMjMuMTI4MiIgeDI9IjIwLjEwNTgiIHkyPSIzNy44NDc0IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+DQo8c3RvcCBzdG9wLWNvbG9yPSIjRUZFRkVGIi8+DQo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IndoaXRlIi8+DQo8L2xpbmVhckdyYWRpZW50Pg0KPC9kZWZzPg0KPC9zdmc+DQo=",