@microsoft/omnichannel-chat-widget 1.8.2-main.e3c1d40 → 1.8.2-main.fc93d3d

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 (44) hide show
  1. package/README.md +2 -0
  2. package/lib/cjs/common/Constants.js +9 -1
  3. package/lib/cjs/components/draggable/DraggableChatWidget.js +16 -1
  4. package/lib/cjs/components/livechatwidget/common/endChat.js +18 -7
  5. package/lib/cjs/components/livechatwidget/common/initWebChatComposer.js +29 -1
  6. package/lib/cjs/components/livechatwidget/common/liveChatConfigUtils.js +18 -1
  7. package/lib/cjs/components/livechatwidget/common/renderSurveyHelpers.js +31 -7
  8. package/lib/cjs/components/livechatwidget/common/setPostChatContextAndLoadSurvey.js +15 -2
  9. package/lib/cjs/components/livechatwidget/common/startChat.js +5 -3
  10. package/lib/cjs/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +22 -11
  11. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/channelDataMiddleware.js +4 -3
  12. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/conversationEndMiddleware.js +12 -6
  13. package/lib/cjs/contexts/common/LiveChatWidgetActionType.js +3 -1
  14. package/lib/cjs/contexts/common/LiveChatWidgetContextInitialState.js +3 -1
  15. package/lib/cjs/contexts/createReducer.js +30 -0
  16. package/lib/cjs/controller/componentController.js +2 -2
  17. package/lib/cjs/firstresponselatency/util.js +52 -27
  18. package/lib/cjs/plugins/newMessageEventHandler.js +12 -6
  19. package/lib/esm/common/Constants.js +7 -0
  20. package/lib/esm/components/draggable/DraggableChatWidget.js +16 -1
  21. package/lib/esm/components/livechatwidget/common/endChat.js +18 -7
  22. package/lib/esm/components/livechatwidget/common/initWebChatComposer.js +29 -1
  23. package/lib/esm/components/livechatwidget/common/liveChatConfigUtils.js +16 -0
  24. package/lib/esm/components/livechatwidget/common/renderSurveyHelpers.js +33 -9
  25. package/lib/esm/components/livechatwidget/common/setPostChatContextAndLoadSurvey.js +16 -3
  26. package/lib/esm/components/livechatwidget/common/startChat.js +5 -3
  27. package/lib/esm/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +22 -11
  28. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/channelDataMiddleware.js +4 -4
  29. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/conversationEndMiddleware.js +12 -6
  30. package/lib/esm/contexts/common/LiveChatWidgetActionType.js +3 -1
  31. package/lib/esm/contexts/common/LiveChatWidgetContextInitialState.js +3 -1
  32. package/lib/esm/contexts/createReducer.js +30 -0
  33. package/lib/esm/controller/componentController.js +2 -2
  34. package/lib/esm/firstresponselatency/util.js +49 -25
  35. package/lib/esm/plugins/newMessageEventHandler.js +12 -6
  36. package/lib/types/common/Constants.d.ts +6 -0
  37. package/lib/types/components/livechatwidget/common/liveChatConfigUtils.d.ts +1 -0
  38. package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/channelDataMiddleware.d.ts +1 -1
  39. package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/conversationEndMiddleware.d.ts +1 -1
  40. package/lib/types/contexts/common/ILiveChatWidgetContext.d.ts +2 -0
  41. package/lib/types/contexts/common/LiveChatWidgetActionType.d.ts +3 -1
  42. package/lib/types/firstresponselatency/util.d.ts +17 -0
  43. package/lib/types/plugins/newMessageEventHandler.d.ts +1 -1
  44. package/package.json +3 -3
@@ -27,7 +27,7 @@ const shouldShowEmailTranscriptPane = state => {
27
27
  };
28
28
  exports.shouldShowEmailTranscriptPane = shouldShowEmailTranscriptPane;
29
29
  const shouldShowWebChatContainer = state => {
30
- return !state.appStates.isMinimized && state.appStates.conversationState === _ConversationState.ConversationState.Active || state.appStates.conversationState === _ConversationState.ConversationState.InActive;
30
+ return !state.appStates.isMinimized && state.appStates.conversationState === _ConversationState.ConversationState.Active || state.appStates.conversationState === _ConversationState.ConversationState.InActive || state.appStates.conversationState === _ConversationState.ConversationState.Postchat && state.appStates.isConversationalSurveyEnabled && state.appStates.isConversationalSurvey;
31
31
  };
32
32
  exports.shouldShowWebChatContainer = shouldShowWebChatContainer;
33
33
  const shouldShowLoadingPane = state => {
@@ -60,7 +60,7 @@ const shouldShowConfirmationPane = state => {
60
60
  };
61
61
  exports.shouldShowConfirmationPane = shouldShowConfirmationPane;
62
62
  const shouldShowPostChatSurveyPane = state => {
63
- return state.appStates.conversationState === _ConversationState.ConversationState.Postchat;
63
+ return state.appStates.conversationState === _ConversationState.ConversationState.Postchat && !state.appStates.isConversationalSurvey;
64
64
  };
65
65
  exports.shouldShowPostChatSurveyPane = shouldShowPostChatSurveyPane;
66
66
  const shouldShowCallingContainer = state => {
@@ -3,41 +3,66 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.polyfillMessagePayloadForEvent = exports.isHistoryMessage = exports.getScenarioType = exports.createTrackingMessage = exports.buildMessagePayload = void 0;
6
+ exports.polyfillMessagePayloadForEvent = exports.isHistoryMessage = exports.getScenarioType = exports.extractTimestampFromId = exports.createTrackingMessage = exports.buildMessagePayload = void 0;
7
7
  var _Constants = require("./Constants");
8
8
  var _Constants2 = require("../common/Constants");
9
+ /**
10
+ * Determines whether a given activity is a historical message.
11
+ *
12
+ * This function checks if the activity is a message type and uses a combination
13
+ * of legacy tags and timestamp-based logic to determine if the message is historical.
14
+ *
15
+ * @param {IActivity} activity - The activity object to evaluate.
16
+ * @param {number} startTime - The start time (in milliseconds since epoch) to compare against.
17
+ * @returns {boolean} - Returns true if the activity is a historical message, false otherwise.
18
+ *
19
+ * Logic:
20
+ * - If the activity type is not a message, it is not historical.
21
+ * - If the activity contains a legacy history message tag, it is considered historical.
22
+ * - Otherwise, the function extracts a timestamp from the activity ID using `extractTimestampFromId`.
23
+ * - If the ID is valid and the timestamp is older than the start time, the message is historical.
24
+ */
9
25
  const isHistoryMessage = (activity, startTime) => {
10
- try {
11
- if ((activity === null || activity === void 0 ? void 0 : activity.type) === _Constants2.Constants.message) {
12
- var _activity$channelData, _activity$channelData2;
13
- // this is an old piece of code, probably no longer relevant
14
- if (activity !== null && activity !== void 0 && (_activity$channelData = activity.channelData) !== null && _activity$channelData !== void 0 && (_activity$channelData2 = _activity$channelData.tags) !== null && _activity$channelData2 !== void 0 && _activity$channelData2.includes(_Constants2.Constants.historyMessageTag)) return true;
26
+ var _activity$channelData, _activity$channelData2;
27
+ // Only process message activities
28
+ if ((activity === null || activity === void 0 ? void 0 : activity.type) !== _Constants2.Constants.message) {
29
+ return false;
30
+ }
15
31
 
16
- // Id is an epoch time in milliseconds , in utc format, for some reason is in a string format
17
- if (activity !== null && activity !== void 0 && activity.id) {
18
- /// activity.id is an string that contains epoch time in milliseconds
19
- const activityId = parseInt(activity === null || activity === void 0 ? void 0 : activity.id);
32
+ // Legacy check for history message tag
33
+ if (activity !== null && activity !== void 0 && (_activity$channelData = activity.channelData) !== null && _activity$channelData !== void 0 && (_activity$channelData2 = _activity$channelData.tags) !== null && _activity$channelData2 !== void 0 && _activity$channelData2.includes(_Constants2.Constants.historyMessageTag)) {
34
+ return true;
35
+ }
36
+ const activityId = extractTimestampFromId(activity);
37
+ const isValidId = !isNaN(activityId) && activityId > 0;
38
+ const isOlderThanStartTime = activityId < startTime;
39
+ const isHistoryById = isValidId && isOlderThanStartTime;
40
+ return isHistoryById;
41
+ };
42
+ exports.isHistoryMessage = isHistoryMessage;
43
+ const extractTimestampFromId = activity => {
44
+ const id = (activity === null || activity === void 0 ? void 0 : activity.id) ?? "";
20
45
 
21
- // if the activity id is not a number, we default to new message
22
- if (isNaN(activityId)) {
23
- return false;
24
- }
46
+ // Helper function to get timestamp fallback
47
+ const getTimestampFallback = () => {
48
+ const timestamp = new Date((activity === null || activity === void 0 ? void 0 : activity.timestamp) ?? "").getTime();
49
+ return isNaN(timestamp) ? 0 : timestamp;
50
+ };
25
51
 
26
- // if the activity id is less than the start time, it means that the message is a history message
27
- if (activityId < startTime) {
28
- return true;
29
- }
30
- }
31
- // anything else will be considered a new message
32
- return false;
33
- }
34
- } catch (e) {
35
- // if there is an error in parsing the activity id, we will consider it a new message
36
- console.error("Error in parsing activity id: ", e);
52
+ // Check if ID looks like a UUID (contains dashes or is very long)
53
+ const UUID_LENGTH_THRESHOLD = 13; // Threshold to distinguish UUIDs from epoch timestamps
54
+ if (id.includes("-") || id.length > UUID_LENGTH_THRESHOLD) {
55
+ // Likely UUID, use timestamp instead
56
+ return getTimestampFallback();
57
+ }
58
+ const activityId = parseInt(id);
59
+ // if activity id is not a number, then we use timestamp field
60
+ if (isNaN(activityId)) {
61
+ return getTimestampFallback();
37
62
  }
38
- return false;
63
+ return activityId;
39
64
  };
40
- exports.isHistoryMessage = isHistoryMessage;
65
+ exports.extractTimestampFromId = extractTimestampFromId;
41
66
  const buildMessagePayload = (activity, userId) => {
42
67
  var _text, _text2, _activity$channelData3, _activity$from;
43
68
  return {
@@ -12,13 +12,13 @@ var _Constants2 = require("../common/Constants");
12
12
  var _FirstResponseLatencyTracker = require("../firstresponselatency/FirstResponseLatencyTracker");
13
13
  var _TelemetryHelper = require("../common/telemetry/TelemetryHelper");
14
14
  var _TelemetryManager = require("../common/telemetry/TelemetryManager");
15
- const createOnNewAdapterActivityHandler = (chatId, userId) => {
15
+ const createOnNewAdapterActivityHandler = (chatId, userId, startTime) => {
16
16
  // Hooking the message tracker in the listener, a bit invasive but easier to control.
17
17
  const firstResponseLatencyTracker = new _FirstResponseLatencyTracker.FirstResponseLatencyTracker();
18
18
  // epoch time in utc for when start to listen.
19
19
  // We dont longer have a mechanism to know if a message is history or new, so any message older than the time we start listening will be considered a history message.
20
20
  // this is a workaround for the fact that we dont have a way to identify if a message is history or new, and it will provide consistency across different scenarios
21
- const startTime = new Date().getTime();
21
+
22
22
  let isHistoryMessageReceivedEventRaised = false;
23
23
  const onNewAdapterActivityHandler = activity => {
24
24
  raiseMessageEvent(activity);
@@ -41,6 +41,7 @@ const createOnNewAdapterActivityHandler = (chatId, userId) => {
41
41
  });
42
42
  };
43
43
  const systemMessageStrategy = activity => {
44
+ var _TelemetryManager$Int3;
44
45
  const payload = (0, _util.buildMessagePayload)(activity, userId);
45
46
  payload.messageType = _Constants2.Constants.systemMessageTag;
46
47
  if ((0, _util.isHistoryMessage)(activity, startTime)) {
@@ -48,6 +49,11 @@ const createOnNewAdapterActivityHandler = (chatId, userId) => {
48
49
  historyMessageStrategy((0, _util.polyfillMessagePayloadForEvent)(activity, payload, (_TelemetryManager$Int2 = _TelemetryManager.TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int2 === void 0 ? void 0 : _TelemetryManager$Int2.conversationId));
49
50
  return;
50
51
  }
52
+ const newMessageReceivedEvent = {
53
+ eventName: _TelemetryConstants.BroadcastEvent.NewMessageReceived,
54
+ payload: (0, _util.polyfillMessagePayloadForEvent)(activity, payload, (_TelemetryManager$Int3 = _TelemetryManager.TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int3 === void 0 ? void 0 : _TelemetryManager$Int3.conversationId)
55
+ };
56
+ _omnichannelChatComponents.BroadcastService.postMessage(newMessageReceivedEvent);
51
57
  _TelemetryHelper.TelemetryHelper.logActionEventToAllTelemetry(_TelemetryConstants.LogLevel.INFO, {
52
58
  Event: _TelemetryConstants.TelemetryEvent.SystemMessageReceived,
53
59
  Description: "System message received"
@@ -83,20 +89,20 @@ const createOnNewAdapterActivityHandler = (chatId, userId) => {
83
89
  return true;
84
90
  };
85
91
  const receivedMessageStrategy = activity => {
86
- var _TelemetryManager$Int4;
92
+ var _TelemetryManager$Int5;
87
93
  if (!isValidMessage(activity)) return;
88
94
  const isHistoryMessageReceived = (0, _util.isHistoryMessage)(activity, startTime);
89
95
  const payload = (0, _util.buildMessagePayload)(activity, userId);
90
96
  payload.messageType = _Constants2.Constants.userMessageTag;
91
97
  if (isHistoryMessageReceived) {
92
- var _TelemetryManager$Int3;
93
- historyMessageStrategy((0, _util.polyfillMessagePayloadForEvent)(activity, payload, (_TelemetryManager$Int3 = _TelemetryManager.TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int3 === void 0 ? void 0 : _TelemetryManager$Int3.conversationId));
98
+ var _TelemetryManager$Int4;
99
+ historyMessageStrategy((0, _util.polyfillMessagePayloadForEvent)(activity, payload, (_TelemetryManager$Int4 = _TelemetryManager.TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int4 === void 0 ? void 0 : _TelemetryManager$Int4.conversationId));
94
100
  return;
95
101
  }
96
102
  firstResponseLatencyTracker.stopClock(payload);
97
103
  const newMessageReceivedEvent = {
98
104
  eventName: _TelemetryConstants.BroadcastEvent.NewMessageReceived,
99
- payload: (0, _util.polyfillMessagePayloadForEvent)(activity, payload, (_TelemetryManager$Int4 = _TelemetryManager.TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int4 === void 0 ? void 0 : _TelemetryManager$Int4.conversationId)
105
+ payload: (0, _util.polyfillMessagePayloadForEvent)(activity, payload, (_TelemetryManager$Int5 = _TelemetryManager.TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int5 === void 0 ? void 0 : _TelemetryManager$Int5.conversationId)
100
106
  };
101
107
  _omnichannelChatComponents.BroadcastService.postMessage(newMessageReceivedEvent);
102
108
  _TelemetryHelper.TelemetryHelper.logActionEventToAllTelemetry(_TelemetryConstants.LogLevel.INFO, {
@@ -16,6 +16,9 @@ _defineProperty(Constants, "channelMessageTag", "channel");
16
16
  _defineProperty(Constants, "historyMessageTag", "history");
17
17
  _defineProperty(Constants, "agentEndConversationMessageTag", "agentendconversation");
18
18
  _defineProperty(Constants, "supervisorForceCloseMessageTag", "supervisorforceclosedconversation");
19
+ _defineProperty(Constants, "endConversationalSurveyMessageTag", "endconversationalsurvey");
20
+ _defineProperty(Constants, "startConversationalSurveyMessageTag", "startconversationalsurvey");
21
+ _defineProperty(Constants, "c2ConversationalSurveyMessageTag", "c2conversationalsurvey");
19
22
  _defineProperty(Constants, "receivedMessageClassName", "ms_lcw_webchat_received_message");
20
23
  _defineProperty(Constants, "sentMessageClassName", "ms_lcw_webchat_sent_message");
21
24
  _defineProperty(Constants, "webchatChannelId", "webchat");
@@ -220,6 +223,10 @@ export let ConversationMode;
220
223
  ConversationMode["Regular"] = "192350000";
221
224
  ConversationMode["Persistent"] = "192350001";
222
225
  })(ConversationMode || (ConversationMode = {}));
226
+ export let SurveyProvider;
227
+ (function (SurveyProvider) {
228
+ SurveyProvider["MicrosoftCopilotStudio"] = "600990001";
229
+ })(SurveyProvider || (SurveyProvider = {}));
223
230
  export let LiveWorkItemState;
224
231
  (function (LiveWorkItemState) {
225
232
  LiveWorkItemState["Active"] = "Active";
@@ -25,8 +25,11 @@ const DraggableChatWidget = props => {
25
25
  };
26
26
  const calculateOffsetsWithinViewport = useCallback((id, offset, delta) => {
27
27
  const draggableElement = document.getElementById(id);
28
+ if (isNullOrUndefined(draggableElement)) {
29
+ return;
30
+ }
28
31
  const positionRelativeToViewport = draggableElement.getBoundingClientRect();
29
- if (isNullOrUndefined(draggableElement) || isNullOrUndefined(positionRelativeToViewport) || isNullOrUndefined(offset.offsetLeft) || isNullOrUndefined(offset.offsetTop)) {
32
+ if (isNullOrUndefined(positionRelativeToViewport) || isNullOrUndefined(offset.offsetLeft) || isNullOrUndefined(offset.offsetTop)) {
30
33
  return;
31
34
  }
32
35
  let offsetLeft = offset.offsetLeft;
@@ -68,6 +71,9 @@ const DraggableChatWidget = props => {
68
71
  }
69
72
  const cacheInitialPosition = () => {
70
73
  const draggableElement = document.getElementById(props.elementId);
74
+ if (isNullOrUndefined(draggableElement)) {
75
+ return;
76
+ }
71
77
  const offsetLeft = draggableElement.offsetLeft;
72
78
  const offsetTop = draggableElement.offsetTop;
73
79
  setInitialPosition({
@@ -77,6 +83,9 @@ const DraggableChatWidget = props => {
77
83
  };
78
84
  const calculateOffsets = () => {
79
85
  const draggableElement = document.getElementById(props.elementId);
86
+ if (isNullOrUndefined(draggableElement)) {
87
+ return;
88
+ }
80
89
  const offsetLeft = draggableElement.offsetLeft;
81
90
  const offsetTop = draggableElement.offsetTop;
82
91
 
@@ -111,6 +120,9 @@ const DraggableChatWidget = props => {
111
120
  resetPosition(initialPosition);
112
121
  } else if (state.appStates.isMinimized) {
113
122
  const draggableElement = document.getElementById(props.elementId);
123
+ if (isNullOrUndefined(draggableElement)) {
124
+ return;
125
+ }
114
126
  const offsetLeft = draggableElement.offsetLeft;
115
127
  const offsetTop = draggableElement.offsetTop;
116
128
  if (!cachedPosition) {
@@ -135,6 +147,9 @@ const DraggableChatWidget = props => {
135
147
 
136
148
  // Update position via DOM manipulation to prevent <Stack/> continuously rendering on style change causing high CPU spike
137
149
  const draggableElement = document.getElementById(props.elementId);
150
+ if (isNullOrUndefined(draggableElement)) {
151
+ return;
152
+ }
138
153
  repositionElement(draggableElement, offsetLeft, offsetTop);
139
154
  setPosition({
140
155
  offsetLeft,
@@ -16,7 +16,7 @@ import { uuidv4 } from "@microsoft/omnichannel-chat-sdk";
16
16
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
17
  const prepareEndChat = async (props, facadeChatSDK, state, dispatch, setAdapter, setWebChatStyles, adapter) => {
18
18
  try {
19
- var _conversationDetails$, _state$domainStates, _state$appStates3;
19
+ var _conversationDetails$, _state$domainStates, _state$appStates5;
20
20
  const {
21
21
  chatConfig
22
22
  } = props;
@@ -27,7 +27,7 @@ const prepareEndChat = async (props, facadeChatSDK, state, dispatch, setAdapter,
27
27
 
28
28
  // Use Case: When post chat is not configured
29
29
  if ((conversationDetails === null || conversationDetails === void 0 ? void 0 : (_conversationDetails$ = conversationDetails.canRenderPostChat) === null || _conversationDetails$ === void 0 ? void 0 : _conversationDetails$.toLowerCase()) === Constants.false) {
30
- var _state$appStates;
30
+ var _state$appStates, _state$appStates2, _state$appStates3;
31
31
  // If ended by customer, just close chat
32
32
  if ((state === null || state === void 0 ? void 0 : (_state$appStates = state.appStates) === null || _state$appStates === void 0 ? void 0 : _state$appStates.conversationEndedBy) === ConversationEndEntity.Customer) {
33
33
  TelemetryHelper.logSDKEvent(LogLevel.INFO, {
@@ -38,6 +38,13 @@ const prepareEndChat = async (props, facadeChatSDK, state, dispatch, setAdapter,
38
38
  }
39
39
 
40
40
  // Use Case: If ended by Agent, stay chat in InActive state
41
+ let isConversationalSurveyEnabled = state.appStates.isConversationalSurveyEnabled;
42
+ if (isConversationalSurveyEnabled && ((state === null || state === void 0 ? void 0 : (_state$appStates2 = state.appStates) === null || _state$appStates2 === void 0 ? void 0 : _state$appStates2.conversationEndedBy) === ConversationEndEntity.Agent || (state === null || state === void 0 ? void 0 : (_state$appStates3 = state.appStates) === null || _state$appStates3 === void 0 ? void 0 : _state$appStates3.conversationEndedBy) === ConversationEndEntity.Bot)) {
43
+ dispatch({
44
+ type: LiveChatWidgetActionType.SET_CONVERSATION_STATE,
45
+ payload: ConversationState.InActive
46
+ });
47
+ }
41
48
  return;
42
49
  }
43
50
 
@@ -53,7 +60,7 @@ const prepareEndChat = async (props, facadeChatSDK, state, dispatch, setAdapter,
53
60
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
54
61
  const postchatContext = (await getPostChatContext(facadeChatSDK, state, dispatch)) ?? (state === null || state === void 0 ? void 0 : (_state$domainStates = state.domainStates) === null || _state$domainStates === void 0 ? void 0 : _state$domainStates.postChatContext);
55
62
  if (postchatContext === undefined) {
56
- var _state$appStates2;
63
+ var _state$appStates4;
57
64
  BroadcastService.postMessage({
58
65
  eventName: BroadcastEvent.OnWidgetError,
59
66
  payload: {
@@ -62,7 +69,7 @@ const prepareEndChat = async (props, facadeChatSDK, state, dispatch, setAdapter,
62
69
  });
63
70
 
64
71
  // For Customer intiated conversations, just close chat widget
65
- if ((state === null || state === void 0 ? void 0 : (_state$appStates2 = state.appStates) === null || _state$appStates2 === void 0 ? void 0 : _state$appStates2.conversationEndedBy) === ConversationEndEntity.Customer) {
72
+ if ((state === null || state === void 0 ? void 0 : (_state$appStates4 = state.appStates) === null || _state$appStates4 === void 0 ? void 0 : _state$appStates4.conversationEndedBy) === ConversationEndEntity.Customer) {
66
73
  TelemetryHelper.logSDKEvent(LogLevel.INFO, {
67
74
  Event: TelemetryEvent.PrepareEndChat,
68
75
  Description: PrepareEndChatDescriptionConstants.ConversationEndedByCustomerWithInvalidPostChat
@@ -80,11 +87,11 @@ const prepareEndChat = async (props, facadeChatSDK, state, dispatch, setAdapter,
80
87
  }
81
88
 
82
89
  // Log PrepareEndChat if conversation ended by customer (bot and agent cases are handled in LiveChatWidgetStateful.tsx)
83
- if (state !== null && state !== void 0 && (_state$appStates3 = state.appStates) !== null && _state$appStates3 !== void 0 && _state$appStates3.conversationEndedBy) {
84
- var _state$appStates4;
90
+ if (state !== null && state !== void 0 && (_state$appStates5 = state.appStates) !== null && _state$appStates5 !== void 0 && _state$appStates5.conversationEndedBy) {
91
+ var _state$appStates6;
85
92
  TelemetryHelper.logSDKEvent(LogLevel.INFO, {
86
93
  Event: TelemetryEvent.PrepareEndChat,
87
- Description: `${PrepareEndChatDescriptionConstants.ConversationEndedByCustomerWithInvalidPostChat} ${state === null || state === void 0 ? void 0 : (_state$appStates4 = state.appStates) === null || _state$appStates4 === void 0 ? void 0 : _state$appStates4.conversationEndedBy}.`
94
+ Description: `${PrepareEndChatDescriptionConstants.ConversationEndedByCustomerWithInvalidPostChat} ${state === null || state === void 0 ? void 0 : (_state$appStates6 = state.appStates) === null || _state$appStates6 === void 0 ? void 0 : _state$appStates6.conversationEndedBy}.`
88
95
  });
89
96
  }
90
97
  const persistentEnabled = isPersistentEnabled(chatConfig);
@@ -256,6 +263,10 @@ export const closeChatStateCleanUp = dispatch => {
256
263
  payload: undefined
257
264
  });
258
265
  // dispatch({ type: LiveChatWidgetActionType.SET_CONVERSATION_STATE, payload: ConversationState.Closed });
266
+ dispatch({
267
+ type: LiveChatWidgetActionType.SET_CONVERSATIONAL_SURVEY_DISPLAY,
268
+ payload: false
269
+ });
259
270
  dispatch({
260
271
  type: LiveChatWidgetActionType.SET_RECONNECT_ID,
261
272
  payload: undefined
@@ -32,6 +32,8 @@ import htmlTextMiddleware from "../../webchatcontainerstateful/webchatcontroller
32
32
  import preProcessingMiddleware from "../../webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/preProcessingMiddleware";
33
33
  import sanitizationMiddleware from "../../webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/sanitizationMiddleware";
34
34
  import { Constants } from "../../../common/Constants";
35
+ import { ConversationState } from "../../../contexts/common/ConversationState";
36
+ import { executeReducer } from "../../../contexts/createReducer";
35
37
 
36
38
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
37
39
  export const initWebChatComposer = (props, state, dispatch, facadeChatSDK, endChat) => {
@@ -53,6 +55,20 @@ export const initWebChatComposer = (props, state, dispatch, facadeChatSDK, endCh
53
55
  let webChatStore = WebChatStoreLoader.store;
54
56
  if (!webChatStore) {
55
57
  var _state$domainStates$l, _state$domainStates$l2, _state$domainStates$l3, _props$webChatContain7;
58
+ const addConversationalSurveyTagsCallback = action => {
59
+ var _inMemoryState$appSta;
60
+ const inMemoryState = executeReducer(state, {
61
+ type: LiveChatWidgetActionType.GET_IN_MEMORY_STATE,
62
+ payload: null
63
+ });
64
+ const isConversationalSurvey = (_inMemoryState$appSta = inMemoryState.appStates) === null || _inMemoryState$appSta === void 0 ? void 0 : _inMemoryState$appSta.isConversationalSurvey;
65
+ if (isConversationalSurvey) {
66
+ if (!action.payload.activity.channelData.tags.includes(Constants.c2ConversationalSurveyMessageTag)) {
67
+ action.payload.activity.channelData.tags.push(Constants.c2ConversationalSurveyMessageTag);
68
+ }
69
+ }
70
+ return action;
71
+ };
56
72
  const conversationEndCallback = async () => {
57
73
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
74
  const conversationDetails = await getConversationDetailsCall(facadeChatSDK);
@@ -82,9 +98,21 @@ export const initWebChatComposer = (props, state, dispatch, facadeChatSDK, endCh
82
98
  });
83
99
  }
84
100
  };
101
+ const startConversationalSurveyCallback = async () => {
102
+ dispatch({
103
+ type: LiveChatWidgetActionType.SET_CONVERSATIONAL_SURVEY_DISPLAY,
104
+ payload: true
105
+ });
106
+ };
107
+ const endConversationalSurveyCallback = async () => {
108
+ dispatch({
109
+ type: LiveChatWidgetActionType.SET_CONVERSATION_STATE,
110
+ payload: ConversationState.InActive
111
+ });
112
+ };
85
113
  webChatStore = createStore({},
86
114
  //initial state
87
- preProcessingMiddleware, attachmentProcessingMiddleware, createAttachmentUploadValidatorMiddleware((_state$domainStates$l = state.domainStates.liveChatConfig) === null || _state$domainStates$l === void 0 ? void 0 : _state$domainStates$l.allowedFileExtensions, (_state$domainStates$l2 = state.domainStates.liveChatConfig) === null || _state$domainStates$l2 === void 0 ? void 0 : _state$domainStates$l2.maxUploadFileSize, localizedTexts), channelDataMiddleware, createConversationEndMiddleware(conversationEndCallback), createDataMaskingMiddleware((_state$domainStates$l3 = state.domainStates.liveChatConfig) === null || _state$domainStates$l3 === void 0 ? void 0 : _state$domainStates$l3.DataMaskingInfo), createMessageTimeStampMiddleware, createMessageSequenceIdOverrideMiddleware, gifUploadMiddleware, htmlPlayerMiddleware, htmlTextMiddleware(honorsTargetInHTMLLinks), createMaxMessageSizeValidator(localizedTexts), sanitizationMiddleware,
115
+ preProcessingMiddleware, attachmentProcessingMiddleware, createAttachmentUploadValidatorMiddleware((_state$domainStates$l = state.domainStates.liveChatConfig) === null || _state$domainStates$l === void 0 ? void 0 : _state$domainStates$l.allowedFileExtensions, (_state$domainStates$l2 = state.domainStates.liveChatConfig) === null || _state$domainStates$l2 === void 0 ? void 0 : _state$domainStates$l2.maxUploadFileSize, localizedTexts), channelDataMiddleware(addConversationalSurveyTagsCallback), createConversationEndMiddleware(conversationEndCallback, startConversationalSurveyCallback, endConversationalSurveyCallback), createDataMaskingMiddleware((_state$domainStates$l3 = state.domainStates.liveChatConfig) === null || _state$domainStates$l3 === void 0 ? void 0 : _state$domainStates$l3.DataMaskingInfo), createMessageTimeStampMiddleware, createMessageSequenceIdOverrideMiddleware, gifUploadMiddleware, htmlPlayerMiddleware, htmlTextMiddleware(honorsTargetInHTMLLinks), createMaxMessageSizeValidator(localizedTexts), sanitizationMiddleware,
88
116
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
89
117
  ...(((_props$webChatContain7 = props.webChatContainerProps) === null || _props$webChatContain7 === void 0 ? void 0 : _props$webChatContain7.storeMiddlewares) ?? []));
90
118
  WebChatStoreLoader.store = webChatStore;
@@ -6,6 +6,22 @@ export const isPostChatSurveyEnabled = async facadeChatSDK => {
6
6
  const postChatEnabled = (_chatConfig$LiveWSAnd = chatConfig.LiveWSAndLiveChatEngJoin) === null || _chatConfig$LiveWSAnd === void 0 ? void 0 : _chatConfig$LiveWSAnd.msdyn_postconversationsurveyenable.toString().toLowerCase();
7
7
  return postChatEnabled === "true";
8
8
  };
9
+ export const getPostChatSurveyConfig = async facadeChatSDK => {
10
+ var _chatConfig$LiveWSAnd2, _chatConfig$LiveWSAnd3, _chatConfig$LiveWSAnd4, _chatConfig$LiveWSAnd5, _chatConfig$LiveWSAnd6, _chatConfig$LiveWSAnd7, _chatConfig$LiveWSAnd8, _chatConfig$LiveWSAnd9, _chatConfig$LiveWSAnd10;
11
+ const chatConfig = await facadeChatSDK.getLiveChatConfig();
12
+ const postChatEnabled = (_chatConfig$LiveWSAnd2 = chatConfig.LiveWSAndLiveChatEngJoin) === null || _chatConfig$LiveWSAnd2 === void 0 ? void 0 : _chatConfig$LiveWSAnd2.msdyn_postconversationsurveyenable.toString().toLowerCase();
13
+ const agentSurveyMode = (_chatConfig$LiveWSAnd3 = chatConfig.LiveWSAndLiveChatEngJoin) === null || _chatConfig$LiveWSAnd3 === void 0 ? void 0 : (_chatConfig$LiveWSAnd4 = _chatConfig$LiveWSAnd3.msdyn_postconversationsurveymode) === null || _chatConfig$LiveWSAnd4 === void 0 ? void 0 : _chatConfig$LiveWSAnd4.toString();
14
+ const botSurveyMode = (_chatConfig$LiveWSAnd5 = chatConfig.LiveWSAndLiveChatEngJoin) === null || _chatConfig$LiveWSAnd5 === void 0 ? void 0 : (_chatConfig$LiveWSAnd6 = _chatConfig$LiveWSAnd5.msdyn_postconversationsurveybotsurveymode) === null || _chatConfig$LiveWSAnd6 === void 0 ? void 0 : _chatConfig$LiveWSAnd6.toString();
15
+ const surveyProvider = (_chatConfig$LiveWSAnd7 = chatConfig.LiveWSAndLiveChatEngJoin) === null || _chatConfig$LiveWSAnd7 === void 0 ? void 0 : (_chatConfig$LiveWSAnd8 = _chatConfig$LiveWSAnd7.msdyn_surveyprovider) === null || _chatConfig$LiveWSAnd8 === void 0 ? void 0 : _chatConfig$LiveWSAnd8.toString();
16
+ const isConversationalSurveyEnabled = (_chatConfig$LiveWSAnd9 = chatConfig.LiveWSAndLiveChatEngJoin) === null || _chatConfig$LiveWSAnd9 === void 0 ? void 0 : (_chatConfig$LiveWSAnd10 = _chatConfig$LiveWSAnd9.msdyn_isConversationalPostChatSurveyEnabled) === null || _chatConfig$LiveWSAnd10 === void 0 ? void 0 : _chatConfig$LiveWSAnd10.toString().toLowerCase();
17
+ return {
18
+ postChatEnabled: postChatEnabled === "true",
19
+ agentSurveyMode: agentSurveyMode,
20
+ botSurveyMode: botSurveyMode,
21
+ surveyProvider: surveyProvider,
22
+ isConversationalSurveyEnabled: isConversationalSurveyEnabled === "true"
23
+ };
24
+ };
9
25
  export const isPersistentChatEnabled = conversationMode => {
10
26
  if (isNullOrUndefined(conversationMode)) {
11
27
  return false;
@@ -1,11 +1,11 @@
1
- import { Constants, ParticipantType, PostChatSurveyTelemetryMessage } from "../../../common/Constants";
1
+ import { Constants, ParticipantType, PostChatSurveyTelemetryMessage, SurveyProvider } from "../../../common/Constants";
2
2
  import { LogLevel, TelemetryEvent } from "../../../common/telemetry/TelemetryConstants";
3
3
  import { ConversationState } from "../../../contexts/common/ConversationState";
4
4
  import { LiveChatWidgetActionType } from "../../../contexts/common/LiveChatWidgetActionType";
5
5
  import { PostChatSurveyMode } from "../../postchatsurveypanestateful/enums/PostChatSurveyMode";
6
6
  import { TelemetryHelper } from "../../../common/telemetry/TelemetryHelper";
7
7
  import { addDelayInMs } from "../../../common/utils";
8
- import { isPostChatSurveyEnabled } from "./liveChatConfigUtils";
8
+ import { getPostChatSurveyConfig } from "./liveChatConfigUtils";
9
9
 
10
10
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
11
  let conversationDetails = undefined;
@@ -46,23 +46,34 @@ const setSurveyMode = async (props, participantType, state, dispatch) => {
46
46
  };
47
47
 
48
48
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
- const renderSurvey = async (postChatContext, dispatch) => {
49
+ const renderSurvey = async (postChatContext, state, dispatch) => {
50
50
  if (postChatSurveyMode === PostChatSurveyMode.Link) {
51
51
  setWidgetStateToInactive(dispatch);
52
52
  return;
53
53
  }
54
54
  if (postChatSurveyMode === PostChatSurveyMode.Embed) {
55
- await embedModePostChatWorkflow(postChatContext, dispatch);
55
+ await embedModePostChatWorkflow(postChatContext, state, dispatch);
56
56
  }
57
57
  };
58
58
 
59
59
  // Function for embed mode postchat workflow which is essentially same for both customer and agent
60
60
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
61
- const embedModePostChatWorkflow = async (postChatContext, dispatch) => {
61
+ const embedModePostChatWorkflow = async (postChatContext, state, dispatch) => {
62
62
  TelemetryHelper.logActionEvent(LogLevel.INFO, {
63
63
  Event: TelemetryEvent.EmbedModePostChatWorkflowStarted
64
64
  });
65
65
  if (postChatContext) {
66
+ if (postChatContext.isConversationalSurveyEnabled && postChatContext.surveyProvider === SurveyProvider.MicrosoftCopilotStudio) {
67
+ dispatch({
68
+ type: LiveChatWidgetActionType.SET_CONVERSATION_STATE,
69
+ payload: ConversationState.Postchat
70
+ });
71
+ dispatch({
72
+ type: LiveChatWidgetActionType.SET_CONVERSATIONAL_SURVEY_DISPLAY,
73
+ payload: true
74
+ });
75
+ return;
76
+ }
66
77
  dispatch({
67
78
  type: LiveChatWidgetActionType.SET_CONVERSATION_STATE,
68
79
  payload: ConversationState.PostchatLoading
@@ -89,7 +100,7 @@ const initiatePostChat = async (props, conversationDetailsParam, state, dispatch
89
100
  conversationDetails = conversationDetailsParam;
90
101
  const participantType = ((_conversationDetails = conversationDetails) === null || _conversationDetails === void 0 ? void 0 : _conversationDetails.participantType) ?? postchatContext.participantType;
91
102
  await setSurveyMode(props, participantType, state, dispatch);
92
- await renderSurvey(postchatContext, dispatch);
103
+ await renderSurvey(postchatContext, state, dispatch);
93
104
  };
94
105
 
95
106
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -102,7 +113,14 @@ const isPostChatEnabled = (props, state) => {
102
113
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
103
114
  const getPostChatContext = async (facadeChatSDK, state, dispatch) => {
104
115
  try {
105
- const postChatEnabled = await isPostChatSurveyEnabled(facadeChatSDK);
116
+ const postChatConfig = await getPostChatSurveyConfig(facadeChatSDK);
117
+ const postChatEnabled = postChatConfig.postChatEnabled;
118
+ if (postChatConfig.isConversationalSurveyEnabled) {
119
+ dispatch({
120
+ type: LiveChatWidgetActionType.SET_CONVERSATIONAL_SURVEY_ENABLED,
121
+ payload: true
122
+ });
123
+ }
106
124
  if (postChatEnabled) {
107
125
  var _state$domainStates2;
108
126
  if ((state === null || state === void 0 ? void 0 : (_state$domainStates2 = state.domainStates) === null || _state$domainStates2 === void 0 ? void 0 : _state$domainStates2.postChatContext) === undefined) {
@@ -112,11 +130,17 @@ const getPostChatContext = async (facadeChatSDK, state, dispatch) => {
112
130
  Event: TelemetryEvent.PostChatContextCallSucceed,
113
131
  Description: PostChatSurveyTelemetryMessage.PostChatContextCallSucceed
114
132
  });
133
+
134
+ // Merge postChatConfig with postChatSurveyContext
135
+ const mergedContext = {
136
+ ...context,
137
+ ...postChatConfig
138
+ };
115
139
  dispatch({
116
140
  type: LiveChatWidgetActionType.SET_POST_CHAT_CONTEXT,
117
- payload: context
141
+ payload: mergedContext
118
142
  });
119
- return context;
143
+ return mergedContext;
120
144
  }
121
145
  }
122
146
  } catch (error) {
@@ -4,12 +4,19 @@ import { ConversationState } from "../../../contexts/common/ConversationState";
4
4
  import { LiveChatWidgetActionType } from "../../../contexts/common/LiveChatWidgetActionType";
5
5
  import { PostChatSurveyTelemetryMessage } from "../../../common/Constants";
6
6
  import { TelemetryHelper } from "../../../common/telemetry/TelemetryHelper";
7
- import { isPostChatSurveyEnabled } from "./liveChatConfigUtils";
7
+ import { getPostChatSurveyConfig } from "./liveChatConfigUtils";
8
8
 
9
9
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
10
  export const setPostChatContextAndLoadSurvey = async (facadeChatSDK, dispatch, persistedChat) => {
11
11
  try {
12
- const postChatEnabled = await isPostChatSurveyEnabled(facadeChatSDK);
12
+ const postChatConfig = await getPostChatSurveyConfig(facadeChatSDK);
13
+ if (postChatConfig.isConversationalSurveyEnabled) {
14
+ dispatch({
15
+ type: LiveChatWidgetActionType.SET_CONVERSATIONAL_SURVEY_ENABLED,
16
+ payload: true
17
+ });
18
+ }
19
+ const postChatEnabled = postChatConfig.postChatEnabled;
13
20
  if (postChatEnabled) {
14
21
  if (!persistedChat) {
15
22
  TelemetryHelper.logSDKEventToAllTelemetry(LogLevel.INFO, {
@@ -22,9 +29,15 @@ export const setPostChatContextAndLoadSurvey = async (facadeChatSDK, dispatch, p
22
29
  Event: TelemetryEvent.PostChatContextCallSucceed,
23
30
  Description: PostChatSurveyTelemetryMessage.PostChatContextCallSucceed
24
31
  });
32
+
33
+ // Merge postChatConfig with postChatSurveyContext
34
+ const mergedContext = {
35
+ ...context,
36
+ ...postChatConfig
37
+ };
25
38
  dispatch({
26
39
  type: LiveChatWidgetActionType.SET_POST_CHAT_CONTEXT,
27
- payload: context
40
+ payload: mergedContext
28
41
  });
29
42
  }
30
43
  }
@@ -197,8 +197,11 @@ const initStartChat = async (facadeChatSDK, dispatch, setAdapter, state, props,
197
197
  portalContactId: (_window$Microsoft = window.Microsoft) === null || _window$Microsoft === void 0 ? void 0 : (_window$Microsoft$Dyn = _window$Microsoft.Dynamic365) === null || _window$Microsoft$Dyn === void 0 ? void 0 : (_window$Microsoft$Dyn2 = _window$Microsoft$Dyn.Portal) === null || _window$Microsoft$Dyn2 === void 0 ? void 0 : (_window$Microsoft$Dyn3 = _window$Microsoft$Dyn2.User) === null || _window$Microsoft$Dyn3 === void 0 ? void 0 : _window$Microsoft$Dyn3.contactId
198
198
  };
199
199
  const startChatOptionalParams = Object.assign({}, params, optionalParams, defaultOptionalParams);
200
+ // startTime is used to determine if a message is history or new, better to be set before creating the adapter to get bandwidth
201
+ const startTime = new Date().getTime();
200
202
  await facadeChatSDK.startChat(startChatOptionalParams);
201
203
  isStartChatSuccessful = true;
204
+ await createAdapterAndSubscribe(facadeChatSDK, dispatch, setAdapter, startTime, props);
202
205
  } catch (error) {
203
206
  checkContactIdError(error);
204
207
  TelemetryHelper.logSDKEvent(LogLevel.ERROR, {
@@ -216,7 +219,6 @@ const initStartChat = async (facadeChatSDK, dispatch, setAdapter, state, props,
216
219
  isStartChatSuccessful = false;
217
220
  throw error;
218
221
  }
219
- await createAdapterAndSubscribe(facadeChatSDK, dispatch, setAdapter, props);
220
222
 
221
223
  // Set app state to Active
222
224
  if (isStartChatSuccessful) {
@@ -266,7 +268,7 @@ const initStartChat = async (facadeChatSDK, dispatch, setAdapter, state, props,
266
268
  };
267
269
 
268
270
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
269
- const createAdapterAndSubscribe = async (facadeChatSDK, dispatch, setAdapter, props) => {
271
+ const createAdapterAndSubscribe = async (facadeChatSDK, dispatch, setAdapter, startTime, props) => {
270
272
  // New adapter creation
271
273
  const newAdapter = await createAdapter(facadeChatSDK, props);
272
274
  setAdapter(newAdapter);
@@ -277,7 +279,7 @@ const createAdapterAndSubscribe = async (facadeChatSDK, dispatch, setAdapter, pr
277
279
  });
278
280
  if (chatToken !== null && chatToken !== void 0 && chatToken.chatId && chatToken !== null && chatToken !== void 0 && chatToken.visitorId) {
279
281
  var _newAdapter$activity$;
280
- newAdapter === null || newAdapter === void 0 ? void 0 : (_newAdapter$activity$ = newAdapter.activity$) === null || _newAdapter$activity$ === void 0 ? void 0 : _newAdapter$activity$.subscribe(createOnNewAdapterActivityHandler(chatToken.chatId, chatToken.visitorId));
282
+ newAdapter === null || newAdapter === void 0 ? void 0 : (_newAdapter$activity$ = newAdapter.activity$) === null || _newAdapter$activity$ === void 0 ? void 0 : _newAdapter$activity$.subscribe(createOnNewAdapterActivityHandler(chatToken.chatId, chatToken.visitorId, startTime));
281
283
  }
282
284
  };
283
285
  // eslint-disable-next-line @typescript-eslint/no-explicit-any