@microsoft/omnichannel-chat-widget 1.7.8-main.7a07fc5 → 1.7.8-main.ab4d3b4

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 (48) hide show
  1. package/lib/cjs/common/telemetry/TelemetryConstants.js +9 -0
  2. package/lib/cjs/components/chatbuttonstateful/ChatButtonStateful.js +4 -5
  3. package/lib/cjs/components/headerstateful/HeaderStateful.js +3 -5
  4. package/lib/cjs/components/livechatwidget/common/ActivitySubscriber/BotAuthActivitySubscriber.js +4 -1
  5. package/lib/cjs/components/livechatwidget/common/chatDisconnectHelper.js +3 -1
  6. package/lib/cjs/components/livechatwidget/common/endChat.js +4 -18
  7. package/lib/cjs/components/livechatwidget/common/startChat.js +2 -0
  8. package/lib/cjs/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +17 -11
  9. package/lib/cjs/components/ooohpanestateful/OOOHPaneStateful.js +6 -4
  10. package/lib/cjs/components/postchatsurveypanestateful/PostChatSurveyPaneStateful.js +22 -6
  11. package/lib/cjs/components/postchatsurveypanestateful/common/isValidSurveyUrl.js +28 -0
  12. package/lib/cjs/components/webchatcontainerstateful/WebChatContainerStateful.js +4 -3
  13. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/DeliveredTimestamp.js +4 -0
  14. package/lib/cjs/contexts/common/LiveChatWidgetContextInitialState.js +18 -6
  15. package/lib/cjs/firstresponselatency/Constants.js +13 -0
  16. package/lib/cjs/firstresponselatency/FirstMessageTrackerFromBot.js +118 -0
  17. package/lib/cjs/firstresponselatency/FirstResponseLatencyTracker.js +179 -0
  18. package/lib/cjs/firstresponselatency/util.js +98 -0
  19. package/lib/cjs/plugins/createChatTranscript.js +4 -4
  20. package/lib/cjs/plugins/newMessageEventHandler.js +102 -88
  21. package/lib/esm/common/telemetry/TelemetryConstants.js +9 -0
  22. package/lib/esm/components/chatbuttonstateful/ChatButtonStateful.js +4 -5
  23. package/lib/esm/components/headerstateful/HeaderStateful.js +3 -5
  24. package/lib/esm/components/livechatwidget/common/ActivitySubscriber/BotAuthActivitySubscriber.js +4 -1
  25. package/lib/esm/components/livechatwidget/common/chatDisconnectHelper.js +3 -1
  26. package/lib/esm/components/livechatwidget/common/endChat.js +4 -18
  27. package/lib/esm/components/livechatwidget/common/startChat.js +2 -0
  28. package/lib/esm/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +17 -11
  29. package/lib/esm/components/ooohpanestateful/OOOHPaneStateful.js +6 -4
  30. package/lib/esm/components/postchatsurveypanestateful/PostChatSurveyPaneStateful.js +22 -6
  31. package/lib/esm/components/postchatsurveypanestateful/common/isValidSurveyUrl.js +21 -0
  32. package/lib/esm/components/webchatcontainerstateful/WebChatContainerStateful.js +4 -3
  33. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/DeliveredTimestamp.js +4 -0
  34. package/lib/esm/contexts/common/LiveChatWidgetContextInitialState.js +18 -6
  35. package/lib/esm/firstresponselatency/Constants.js +6 -0
  36. package/lib/esm/firstresponselatency/FirstMessageTrackerFromBot.js +112 -0
  37. package/lib/esm/firstresponselatency/FirstResponseLatencyTracker.js +172 -0
  38. package/lib/esm/firstresponselatency/util.js +87 -0
  39. package/lib/esm/plugins/createChatTranscript.js +4 -4
  40. package/lib/esm/plugins/newMessageEventHandler.js +100 -86
  41. package/lib/types/common/telemetry/TelemetryConstants.d.ts +10 -1
  42. package/lib/types/components/postchatsurveypanestateful/common/isValidSurveyUrl.d.ts +2 -0
  43. package/lib/types/contexts/common/ILiveChatWidgetContext.d.ts +1 -1
  44. package/lib/types/firstresponselatency/Constants.d.ts +30 -0
  45. package/lib/types/firstresponselatency/FirstMessageTrackerFromBot.d.ts +1 -0
  46. package/lib/types/firstresponselatency/FirstResponseLatencyTracker.d.ts +22 -0
  47. package/lib/types/firstresponselatency/util.d.ts +7 -0
  48. package/package.json +13 -3
@@ -61,6 +61,8 @@ export let BroadcastEvent;
61
61
  BroadcastEvent["ContactIdNotFound"] = "ContactIdNotFound";
62
62
  BroadcastEvent["SyncMinimize"] = "SyncMinimize";
63
63
  BroadcastEvent["OnWidgetError"] = "OnWidgetError";
64
+ BroadcastEvent["FMLTrackingCompletedAck"] = "FMLTrackingCompletedAck";
65
+ BroadcastEvent["FMLTrackingCompleted"] = "FMLTrackingCompleted";
64
66
  })(BroadcastEvent || (BroadcastEvent = {}));
65
67
  export let TelemetryEvent;
66
68
  (function (TelemetryEvent) {
@@ -199,6 +201,11 @@ export let TelemetryEvent;
199
201
  TelemetryEvent["ReconnectChatMinimize"] = "ReconnectChatMinimize";
200
202
  TelemetryEvent["MessageSent"] = "MessageSent";
201
203
  TelemetryEvent["MessageReceived"] = "MessageReceived";
204
+ TelemetryEvent["MessageLapTrack"] = "MessageLapTrack";
205
+ TelemetryEvent["BotFirstMessageLapTrack"] = "BotFirstMessageLapTrackError";
206
+ TelemetryEvent["BotFirstMessageLapTrackError"] = "BotFirstMessageLapTrack";
207
+ TelemetryEvent["MessageStartLapTrackError"] = "MessageStartLapTrackError";
208
+ TelemetryEvent["MessageStopLapTrackError"] = "MessageStopLapTrackError";
202
209
  TelemetryEvent["SystemMessageReceived"] = "SystemMessageReceived";
203
210
  TelemetryEvent["RehydrateMessageReceived"] = "RehydrateMessageReceived";
204
211
  TelemetryEvent["CustomContextReceived"] = "CustomContextReceived";
@@ -213,6 +220,8 @@ export let TelemetryEvent;
213
220
  TelemetryEvent["PostChatContextCallFailed"] = "PostChatContextCallFailed";
214
221
  TelemetryEvent["PostChatSurveyLoadingPaneLoaded"] = "PostChatSurveyLoadingPaneLoaded";
215
222
  TelemetryEvent["PostChatSurveyLoaded"] = "PostChatSurveyLoaded";
223
+ TelemetryEvent["PostChatSurveyUrlValidationCompleted"] = "PostChatSurveyUrlValidationCompleted";
224
+ TelemetryEvent["PostChatSurveyUrlValidationFailed"] = "PostChatSurveyUrlValidationFailed";
216
225
  TelemetryEvent["ChatDisconnectThreadEventReceived"] = "ChatDisconnectThreadEventReceived";
217
226
  TelemetryEvent["HiddenAdaptiveCardMessageReceived"] = "HiddenAdaptiveCardMessageReceived";
218
227
  TelemetryEvent["EndingAdapterAfterDisconnectionError"] = "EndingAdapterAfterDisconnectionError";
@@ -11,7 +11,7 @@ import { defaultOutOfOfficeChatButtonStyleProps } from "./common/styleProps/defa
11
11
  import useChatContextStore from "../../hooks/useChatContextStore";
12
12
  let uiTimer;
13
13
  export const ChatButtonStateful = props => {
14
- var _state$domainStates$l, _state$domainStates$l2, _buttonProps$controlP, _props$buttonProps, _props$buttonProps$co, _props$buttonProps2, _props$buttonProps2$c, _props$buttonProps3, _props$buttonProps3$c;
14
+ var _buttonProps$controlP, _props$buttonProps, _props$buttonProps$co, _props$buttonProps2, _props$buttonProps2$c, _props$buttonProps3, _props$buttonProps3$c;
15
15
  // this is to ensure the telemetry is set only once and start the load timer
16
16
  useEffect(() => {
17
17
  uiTimer = createTimer();
@@ -28,7 +28,8 @@ export const ChatButtonStateful = props => {
28
28
  startChat
29
29
  } = props;
30
30
  //Setting OutOfOperatingHours Flag
31
- 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");
31
+ //Setting OutOfOperatingHours Flag - to string conversion to normalize the value (could be boolean from other states or string directly from config)
32
+ const [outOfOperatingHours, setOutOfOperatingHours] = useState(state.appStates.outsideOperatingHours);
32
33
  const ref = useRef(() => {
33
34
  return;
34
35
  });
@@ -87,9 +88,7 @@ export const ChatButtonStateful = props => {
87
88
  ...(outOfOfficeButtonProps === null || outOfOfficeButtonProps === void 0 ? void 0 : outOfOfficeButtonProps.controlProps)
88
89
  };
89
90
  useEffect(() => {
90
- if (state.appStates.outsideOperatingHours) {
91
- setOutOfOperatingHours(true);
92
- }
91
+ setOutOfOperatingHours(state.appStates.outsideOperatingHours);
93
92
  TelemetryHelper.logLoadingEvent(LogLevel.INFO, {
94
93
  Event: TelemetryEvent.LCWChatButtonShow,
95
94
  ElapsedTimeInMilliseconds: TelemetryTimers.LcwLoadToChatButtonTimer.milliSecondsElapsed
@@ -12,7 +12,7 @@ import useChatAdapterStore from "../../hooks/useChatAdapterStore";
12
12
  import useChatContextStore from "../../hooks/useChatContextStore";
13
13
  let uiTimer;
14
14
  export const HeaderStateful = props => {
15
- var _state$domainStates$l, _state$domainStates$l2, _state$domainStates, _headerProps$controlP, _headerProps$controlP2, _headerProps$controlP3, _outOfOfficeHeaderPro, _state$domainStates3;
15
+ var _state$domainStates, _headerProps$controlP, _headerProps$controlP2, _headerProps$controlP3, _outOfOfficeHeaderPro, _state$domainStates3;
16
16
  useEffect(() => {
17
17
  uiTimer = createTimer();
18
18
  TelemetryHelper.logLoadingEvent(LogLevel.INFO, {
@@ -28,7 +28,7 @@ export const HeaderStateful = props => {
28
28
  endChat
29
29
  } = props;
30
30
  //Setting OutOfOperatingHours Flag
31
- 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");
31
+ const [outOfOperatingHours, setOutOfOperatingHours] = useState(state.appStates.outsideOperatingHours);
32
32
  const outOfOfficeStyleProps = Object.assign({}, defaultOutOfOfficeHeaderStyleProps, outOfOfficeHeaderProps === null || outOfOfficeHeaderProps === void 0 ? void 0 : outOfOfficeHeaderProps.styleProps);
33
33
 
34
34
  // For some reason state object is not getting updated values in this component
@@ -100,9 +100,7 @@ export const HeaderStateful = props => {
100
100
  hideCloseButton: state.appStates.conversationState === ConversationState.OutOfOffice || (outOfOfficeHeaderProps === null || outOfOfficeHeaderProps === void 0 ? void 0 : (_outOfOfficeHeaderPro = outOfOfficeHeaderProps.controlProps) === null || _outOfOfficeHeaderPro === void 0 ? void 0 : _outOfOfficeHeaderPro.hideCloseButton)
101
101
  };
102
102
  useEffect(() => {
103
- if (state.appStates.outsideOperatingHours) {
104
- setOutOfOperatingHours(true);
105
- }
103
+ setOutOfOperatingHours(state.appStates.outsideOperatingHours);
106
104
  }, []);
107
105
  useEffect(() => {
108
106
  var _state$domainStates2;
@@ -6,6 +6,7 @@ function _toPrimitive(input, hint) { if (typeof input !== "object" || input ===
6
6
  import { BroadcastService } from "@microsoft/omnichannel-chat-components";
7
7
  import { BroadcastEvent, LogLevel, TelemetryEvent } from "../../../../common/telemetry/TelemetryConstants";
8
8
  import { TelemetryHelper } from "../../../../common/telemetry/TelemetryHelper";
9
+ import { TelemetryManager } from "../../../../common/telemetry/TelemetryManager";
9
10
  const supportedSignInCardContentTypes = ["application/vnd.microsoft.card.signin", "application/vnd.microsoft.card.oauth"];
10
11
  const botOauthUrlRegex = /[\S]+.botframework.com\/api\/oauth\/signin\?signin=([\S]+)/;
11
12
  const delay = t => new Promise(resolve => setTimeout(resolve, t));
@@ -83,6 +84,7 @@ export class BotAuthActivitySubscriber {
83
84
  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;
84
85
  }
85
86
  async apply(activity) {
87
+ var _TelemetryManager$Int;
86
88
  this.observer.next(false); // Hides card
87
89
  const attachment = activity.attachments[0];
88
90
  const signInUrl = attachment.content.buttons[0].value;
@@ -102,7 +104,8 @@ export class BotAuthActivitySubscriber {
102
104
  const event = {
103
105
  eventName: BroadcastEvent.SigninCardReceived,
104
106
  payload: {
105
- sasUrl
107
+ sasUrl,
108
+ conversationId: (_TelemetryManager$Int = TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int === void 0 ? void 0 : _TelemetryManager$Int.conversationId
106
109
  }
107
110
  };
108
111
  if (!sasUrl) {
@@ -6,7 +6,7 @@ import { defaultMiddlewareLocalizedTexts } from "../../webchatcontainerstateful/
6
6
 
7
7
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
8
  const handleChatDisconnect = (props, state, setWebChatStyles) => {
9
- var _state$appStates, _state$domainStates, _state$domainStates$m, _props$webChatContain, _props$webChatContain2;
9
+ var _state$appStates, _state$domainStates, _state$domainStates$m, _props$webChatContain, _props$webChatContain2, _props$webChatContain3, _props$webChatContain4;
10
10
  const chatDisconnectState = state === null || state === void 0 ? void 0 : (_state$appStates = state.appStates) === null || _state$appStates === void 0 ? void 0 : _state$appStates.chatDisconnectEventReceived;
11
11
  const chatDisconnectMessage = (state === null || state === void 0 ? void 0 : (_state$domainStates = state.domainStates) === null || _state$domainStates === void 0 ? void 0 : (_state$domainStates$m = _state$domainStates.middlewareLocalizedTexts) === null || _state$domainStates$m === void 0 ? void 0 : _state$domainStates$m.MIDDLEWARE_BANNER_CHAT_DISCONNECT) ?? defaultMiddlewareLocalizedTexts.MIDDLEWARE_BANNER_CHAT_DISCONNECT;
12
12
  const hideSendBoxOnConversationEnd = props === null || props === void 0 ? void 0 : (_props$webChatContain = props.webChatContainerProps) === null || _props$webChatContain === void 0 ? void 0 : (_props$webChatContain2 = _props$webChatContain.renderingMiddlewareProps) === null || _props$webChatContain2 === void 0 ? void 0 : _props$webChatContain2.hideSendboxOnConversationEnd;
@@ -27,6 +27,8 @@ const handleChatDisconnect = (props, state, setWebChatStyles) => {
27
27
  });
28
28
  break;
29
29
  case false:
30
+ // this means customer on purpose wants to hide the send box, we should not override it
31
+ if ((props === null || props === void 0 ? void 0 : (_props$webChatContain3 = props.webChatContainerProps) === null || _props$webChatContain3 === void 0 ? void 0 : (_props$webChatContain4 = _props$webChatContain3.webChatStyles) === null || _props$webChatContain4 === void 0 ? void 0 : _props$webChatContain4.hideSendBox) === true) return;
30
32
  if (hideSendBoxOnConversationEnd !== false) {
31
33
  setWebChatStyles(styles => {
32
34
  return {
@@ -203,7 +203,7 @@ const endChat = async (props, facadeChatSDK, state, dispatch, setAdapter, setWeb
203
203
  payload: undefined
204
204
  });
205
205
  // Always allow to close the chat for embedded mode irrespective of end chat errors
206
- closeChatWidget(dispatch, props, state);
206
+ closeChatWidget(dispatch);
207
207
  facadeChatSDK.destroy();
208
208
  }
209
209
  }
@@ -327,21 +327,7 @@ export const endVoiceVideoCallIfOngoing = async (facadeChatSDK, dispatch) => {
327
327
  }, callId);
328
328
  }
329
329
  };
330
- const closeChatWidget = (dispatch, props, state) => {
331
- var _state$appStates5;
332
- if (state !== null && state !== void 0 && (_state$appStates5 = state.appStates) !== null && _state$appStates5 !== void 0 && _state$appStates5.hideStartChatButton) {
333
- var _props$controlProps2, _props$controlProps3;
334
- // Only close chat if header is enabled for popout
335
- // TODO : This condition needs to be removed eventually when the filler UX is ready for popout, removing this condition would show a blank screen for OOB Widget
336
- if ((props === null || props === void 0 ? void 0 : (_props$controlProps2 = props.controlProps) === null || _props$controlProps2 === void 0 ? void 0 : _props$controlProps2.hideHeader) === undefined || (props === null || props === void 0 ? void 0 : (_props$controlProps3 = props.controlProps) === null || _props$controlProps3 === void 0 ? void 0 : _props$controlProps3.hideHeader) === false) {
337
- dispatch({
338
- type: LiveChatWidgetActionType.SET_CONVERSATION_STATE,
339
- payload: ConversationState.Closed
340
- });
341
- }
342
- return;
343
- }
344
-
330
+ const closeChatWidget = dispatch => {
345
331
  // Embedded chat
346
332
  dispatch({
347
333
  type: LiveChatWidgetActionType.SET_CONVERSATION_STATE,
@@ -361,7 +347,7 @@ const chatTokenCleanUp = async dispatch => {
361
347
  };
362
348
 
363
349
  const getEndChatEventName = async (facadeChatSDK, props) => {
364
- var _facadeChatSDK$getCha2, _facadeChatSDK$getCha3, _facadeChatSDK$getCha4, _facadeChatSDK$getCha5, _props$controlProps4;
365
- return getWidgetEndChatEventName((_facadeChatSDK$getCha2 = facadeChatSDK.getChatSDK()) === null || _facadeChatSDK$getCha2 === void 0 ? void 0 : (_facadeChatSDK$getCha3 = _facadeChatSDK$getCha2.omnichannelConfig) === null || _facadeChatSDK$getCha3 === void 0 ? void 0 : _facadeChatSDK$getCha3.orgId, (_facadeChatSDK$getCha4 = facadeChatSDK.getChatSDK()) === null || _facadeChatSDK$getCha4 === void 0 ? void 0 : (_facadeChatSDK$getCha5 = _facadeChatSDK$getCha4.omnichannelConfig) === null || _facadeChatSDK$getCha5 === void 0 ? void 0 : _facadeChatSDK$getCha5.widgetId, (props === null || props === void 0 ? void 0 : (_props$controlProps4 = props.controlProps) === null || _props$controlProps4 === void 0 ? void 0 : _props$controlProps4.widgetInstanceId) ?? "");
350
+ var _facadeChatSDK$getCha2, _facadeChatSDK$getCha3, _facadeChatSDK$getCha4, _facadeChatSDK$getCha5, _props$controlProps2;
351
+ return getWidgetEndChatEventName((_facadeChatSDK$getCha2 = facadeChatSDK.getChatSDK()) === null || _facadeChatSDK$getCha2 === void 0 ? void 0 : (_facadeChatSDK$getCha3 = _facadeChatSDK$getCha2.omnichannelConfig) === null || _facadeChatSDK$getCha3 === void 0 ? void 0 : _facadeChatSDK$getCha3.orgId, (_facadeChatSDK$getCha4 = facadeChatSDK.getChatSDK()) === null || _facadeChatSDK$getCha4 === void 0 ? void 0 : (_facadeChatSDK$getCha5 = _facadeChatSDK$getCha4.omnichannelConfig) === null || _facadeChatSDK$getCha5 === void 0 ? void 0 : _facadeChatSDK$getCha5.widgetId, (props === null || props === void 0 ? void 0 : (_props$controlProps2 = props.controlProps) === null || _props$controlProps2 === void 0 ? void 0 : _props$controlProps2.widgetInstanceId) ?? "");
366
352
  };
367
353
  export { prepareEndChat, endChat };
@@ -13,6 +13,7 @@ import { TelemetryTimers } from "../../../common/telemetry/TelemetryManager";
13
13
  import { chatSDKStateCleanUp } from "./endChat";
14
14
  import { createAdapter } from "./createAdapter";
15
15
  import { createOnNewAdapterActivityHandler } from "../../../plugins/newMessageEventHandler";
16
+ import { createTrackingForFirstMessage } from "../../../firstresponselatency/FirstMessageTrackerFromBot";
16
17
  import { isPersistentChatEnabled } from "./liveChatConfigUtils";
17
18
  import { setPostChatContextAndLoadSurvey } from "./setPostChatContextAndLoadSurvey";
18
19
  import { shouldSetPreChatIfPersistentChat } from "./persistentChatHelper";
@@ -145,6 +146,7 @@ const setPreChatAndInitiateChat = async (facadeChatSDK, dispatch, setAdapter, is
145
146
  const optionalParams = {
146
147
  isProactiveChat
147
148
  };
149
+ createTrackingForFirstMessage();
148
150
  await initStartChat(facadeChatSDK, dispatch, setAdapter, state, props, optionalParams);
149
151
  };
150
152
 
@@ -56,7 +56,7 @@ import useChatContextStore from "../../../hooks/useChatContextStore";
56
56
  import useFacadeSDKStore from "../../../hooks/useFacadeChatSDKStore";
57
57
  let uiTimer;
58
58
  export const LiveChatWidgetStateful = props => {
59
- var _props$webChatContain, _props$styleProps, _props$webChatContain2, _props$webChatContain3, _props$controlProps, _props$controlProps3, _state$appStates7, _props$webChatContain7, _state$appStates14, _props$webChatContain9, _props$webChatContain10, _props$controlProps12, _props$draggableChatW, _props$draggableChatW2, _props$draggableChatW3, _props$draggableChatW4, _props$draggableChatW5, _livechatProps$webCha, _props$webChatContain11, _props$webChatContain12, _props$webChatContain13, _props$webChatContain14, _livechatProps$styleP, _livechatProps$contro, _livechatProps$contro2, _livechatProps$compon, _livechatProps$contro3, _livechatProps$compon2, _livechatProps$contro4, _livechatProps$compon3, _livechatProps$contro5, _livechatProps$compon4, _livechatProps$contro6, _livechatProps$compon5, _livechatProps$contro7, _livechatProps$compon6, _livechatProps$contro8, _livechatProps$compon7, _livechatProps$contro9, _livechatProps$compon8, _livechatProps$contro10, _livechatProps$contro11, _livechatProps$compon9, _livechatProps$contro12, _livechatProps$compon10, _livechatProps$contro13, _livechatProps$compon11, _livechatProps$compon12, _livechatProps$compon13;
59
+ var _props$webChatContain, _props$styleProps, _props$webChatContain2, _props$webChatContain3, _props$controlProps, _props$controlProps3, _state$appStates7, _props$webChatContain7, _state$appStates14, _props$webChatContain9, _props$webChatContain10, _props$controlProps12, _props$draggableChatW, _props$draggableChatW2, _props$draggableChatW3, _props$draggableChatW4, _props$draggableChatW5, _livechatProps$webCha, _props$webChatContain11, _props$webChatContain12, _props$webChatContain13, _props$webChatContain14, _props$webChatContain15, _props$webChatContain16, _props$webChatContain17, _props$webChatContain18, _livechatProps$styleP, _livechatProps$contro, _livechatProps$contro2, _livechatProps$compon, _livechatProps$contro3, _livechatProps$compon2, _livechatProps$contro4, _livechatProps$compon3, _livechatProps$contro5, _livechatProps$compon4, _livechatProps$contro6, _livechatProps$compon5, _livechatProps$contro7, _livechatProps$compon6, _livechatProps$contro8, _livechatProps$compon7, _livechatProps$contro9, _livechatProps$compon8, _livechatProps$contro10, _livechatProps$contro11, _livechatProps$compon9, _livechatProps$contro12, _livechatProps$compon10, _livechatProps$contro13, _livechatProps$compon11, _livechatProps$compon12, _livechatProps$compon13;
60
60
  useEffect(() => {
61
61
  uiTimer = createTimer();
62
62
  TelemetryHelper.logLoadingEvent(LogLevel.INFO, {
@@ -409,7 +409,7 @@ export const LiveChatWidgetStateful = props => {
409
409
 
410
410
  // Start chat from SDK Event
411
411
  BroadcastService.getMessageByEventName(BroadcastEvent.StartChat).subscribe(msg => {
412
- var _props$chatConfig5, _props$chatConfig5$Li, _props$chatConfig6, _props$chatConfig6$Li, _msg$payload5, _msg$payload6, _msg$payload7, _msg$payload9, _inMemoryState$appSta2, _inMemoryState$appSta3, _inMemoryState$appSta4, _inMemoryState$appSta5;
412
+ var _props$chatConfig5, _props$chatConfig5$Li, _props$chatConfig6, _props$chatConfig6$Li, _msg$payload5, _msg$payload6, _msg$payload7, _msg$payload9, _inMemoryState$appSta2, _inMemoryState$appSta3;
413
413
  // If chat is out of operating hours chat widget sets the conversation state to OutOfOffice.
414
414
  if (typeof (props === null || props === void 0 ? void 0 : (_props$chatConfig5 = props.chatConfig) === null || _props$chatConfig5 === void 0 ? void 0 : (_props$chatConfig5$Li = _props$chatConfig5.LiveWSAndLiveChatEngJoin) === null || _props$chatConfig5$Li === void 0 ? void 0 : _props$chatConfig5$Li.OutOfOperatingHours) === "string" && (props === null || props === void 0 ? void 0 : (_props$chatConfig6 = props.chatConfig) === null || _props$chatConfig6 === void 0 ? void 0 : (_props$chatConfig6$Li = _props$chatConfig6.LiveWSAndLiveChatEngJoin) === null || _props$chatConfig6$Li === void 0 ? void 0 : _props$chatConfig6$Li.OutOfOperatingHours.toLowerCase()) === "true") {
415
415
  (state === null || state === void 0 ? void 0 : state.appStates.isMinimized) && dispatch({
@@ -447,8 +447,14 @@ export const LiveChatWidgetStateful = props => {
447
447
  });
448
448
  inMemoryState.domainStates.customContext = msg === null || msg === void 0 ? void 0 : (_msg$payload9 = msg.payload) === null || _msg$payload9 === void 0 ? void 0 : _msg$payload9.customContext;
449
449
 
450
- // Only initiate new chat if widget runtime state is one of the followings
451
- if (((_inMemoryState$appSta2 = inMemoryState.appStates) === null || _inMemoryState$appSta2 === void 0 ? void 0 : _inMemoryState$appSta2.conversationState) === ConversationState.Closed || ((_inMemoryState$appSta3 = inMemoryState.appStates) === null || _inMemoryState$appSta3 === void 0 ? void 0 : _inMemoryState$appSta3.conversationState) === ConversationState.InActive || ((_inMemoryState$appSta4 = inMemoryState.appStates) === null || _inMemoryState$appSta4 === void 0 ? void 0 : _inMemoryState$appSta4.conversationState) === ConversationState.Postchat) {
450
+ /*
451
+ * If the conversation is in closed state then we start a new chat,
452
+ * else if the conversation is in active state then we maximize the chat
453
+ * If the conversation is in inactive or postchat state then we maximize the chat.
454
+ *
455
+ * To start a new chat, it needs to be called via the close button or close chat via SDK.
456
+ **/
457
+ if (((_inMemoryState$appSta2 = inMemoryState.appStates) === null || _inMemoryState$appSta2 === void 0 ? void 0 : _inMemoryState$appSta2.conversationState) === ConversationState.Closed) {
452
458
  BroadcastService.postMessage({
453
459
  eventName: BroadcastEvent.ChatInitiated
454
460
  });
@@ -457,7 +463,7 @@ export const LiveChatWidgetStateful = props => {
457
463
  }
458
464
 
459
465
  // If minimized, maximize the chat
460
- if ((inMemoryState === null || inMemoryState === void 0 ? void 0 : (_inMemoryState$appSta5 = inMemoryState.appStates) === null || _inMemoryState$appSta5 === void 0 ? void 0 : _inMemoryState$appSta5.isMinimized) === true) {
466
+ if ((inMemoryState === null || inMemoryState === void 0 ? void 0 : (_inMemoryState$appSta3 = inMemoryState.appStates) === null || _inMemoryState$appSta3 === void 0 ? void 0 : _inMemoryState$appSta3.isMinimized) === true) {
461
467
  var _inMemoryState$domain, _inMemoryState$domain2, _inMemoryState$domain3, _inMemoryState$domain4;
462
468
  dispatch({
463
469
  type: LiveChatWidgetActionType.SET_MINIMIZED,
@@ -585,12 +591,12 @@ export const LiveChatWidgetStateful = props => {
585
591
  disablePolling: true
586
592
  });
587
593
  facadeChatSDK === null || facadeChatSDK === void 0 ? void 0 : facadeChatSDK.onAgentEndSession(event => {
588
- var _inMemoryState$appSta6;
594
+ var _inMemoryState$appSta4;
589
595
  const inMemoryState = executeReducer(state, {
590
596
  type: LiveChatWidgetActionType.GET_IN_MEMORY_STATE,
591
597
  payload: null
592
598
  });
593
- if ("participantsRemoved" in event && (inMemoryState === null || inMemoryState === void 0 ? void 0 : (_inMemoryState$appSta6 = inMemoryState.appStates) === null || _inMemoryState$appSta6 === void 0 ? void 0 : _inMemoryState$appSta6.conversationState) === ConversationState.Active) {
599
+ if ("participantsRemoved" in event && (inMemoryState === null || inMemoryState === void 0 ? void 0 : (_inMemoryState$appSta4 = inMemoryState.appStates) === null || _inMemoryState$appSta4 === void 0 ? void 0 : _inMemoryState$appSta4.conversationState) === ConversationState.Active) {
594
600
  setWebChatStyles(styles => {
595
601
  return {
596
602
  ...styles,
@@ -733,13 +739,13 @@ export const LiveChatWidgetStateful = props => {
733
739
 
734
740
  // Handle Chat disconnect cases
735
741
  useEffect(() => {
736
- var _inMemoryState$appSta7;
742
+ var _inMemoryState$appSta5;
737
743
  const inMemoryState = executeReducer(state, {
738
744
  type: LiveChatWidgetActionType.GET_IN_MEMORY_STATE,
739
745
  payload: null
740
746
  });
741
747
  handleChatDisconnect(props, inMemoryState, setWebChatStyles);
742
- const chatDisconnectState = inMemoryState === null || inMemoryState === void 0 ? void 0 : (_inMemoryState$appSta7 = inMemoryState.appStates) === null || _inMemoryState$appSta7 === void 0 ? void 0 : _inMemoryState$appSta7.chatDisconnectEventReceived;
748
+ const chatDisconnectState = inMemoryState === null || inMemoryState === void 0 ? void 0 : (_inMemoryState$appSta5 = inMemoryState.appStates) === null || _inMemoryState$appSta5 === void 0 ? void 0 : _inMemoryState$appSta5.chatDisconnectEventReceived;
743
749
  if (chatDisconnectState && adapter) {
744
750
  try {
745
751
  adapter.end();
@@ -868,8 +874,8 @@ export const LiveChatWidgetStateful = props => {
868
874
  userID: userID,
869
875
  styleOptions: {
870
876
  ...webChatStyles,
871
- bubbleBackground: ((_props$webChatContain11 = props.webChatContainerProps) === null || _props$webChatContain11 === void 0 ? void 0 : (_props$webChatContain12 = _props$webChatContain11.adaptiveCardStyles) === null || _props$webChatContain12 === void 0 ? void 0 : _props$webChatContain12.background) ?? defaultAdaptiveCardStyles.background,
872
- bubbleTextColor: ((_props$webChatContain13 = props.webChatContainerProps) === null || _props$webChatContain13 === void 0 ? void 0 : (_props$webChatContain14 = _props$webChatContain13.adaptiveCardStyles) === null || _props$webChatContain14 === void 0 ? void 0 : _props$webChatContain14.color) ?? defaultAdaptiveCardStyles.color
877
+ bubbleBackground: ((_props$webChatContain11 = props.webChatContainerProps) === null || _props$webChatContain11 === void 0 ? void 0 : (_props$webChatContain12 = _props$webChatContain11.webChatStyles) === null || _props$webChatContain12 === void 0 ? void 0 : _props$webChatContain12.bubbleBackground) ?? ((_props$webChatContain13 = props.webChatContainerProps) === null || _props$webChatContain13 === void 0 ? void 0 : (_props$webChatContain14 = _props$webChatContain13.adaptiveCardStyles) === null || _props$webChatContain14 === void 0 ? void 0 : _props$webChatContain14.background) ?? defaultAdaptiveCardStyles.background,
878
+ bubbleTextColor: ((_props$webChatContain15 = props.webChatContainerProps) === null || _props$webChatContain15 === void 0 ? void 0 : (_props$webChatContain16 = _props$webChatContain15.webChatStyles) === null || _props$webChatContain16 === void 0 ? void 0 : _props$webChatContain16.bubbleTextColor) ?? ((_props$webChatContain17 = props.webChatContainerProps) === null || _props$webChatContain17 === void 0 ? void 0 : (_props$webChatContain18 = _props$webChatContain17.adaptiveCardStyles) === null || _props$webChatContain18 === void 0 ? void 0 : _props$webChatContain18.color) ?? defaultAdaptiveCardStyles.color
873
879
  },
874
880
  directLine: directLine
875
881
  }), /*#__PURE__*/React.createElement(Stack, {
@@ -1,11 +1,11 @@
1
1
  import { LogLevel, TelemetryEvent } from "../../common/telemetry/TelemetryConstants";
2
2
  import React, { useEffect } from "react";
3
3
  import { createTimer, findAllFocusableElement } from "../../common/utils";
4
+ import DOMPurify from "dompurify";
4
5
  import { OutOfOfficeHoursPane } from "@microsoft/omnichannel-chat-components";
5
6
  import { TelemetryHelper } from "../../common/telemetry/TelemetryHelper";
6
7
  import { defaultGeneralStyleProps } from "./common/defaultStyleProps/defaultgeneralOOOHPaneStyleProps";
7
8
  import useChatContextStore from "../../hooks/useChatContextStore";
8
- import DOMPurify from "dompurify";
9
9
  let uiTimer;
10
10
  export const OutOfOfficeHoursPaneStateful = props => {
11
11
  var _props$styleProps;
@@ -29,9 +29,11 @@ export const OutOfOfficeHoursPaneStateful = props => {
29
29
 
30
30
  // Move focus to the first button
31
31
  useEffect(() => {
32
- const firstElement = findAllFocusableElement(`#${state.domainStates.widgetElementId}`);
33
- if (firstElement && firstElement[0]) {
34
- firstElement[0].focus();
32
+ if (state.domainStates.widgetElementId !== null && state.domainStates.widgetElementId !== undefined && state.domainStates.widgetElementId.trim() !== "") {
33
+ const firstElement = findAllFocusableElement(`#${state.domainStates.widgetElementId}`);
34
+ if (firstElement && firstElement[0]) {
35
+ firstElement[0].focus();
36
+ }
35
37
  }
36
38
  TelemetryHelper.logLoadingEvent(LogLevel.INFO, {
37
39
  Event: TelemetryEvent.OutOfOfficePaneLoaded
@@ -8,14 +8,16 @@ import { TelemetryHelper } from "../../common/telemetry/TelemetryHelper";
8
8
  import { defaultGeneralPostChatSurveyPaneStyleProps } from "./common/defaultStyleProps/defaultgeneralPostChatSurveyPaneStyleProps";
9
9
  import { findAllFocusableElement } from "../../common/utils";
10
10
  import useChatContextStore from "../../hooks/useChatContextStore";
11
+ import isValidSurveyUrl from "./common/isValidSurveyUrl";
11
12
  const generateSurveyInviteLink = function (surveyInviteLink, isEmbed, locale, compact) {
12
13
  let showMultiLingual = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
13
- const surveyLink = `${surveyInviteLink}
14
- &embed=${isEmbed.toString()}
15
- &compact=${compact.toString() ?? "true"}
16
- &lang=${locale ?? "en-us"}
17
- &showmultilingual=${showMultiLingual.toString() ?? "false"}`;
18
- return surveyLink;
14
+ const surveyLinkParams = new URLSearchParams({
15
+ embed: isEmbed.toString(),
16
+ compact: (compact ?? true).toString(),
17
+ lang: locale ?? "en-us",
18
+ showmultilingual: (showMultiLingual ?? false).toString()
19
+ });
20
+ return `${surveyInviteLink}&${surveyLinkParams.toString()}`;
19
21
  };
20
22
  export const PostChatSurveyPaneStateful = props => {
21
23
  var _props$styleProps, _state$appStates, _props$controlProps;
@@ -45,6 +47,20 @@ export const PostChatSurveyPaneStateful = props => {
45
47
  surveyURL: ((_props$controlProps = props.controlProps) === null || _props$controlProps === void 0 ? void 0 : _props$controlProps.surveyURL) ?? surveyInviteLink,
46
48
  ...props.controlProps
47
49
  };
50
+ if (controlProps.surveyURL) {
51
+ if (!isValidSurveyUrl(controlProps.surveyURL)) {
52
+ TelemetryHelper.logLoadingEvent(LogLevel.ERROR, {
53
+ Event: TelemetryEvent.PostChatSurveyUrlValidationFailed,
54
+ Description: `${controlProps.surveyURL} is not a valid Survey URL`
55
+ });
56
+ controlProps.surveyURL = "";
57
+ } else {
58
+ TelemetryHelper.logLoadingEvent(LogLevel.INFO, {
59
+ Event: TelemetryEvent.PostChatSurveyUrlValidationCompleted,
60
+ Description: `${controlProps.surveyURL} is a valid Survey URL`
61
+ });
62
+ }
63
+ }
48
64
 
49
65
  // Move focus to the first button
50
66
  useEffect(() => {
@@ -0,0 +1,21 @@
1
+ import { isNullOrEmptyString } from "../../../common/utils";
2
+ const validRootDomains = ["microsoft.com", "microsoft.us", "appsplatform.us", "powervirtualagents.cn"];
3
+ const isValidSurveyUrl = url => {
4
+ if (isNullOrEmptyString(url)) {
5
+ return false;
6
+ }
7
+ try {
8
+ const objectUrl = new URL(url);
9
+ if (!objectUrl.origin || objectUrl.origin === "null") {
10
+ return false;
11
+ }
12
+ const validDomain = validRootDomains.find(domain => objectUrl.origin.endsWith(domain));
13
+ if (validDomain) {
14
+ return true;
15
+ }
16
+ } catch (error) {
17
+ return false;
18
+ }
19
+ return false;
20
+ };
21
+ export default isValidSurveyUrl;
@@ -1,12 +1,12 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
 
3
+ import { Constants, HtmlAttributeNames, HtmlClassNames } from "../../common/Constants";
3
4
  import { Stack } from "@fluentui/react";
4
5
  import { LogLevel, TelemetryEvent } from "../../common/telemetry/TelemetryConstants";
5
6
  import React, { useEffect } from "react";
6
7
  import { createTimer, getDeviceType, setFocusOnSendBox } from "../../common/utils";
7
8
  import { BotMagicCodeStore } from "./webchatcontroller/BotMagicCodeStore";
8
9
  import { Components } from "botframework-webchat";
9
- import { Constants, HtmlAttributeNames, HtmlClassNames } from "../../common/Constants";
10
10
  import { LiveChatWidgetActionType } from "../../contexts/common/LiveChatWidgetActionType";
11
11
  import { NotificationHandler } from "./webchatcontroller/notification/NotificationHandler";
12
12
  import { NotificationScenarios } from "./webchatcontroller/enums/NotificationScenarios";
@@ -48,7 +48,7 @@ const createMagicCodeSuccessResponse = signin => {
48
48
  };
49
49
  };
50
50
  export const WebChatContainerStateful = props => {
51
- var _webChatContainerProp, _webChatContainerProp2, _webChatContainerProp3, _webChatContainerProp4, _webChatContainerProp5, _webChatContainerProp6, _webChatContainerProp7, _props$webChatContain5, _props$webChatContain6, _defaultWebChatContai, _props$webChatContain7, _props$webChatContain8, _defaultWebChatContai2, _webChatContainerProp8, _webChatContainerProp9, _webChatContainerProp10, _webChatContainerProp11, _webChatContainerProp12, _webChatContainerProp13, _webChatContainerProp14, _webChatContainerProp15, _props$webChatContain9, _props$webChatContain10;
51
+ var _webChatContainerProp, _webChatContainerProp2, _webChatContainerProp3, _webChatContainerProp4, _webChatContainerProp5, _webChatContainerProp6, _webChatContainerProp7, _props$webChatContain5, _props$webChatContain6, _defaultWebChatContai, _props$webChatContain7, _props$webChatContain8, _defaultWebChatContai2, _webChatContainerProp8, _webChatContainerProp9, _webChatContainerProp10, _webChatContainerProp11, _webChatContainerProp12, _webChatContainerProp13, _webChatContainerProp14, _webChatContainerProp15, _webChatContainerProp16, _props$webChatContain9, _props$webChatContain10;
52
52
  useEffect(() => {
53
53
  uiTimer = createTimer();
54
54
  TelemetryHelper.logLoadingEvent(LogLevel.INFO, {
@@ -233,8 +233,9 @@ export const WebChatContainerStateful = props => {
233
233
  color: ${(webChatContainerProps === null || webChatContainerProps === void 0 ? void 0 : (_webChatContainerProp14 = webChatContainerProps.renderingMiddlewareProps) === null || _webChatContainerProp14 === void 0 ? void 0 : (_webChatContainerProp15 = _webChatContainerProp14.sentMessageAnchorStyles) === null || _webChatContainerProp15 === void 0 ? void 0 : _webChatContainerProp15.color) ?? (defaultSentMessageAnchorStyles === null || defaultSentMessageAnchorStyles === void 0 ? void 0 : defaultSentMessageAnchorStyles.color)};
234
234
  }
235
235
 
236
+ // we had a nasty bug long time ago with crashing borders messing with the sendbox, so if customer adds this value, they need to deal with that
236
237
  .webchat__bubble:not(.webchat__bubble--from-user) .webchat__bubble__content {
237
- border-radius: 0 !important; /* Override border-radius */
238
+ border-radius: ${(webChatContainerProps === null || webChatContainerProps === void 0 ? void 0 : (_webChatContainerProp16 = webChatContainerProps.webChatStyles) === null || _webChatContainerProp16 === void 0 ? void 0 : _webChatContainerProp16.bubbleBorderRadius) ?? 0} !important; /* Override border-radius */
238
239
  }
239
240
 
240
241
  .webchat__stacked-layout_container>div {
@@ -35,6 +35,10 @@ export const DeliveredTimestamp = _ref => {
35
35
  return /*#__PURE__*/React.createElement("span", {
36
36
  dir: "ltr"
37
37
  }, getTimestampHourMinute(timestamp));
38
+ } else {
39
+ return /*#__PURE__*/React.createElement("span", {
40
+ dir: dir
41
+ }, getTimestampHourMinute(timestamp));
38
42
  }
39
43
  return timeString;
40
44
  };
@@ -1,11 +1,15 @@
1
- import { ConversationState } from "./ConversationState";
2
- import { defaultMiddlewareLocalizedTexts } from "../../components/webchatcontainerstateful/common/defaultProps/defaultMiddlewareLocalizedTexts";
3
- import { getWidgetCacheIdfromProps, isNullOrUndefined } from "../../common/utils";
4
- import { defaultClientDataStoreProvider } from "../../common/storage/default/defaultClientDataStoreProvider";
5
1
  import { ConfirmationState, Constants, ConversationEndEntity, StorageType } from "../../common/Constants";
2
+ import { getWidgetCacheIdfromProps, isNullOrUndefined } from "../../common/utils";
3
+ import { ConversationState } from "./ConversationState";
6
4
  import { StartChatFailureType } from "./StartChatFailureType";
5
+ import { defaultClientDataStoreProvider } from "../../common/storage/default/defaultClientDataStoreProvider";
6
+ import { defaultMiddlewareLocalizedTexts } from "../../components/webchatcontainerstateful/common/defaultProps/defaultMiddlewareLocalizedTexts";
7
7
  export const getLiveChatWidgetContextInitialState = props => {
8
8
  var _props$controlProps, _props$webChatContain;
9
+ const isOutsideOperatingHours = () => {
10
+ var _props$chatConfig, _props$chatConfig$Liv, _props$chatConfig$Liv2;
11
+ return ((_props$chatConfig = props.chatConfig) === null || _props$chatConfig === void 0 ? void 0 : (_props$chatConfig$Liv = _props$chatConfig.LiveWSAndLiveChatEngJoin) === null || _props$chatConfig$Liv === void 0 ? void 0 : (_props$chatConfig$Liv2 = _props$chatConfig$Liv.OutOfOperatingHours) === null || _props$chatConfig$Liv2 === void 0 ? void 0 : _props$chatConfig$Liv2.toString().toLowerCase()) === "true";
12
+ };
9
13
  const widgetCacheId = getWidgetCacheIdfromProps(props);
10
14
  const cacheTtlInMins = (props === null || props === void 0 ? void 0 : (_props$controlProps = props.controlProps) === null || _props$controlProps === void 0 ? void 0 : _props$controlProps.cacheTtlInMins) ?? Constants.CacheTtlInMinutes;
11
15
  const storageType = (props === null || props === void 0 ? void 0 : props.useSessionStorage) === true ? StorageType.sessionStorage : StorageType.localStorage;
@@ -22,6 +26,14 @@ export const getLiveChatWidgetContextInitialState = props => {
22
26
  if (initialStateFromCache.appStates.conversationState === ConversationState.Prechat) {
23
27
  initialStateFromCache.appStates.conversationState = ConversationState.Closed;
24
28
  }
29
+
30
+ // we are always setting the chatConfig from the props to avoid any issues with the cache
31
+ initialStateFromCache.domainStates.liveChatConfig = props.chatConfig;
32
+
33
+ // Cache the result of isOutsideOperatingHours() to ensure consistency
34
+ const outsideOperatingHours = isOutsideOperatingHours();
35
+ initialStateFromCache.appStates.outsideOperatingHours = outsideOperatingHours;
36
+ initialStateFromCache.appStates.conversationState = outsideOperatingHours ? ConversationState.OutOfOffice : initialStateFromCache.appStates.conversationState;
25
37
  return initialStateFromCache;
26
38
  }
27
39
  const LiveChatWidgetContextInitialState = {
@@ -46,11 +58,11 @@ export const getLiveChatWidgetContextInitialState = props => {
46
58
  startChatFailureType: StartChatFailureType.Generic
47
59
  },
48
60
  appStates: {
49
- conversationState: ConversationState.Closed,
61
+ conversationState: isOutsideOperatingHours() ? ConversationState.OutOfOffice : ConversationState.Closed,
50
62
  isMinimized: undefined,
51
63
  previousElementIdOnFocusBeforeModalOpen: null,
52
64
  startChatFailed: false,
53
- outsideOperatingHours: false,
65
+ outsideOperatingHours: isOutsideOperatingHours(),
54
66
  preChatResponseEmail: "",
55
67
  isAudioMuted: null,
56
68
  newMessage: false,
@@ -0,0 +1,6 @@
1
+ export let ScenarioType;
2
+ (function (ScenarioType) {
3
+ ScenarioType["UserSendMessageStrategy"] = "UserSendMessageStrategy";
4
+ ScenarioType["SystemMessageStrategy"] = "SystemMessageStrategy";
5
+ ScenarioType["ReceivedMessageStrategy"] = "ReceivedMessageStrategy";
6
+ })(ScenarioType || (ScenarioType = {}));
@@ -0,0 +1,112 @@
1
+ import { BroadcastEvent, LogLevel, TelemetryEvent } from "../common/telemetry/TelemetryConstants";
2
+ import { BroadcastService } from "@microsoft/omnichannel-chat-components";
3
+ import { TelemetryHelper } from "../common/telemetry/TelemetryHelper";
4
+ import { createTrackingMessage } from "./util";
5
+
6
+ // This tracker is event based, this is since we are tracking events coming from different sources
7
+ // with different timeline, therefore this is a functional approach to track the events, instead of a class based approach
8
+ export const createTrackingForFirstMessage = () => {
9
+ // Reset the tracking variables
10
+ let startTracking = false;
11
+ let stopTracking = false;
12
+ let startTime = 0;
13
+ let stopTime = 0;
14
+ let stopTrackingMessage;
15
+ let flag = false;
16
+ const isMessageFromValidSender = payload => {
17
+ var _payload$tags;
18
+ // agent scenario
19
+ if (payload !== null && payload !== void 0 && (_payload$tags = payload.tags) !== null && _payload$tags !== void 0 && _payload$tags.includes("public")) {
20
+ return false;
21
+ }
22
+ return true;
23
+ };
24
+ const widgetLoadListener = BroadcastService.getMessageByEventName(TelemetryEvent.WidgetLoadComplete).subscribe(() => {
25
+ if (startTracking) return;
26
+ startTracking = true;
27
+ startTime = new Date().getTime();
28
+ });
29
+ const newMessageListener = BroadcastService.getMessageByEventName(BroadcastEvent.NewMessageReceived).subscribe(message => {
30
+ const payload = message.payload;
31
+
32
+ // we only care for bot, so we need to check if the message is from the bot
33
+ // pending to add typing message indicator signal detection
34
+
35
+ if (isMessageFromValidSender(payload)) {
36
+ if (startTracking && !stopTracking) {
37
+ stopTime = new Date().getTime();
38
+ const elapsedTime = stopTime - startTime;
39
+ stopTracking = true;
40
+ stopTrackingMessage = createTrackingMessage(payload, "botMessage");
41
+ notifyFMLTrackingCompleted();
42
+ TelemetryHelper.logActionEvent(LogLevel.INFO, {
43
+ Event: TelemetryEvent.BotFirstMessageLapTrack,
44
+ Description: "First Message from Bot latency tracking",
45
+ CustomProperties: {
46
+ elapsedTime,
47
+ widgetLoadedAt: startTime,
48
+ botMessage: stopTrackingMessage
49
+ }
50
+ });
51
+ }
52
+ }
53
+
54
+ // this track only first message, if coming from the bot or not
55
+ // the only difference is that it logs only those from bot
56
+ disconnectListener();
57
+ });
58
+ const notifyFMLTrackingCompleted = () => {
59
+ ackListener();
60
+ // Retry sending until flag is true, but do not block the main thread
61
+ const interval = setInterval(() => {
62
+ if (flag) {
63
+ clearInterval(interval);
64
+ } else {
65
+ BroadcastService.postMessage({
66
+ eventName: BroadcastEvent.FMLTrackingCompleted,
67
+ payload: null
68
+ });
69
+ }
70
+ }, 100);
71
+ };
72
+ const ackListener = () => {
73
+ const listen = BroadcastService.getMessageByEventName(BroadcastEvent.FMLTrackingCompletedAck).subscribe(() => {
74
+ flag = true;
75
+ listen.unsubscribe();
76
+ });
77
+ };
78
+
79
+ // Rehydrate message is received when the widget is reloaded, this is to ensure that we are not tracking messages that are not part of the current conversation
80
+ // No need to keep listerning for tracking, enforcing disconnection for the listners
81
+ const rehydrateListener = BroadcastService.getMessageByEventName(TelemetryEvent.RehydrateMessageReceived).subscribe(() => {
82
+ startTracking = false;
83
+ stopTracking = false;
84
+ disconnectListener();
85
+ });
86
+
87
+ // Rehydrate message is received when the widget is reloaded, this is to ensure that we are not tracking messages that are not part of the current conversation
88
+ // No need to keep listerning for tracking, enforcing disconnection for the listners
89
+ const historyListener = BroadcastService.getMessageByEventName(BroadcastEvent.HistoryMessageReceived).subscribe(() => {
90
+ startTracking = false;
91
+ stopTracking = false;
92
+ disconnectListener();
93
+ });
94
+ const offlineNetworkListener = BroadcastService.getMessageByEventName(TelemetryEvent.NetworkDisconnected).subscribe(() => {
95
+ startTracking = false;
96
+ stopTracking = false;
97
+ disconnectListener();
98
+ TelemetryHelper.logActionEvent(LogLevel.INFO, {
99
+ Event: TelemetryEvent.BotFirstMessageLapTrackError,
100
+ Description: "Tracker Stopped due to network disconnection"
101
+ });
102
+ });
103
+
104
+ // this is to ensure that we are not tracking messages that are not part of the current conversation
105
+ const disconnectListener = () => {
106
+ historyListener.unsubscribe();
107
+ rehydrateListener.unsubscribe();
108
+ newMessageListener.unsubscribe();
109
+ widgetLoadListener.unsubscribe();
110
+ offlineNetworkListener.unsubscribe();
111
+ };
112
+ };