@microsoft/omnichannel-chat-widget 1.8.2-main.5195aba → 1.8.2-main.5a42a08

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 (40) hide show
  1. package/README.md +46 -1
  2. package/lib/cjs/common/Constants.js +6 -0
  3. package/lib/cjs/common/telemetry/TelemetryConstants.js +1 -0
  4. package/lib/cjs/common/utils.js +21 -2
  5. package/lib/cjs/components/chatbuttonstateful/ChatButtonStateful.js +4 -4
  6. package/lib/cjs/components/livechatwidget/common/createMarkdown.js +54 -1
  7. package/lib/cjs/components/livechatwidget/common/customEventHandler.js +53 -0
  8. package/lib/cjs/components/livechatwidget/common/initWebChatComposer.js +4 -1
  9. package/lib/cjs/components/livechatwidget/common/startChat.js +1 -1
  10. package/lib/cjs/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +5 -1
  11. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/callActionMiddleware.js +42 -0
  12. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/customEventMiddleware.js +41 -0
  13. package/lib/cjs/contexts/common/CustomEventType.js +1 -0
  14. package/lib/cjs/firstresponselatency/FirstMessageTrackerFromBot.js +101 -36
  15. package/lib/cjs/firstresponselatency/FirstResponseLatencyTracker.js +39 -21
  16. package/lib/cjs/firstresponselatency/util.js +12 -8
  17. package/lib/esm/common/Constants.js +6 -0
  18. package/lib/esm/common/telemetry/TelemetryConstants.js +1 -0
  19. package/lib/esm/common/utils.js +17 -0
  20. package/lib/esm/components/chatbuttonstateful/ChatButtonStateful.js +4 -4
  21. package/lib/esm/components/livechatwidget/common/createMarkdown.js +54 -1
  22. package/lib/esm/components/livechatwidget/common/customEventHandler.js +45 -0
  23. package/lib/esm/components/livechatwidget/common/initWebChatComposer.js +4 -1
  24. package/lib/esm/components/livechatwidget/common/startChat.js +1 -1
  25. package/lib/esm/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +5 -1
  26. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/callActionMiddleware.js +36 -0
  27. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/customEventMiddleware.js +33 -0
  28. package/lib/esm/contexts/common/CustomEventType.js +1 -0
  29. package/lib/esm/firstresponselatency/FirstMessageTrackerFromBot.js +101 -36
  30. package/lib/esm/firstresponselatency/FirstResponseLatencyTracker.js +39 -21
  31. package/lib/esm/firstresponselatency/util.js +12 -8
  32. package/lib/types/common/Constants.d.ts +6 -0
  33. package/lib/types/common/telemetry/TelemetryConstants.d.ts +1 -0
  34. package/lib/types/common/utils.d.ts +3 -0
  35. package/lib/types/components/livechatwidget/common/customEventHandler.d.ts +4 -0
  36. package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/callActionMiddleware.d.ts +8 -0
  37. package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/customEventMiddleware.d.ts +22 -0
  38. package/lib/types/contexts/common/CustomEventType.d.ts +6 -0
  39. package/lib/types/firstresponselatency/FirstResponseLatencyTracker.d.ts +2 -2
  40. package/package.json +2 -2
@@ -17,14 +17,13 @@ let FirstResponseLatencyTracker = /*#__PURE__*/function () {
17
17
  function FirstResponseLatencyTracker() {
18
18
  _classCallCheck(this, FirstResponseLatencyTracker);
19
19
  _defineProperty(this, "isABotConversation", false);
20
- _defineProperty(this, "isStarted", false);
21
- _defineProperty(this, "isEnded", false);
20
+ _defineProperty(this, "isTracking", false);
22
21
  _defineProperty(this, "startTrackingMessage", void 0);
23
22
  _defineProperty(this, "stopTrackingMessage", void 0);
24
23
  _defineProperty(this, "isReady", false);
24
+ _defineProperty(this, "trackingTimeoutId", void 0);
25
25
  _defineProperty(this, "offlineNetworkListener", _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.TelemetryEvent.NetworkDisconnected).subscribe(() => {
26
- this.isStarted = false;
27
- this.isEnded = false;
26
+ this.isTracking = false;
28
27
  _TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
29
28
  Event: _TelemetryConstants.TelemetryEvent.MessageStopLapTrackError,
30
29
  Description: "Tracker Stopped due to network disconnection"
@@ -72,7 +71,7 @@ let FirstResponseLatencyTracker = /*#__PURE__*/function () {
72
71
  value: function startTracking(payload) {
73
72
  if (!this.isReady) return;
74
73
  // this prevents to initiate tracking for multiple incoming messages
75
- if (this.isStarted) {
74
+ if (this.isTracking) {
76
75
  return;
77
76
  }
78
77
  // this is to ensure we track only messages where bot is engaged
@@ -80,10 +79,24 @@ let FirstResponseLatencyTracker = /*#__PURE__*/function () {
80
79
  return;
81
80
  }
82
81
  // control of states to prevent clashing of messages
83
- this.isStarted = true;
84
- this.isEnded = false;
82
+ this.isTracking = true;
85
83
  // The idea of using types is to enrich telemetry data
86
84
  this.startTrackingMessage = this.createTrackingMessage(payload, "userMessage");
85
+
86
+ // Start a 5-second timeout to auto-stop tracking if not stopped
87
+ if (this.trackingTimeoutId) {
88
+ clearTimeout(this.trackingTimeoutId);
89
+ }
90
+ this.trackingTimeoutId = setTimeout(() => {
91
+ // this means the start process is in progress, but the end wasn't called within the time limit
92
+ if (this.isTracking) {
93
+ // Reset state variables and skip stopTracking
94
+ this.isTracking = false;
95
+ this.startTrackingMessage = undefined;
96
+ this.stopTrackingMessage = undefined;
97
+ this.trackingTimeoutId = undefined;
98
+ }
99
+ }, 5000);
87
100
  }
88
101
  }, {
89
102
  key: "handleAgentMessage",
@@ -99,13 +112,16 @@ let FirstResponseLatencyTracker = /*#__PURE__*/function () {
99
112
  value: function stopTracking(payload) {
100
113
  var _this$stopTrackingMes, _this$startTrackingMe;
101
114
  // this prevents execution for multiple incoming messages from the bot.
102
- if (this.isEnded && !this.isStarted) {
115
+ if (!this.isTracking) {
103
116
  return;
104
117
  }
105
-
118
+ // Clear the timeout if it exists
119
+ if (this.trackingTimeoutId) {
120
+ clearTimeout(this.trackingTimeoutId);
121
+ this.trackingTimeoutId = undefined;
122
+ }
106
123
  // control of states to prevent clashing of messages
107
- this.isEnded = true;
108
- this.isStarted = false;
124
+ this.isTracking = false;
109
125
 
110
126
  // The idea of using types is to enrich telemetry data
111
127
  this.stopTrackingMessage = this.createTrackingMessage(payload, "botMessage");
@@ -160,12 +176,16 @@ let FirstResponseLatencyTracker = /*#__PURE__*/function () {
160
176
  if (!payload || !payload.Id) {
161
177
  throw new Error("Invalid payload");
162
178
  }
163
- if (!this.isMessageFromValidSender(payload)) return;
164
- if (this.isABotConversation && this.isStarted) {
179
+
180
+ // Only allow stopTracking if sender is valid and tracking is active
181
+ if (!this.isMessageFromValidSender(payload)) {
182
+ // Do not change isTracking or stopTrackingMessage
183
+ return;
184
+ }
185
+ if (this.isABotConversation && this.isTracking) {
165
186
  this.stopTracking(payload);
166
187
  }
167
188
  } catch (e) {
168
- console.error("FRL : error while trying to stop the tracker", e);
169
189
  _TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.ERROR, {
170
190
  Event: _TelemetryConstants.TelemetryEvent.MessageStopLapTrackError,
171
191
  Description: "Error while stopping the clock",
@@ -174,11 +194,6 @@ let FirstResponseLatencyTracker = /*#__PURE__*/function () {
174
194
  payload: payload
175
195
  }
176
196
  });
177
- //reset state
178
- this.startTrackingMessage = undefined;
179
- this.stopTrackingMessage = undefined;
180
- this.isStarted = false;
181
- this.isEnded = false;
182
197
  }
183
198
  }
184
199
  }, {
@@ -186,10 +201,13 @@ let FirstResponseLatencyTracker = /*#__PURE__*/function () {
186
201
  value: function deregister() {
187
202
  // Reset State
188
203
  this.isABotConversation = false;
189
- this.isStarted = false;
190
- this.isEnded = false;
204
+ this.isTracking = false;
191
205
  this.startTrackingMessage = undefined;
192
206
  this.stopTrackingMessage = undefined;
207
+ if (this.trackingTimeoutId) {
208
+ clearTimeout(this.trackingTimeoutId);
209
+ this.trackingTimeoutId = undefined;
210
+ }
193
211
  this.offlineNetworkListener.unsubscribe();
194
212
  this.fmltrackingListener.unsubscribe();
195
213
  this.rehydrateListener.unsubscribe();
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", {
6
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
+ const DELTA_WITHIN_LIMITS_IN_MS = 250;
10
+
9
11
  /**
10
12
  * Determines whether a given activity is a historical message.
11
13
  *
@@ -23,19 +25,21 @@ var _Constants2 = require("../common/Constants");
23
25
  * - If the ID is valid and the timestamp is older than the start time, the message is historical.
24
26
  */
25
27
  const isHistoryMessage = (activity, startTime) => {
26
- var _activity$channelData, _activity$channelData2;
28
+ var _activity$channelData;
27
29
  // Only process message activities
28
30
  if ((activity === null || activity === void 0 ? void 0 : activity.type) !== _Constants2.Constants.message) {
29
31
  return false;
30
32
  }
31
33
 
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
+ // Prioritize legacy history tag
35
+ if (activity !== null && activity !== void 0 && (_activity$channelData = activity.channelData) !== null && _activity$channelData !== void 0 && _activity$channelData.tags && activity.channelData.tags.includes(_Constants2.Constants.historyMessageTag)) {
34
36
  return true;
35
37
  }
36
38
  const activityId = extractTimestampFromId(activity);
37
39
  const isValidId = !isNaN(activityId) && activityId > 0;
38
- const isOlderThanStartTime = activityId < startTime;
40
+ const difference = startTime - activityId;
41
+ // Only consider historical if activityId < startTime and difference >= DELTA_WITHIN_LIMITS_IN_MS
42
+ const isOlderThanStartTime = activityId < startTime && difference >= DELTA_WITHIN_LIMITS_IN_MS;
39
43
  const isHistoryById = isValidId && isOlderThanStartTime;
40
44
  return isHistoryById;
41
45
  };
@@ -64,7 +68,7 @@ const extractTimestampFromId = activity => {
64
68
  };
65
69
  exports.extractTimestampFromId = extractTimestampFromId;
66
70
  const buildMessagePayload = (activity, userId) => {
67
- var _text, _text2, _activity$channelData3, _activity$from;
71
+ var _text, _text2, _activity$channelData2, _activity$from;
68
72
  return {
69
73
  // To identify hidden contents vs empty content
70
74
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -72,7 +76,7 @@ const buildMessagePayload = (activity, userId) => {
72
76
  type: activity === null || activity === void 0 ? void 0 : activity.type,
73
77
  timestamp: activity === null || activity === void 0 ? void 0 : activity.timestamp,
74
78
  userId: userId,
75
- tags: activity === null || activity === void 0 ? void 0 : (_activity$channelData3 = activity.channelData) === null || _activity$channelData3 === void 0 ? void 0 : _activity$channelData3.tags,
79
+ tags: (activity === null || activity === void 0 ? void 0 : (_activity$channelData2 = activity.channelData) === null || _activity$channelData2 === void 0 ? void 0 : _activity$channelData2.tags) || [],
76
80
  messageType: "",
77
81
  Id: activity === null || activity === void 0 ? void 0 : activity.id,
78
82
  role: activity === null || activity === void 0 ? void 0 : (_activity$from = activity.from) === null || _activity$from === void 0 ? void 0 : _activity$from.role,
@@ -98,9 +102,9 @@ const polyfillMessagePayloadForEvent = (activity, payload, conversationId) => {
98
102
  };
99
103
  exports.polyfillMessagePayloadForEvent = polyfillMessagePayloadForEvent;
100
104
  const getScenarioType = activity => {
101
- var _activity$from3, _activity$channelData4;
105
+ var _activity$from3, _activity$channelData3;
102
106
  const role = activity === null || activity === void 0 ? void 0 : (_activity$from3 = activity.from) === null || _activity$from3 === void 0 ? void 0 : _activity$from3.role;
103
- const tags = activity === null || activity === void 0 ? void 0 : (_activity$channelData4 = activity.channelData) === null || _activity$channelData4 === void 0 ? void 0 : _activity$channelData4.tags;
107
+ const tags = activity === null || activity === void 0 ? void 0 : (_activity$channelData3 = activity.channelData) === null || _activity$channelData3 === void 0 ? void 0 : _activity$channelData3.tags;
104
108
  if (role === _Constants2.Constants.userMessageTag) {
105
109
  return _Constants.ScenarioType.UserSendMessageStrategy;
106
110
  }
@@ -25,6 +25,7 @@ _defineProperty(Constants, "webchatChannelId", "webchat");
25
25
  _defineProperty(Constants, "markdown", "markdown");
26
26
  _defineProperty(Constants, "actionType", "actionType");
27
27
  _defineProperty(Constants, "markDownSystemMessageClass", "webchat__basic-transcript__activity-markdown-body");
28
+ _defineProperty(Constants, "MARKDOWN_LIST_INDENTATION", " ");
28
29
  _defineProperty(Constants, "String", "string");
29
30
  _defineProperty(Constants, "ChatMessagesJson", "chatMessagesJson");
30
31
  _defineProperty(Constants, "truePascal", "True");
@@ -124,6 +125,11 @@ _defineProperty(Constants, "InitContextParamsResponse", "initContextParamsRespon
124
125
  _defineProperty(Constants, "OCOriginalMessageId", "OriginalMessageId");
125
126
  _defineProperty(Constants, "WebchatSequenceIdAttribute", "webchat:sequence-id");
126
127
  _defineProperty(Constants, "MessageSequenceIdOverride", "MessageSequenceIdOverride");
128
+ _defineProperty(Constants, "sendCustomEvent", "sendCustomEvent");
129
+ _defineProperty(Constants, "onCustomEvent", "onCustomEvent");
130
+ _defineProperty(Constants, "customEventName", "customEventName");
131
+ _defineProperty(Constants, "customEventValue", "customEventValue");
132
+ _defineProperty(Constants, "Hidden", "Hidden");
127
133
  _defineProperty(Constants, "EndConversationDueToOverflow", "endconversationduetooverflow");
128
134
  export const Regex = (_class = /*#__PURE__*/_createClass(function Regex() {
129
135
  _classCallCheck(this, Regex);
@@ -223,6 +223,7 @@ export let TelemetryEvent;
223
223
  TelemetryEvent["SystemMessageReceived"] = "SystemMessageReceived";
224
224
  TelemetryEvent["RehydrateMessageReceived"] = "RehydrateMessageReceived";
225
225
  TelemetryEvent["CustomContextReceived"] = "CustomContextReceived";
226
+ TelemetryEvent["CustomEventAction"] = "CustomEventAction";
226
227
  TelemetryEvent["NetworkDisconnected"] = "NetworkDisconnected";
227
228
  TelemetryEvent["NetworkReconnected"] = "NetworkReconnected";
228
229
  TelemetryEvent["LinkModePostChatWorkflowStarted"] = "LinkModePostChatWorkflowStarted";
@@ -454,6 +454,23 @@ export function getDeviceType() {
454
454
  return "standard";
455
455
  }
456
456
  }
457
+
458
+ //Bots expect a payload containing:
459
+ //1. customEventName: this should be string describe the event name
460
+ //2. customEventValue: given the value is from customer with unknown type, it is required to stringify the payload later
461
+ export const isValidCustomEvent = payload => {
462
+ if (Constants.customEventName in payload && payload.customEventName && typeof payload.customEventName === Constants.String && Constants.customEventValue in payload && payload.customEventValue) return true;
463
+ return false;
464
+ };
465
+ export const getCustomEventValue = customEventPayload => {
466
+ let returnVal = "";
467
+ try {
468
+ returnVal = typeof customEventPayload.customEventValue === Constants.String ? customEventPayload.customEventValue : JSON.stringify(customEventPayload.customEventValue);
469
+ } catch (error) {
470
+ console.error(error);
471
+ }
472
+ return returnVal;
473
+ };
457
474
  export function isEndConversationDueToOverflowActivity(activity) {
458
475
  var _activity$channelData, _activity$channelData2;
459
476
  return (activity === null || activity === void 0 ? void 0 : (_activity$channelData = activity.channelData) === null || _activity$channelData === void 0 ? void 0 : _activity$channelData.tags) && Array.isArray(activity === null || activity === void 0 ? void 0 : (_activity$channelData2 = activity.channelData) === null || _activity$channelData2 === void 0 ? void 0 : _activity$channelData2.tags) && activity.channelData.tags.includes(Constants.EndConversationDueToOverflow);
@@ -65,7 +65,6 @@ export const ChatButtonStateful = props => {
65
65
  };
66
66
  const outOfOfficeStyleProps = Object.assign({}, defaultOutOfOfficeChatButtonStyleProps, outOfOfficeButtonProps === null || outOfOfficeButtonProps === void 0 ? void 0 : outOfOfficeButtonProps.styleProps);
67
67
  const controlProps = {
68
- ...(buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.controlProps),
69
68
  id: "oc-lcw-chat-button",
70
69
  dir: state.domainStates.globalDir,
71
70
  titleText: "Let's Chat!",
@@ -74,7 +73,8 @@ export const ChatButtonStateful = props => {
74
73
  unreadMessageCount: state.appStates.unreadMessageCount ? state.appStates.unreadMessageCount > Constants.maximumUnreadMessageCount ? (_props$buttonProps = props.buttonProps) === null || _props$buttonProps === void 0 ? void 0 : (_props$buttonProps$co = _props$buttonProps.controlProps) === null || _props$buttonProps$co === void 0 ? void 0 : _props$buttonProps$co.largeUnreadMessageString : state.appStates.unreadMessageCount.toString() : "0",
75
74
  unreadMessageString: (_props$buttonProps2 = props.buttonProps) === null || _props$buttonProps2 === void 0 ? void 0 : (_props$buttonProps2$c = _props$buttonProps2.controlProps) === null || _props$buttonProps2$c === void 0 ? void 0 : _props$buttonProps2$c.unreadMessageString,
76
75
  // Regular chat button onClick - this will always take precedence
77
- onClick: () => ref.current()
76
+ onClick: () => ref.current(),
77
+ ...(buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.controlProps)
78
78
  };
79
79
  const outOfOfficeControlProps = {
80
80
  // Only take specific properties from outOfOfficeButtonProps, never onClick
@@ -83,7 +83,6 @@ export const ChatButtonStateful = props => {
83
83
  titleText: (outOfOfficeButtonProps === null || outOfOfficeButtonProps === void 0 ? void 0 : (_outOfOfficeButtonPro = outOfOfficeButtonProps.controlProps) === null || _outOfOfficeButtonPro === void 0 ? void 0 : _outOfOfficeButtonPro.titleText) || "We're Offline",
84
84
  subtitleText: (outOfOfficeButtonProps === null || outOfOfficeButtonProps === void 0 ? void 0 : (_outOfOfficeButtonPro2 = outOfOfficeButtonProps.controlProps) === null || _outOfOfficeButtonPro2 === void 0 ? void 0 : _outOfOfficeButtonPro2.subtitleText) || "No agents available",
85
85
  unreadMessageString: (_props$buttonProps3 = props.buttonProps) === null || _props$buttonProps3 === void 0 ? void 0 : (_props$buttonProps3$c = _props$buttonProps3.controlProps) === null || _props$buttonProps3$c === void 0 ? void 0 : _props$buttonProps3$c.unreadMessageString,
86
- ...(outOfOfficeButtonProps === null || outOfOfficeButtonProps === void 0 ? void 0 : outOfOfficeButtonProps.controlProps),
87
86
  // Out-of-office specific onClick - this will ALWAYS take precedence
88
87
  onClick: () => {
89
88
  if (state.appStates.isMinimized) {
@@ -96,7 +95,8 @@ export const ChatButtonStateful = props => {
96
95
  type: LiveChatWidgetActionType.SET_CONVERSATION_STATE,
97
96
  payload: ConversationState.OutOfOffice
98
97
  });
99
- }
98
+ },
99
+ ...(outOfOfficeButtonProps === null || outOfOfficeButtonProps === void 0 ? void 0 : outOfOfficeButtonProps.controlProps)
100
100
  };
101
101
  useEffect(() => {
102
102
  TelemetryHelper.logLoadingEvent(LogLevel.INFO, {
@@ -28,12 +28,65 @@ export const createMarkdown = (disableMarkdownMessageFormatting, disableNewLineM
28
28
  // Rule to process html blocks and paragraphs
29
29
  "html_inline",
30
30
  // Rule to process html tags
31
- "newline" // Rule to proceess '\n'
31
+ "newline",
32
+ // Rule to proceess '\n'
33
+ "list" // Enable list parsing rule
32
34
  ]);
33
35
  }
34
36
 
35
37
  markdown.disable(["strikethrough"]);
36
38
 
39
+ // Custom plugin to fix numbered list continuity
40
+ markdown.use(function (md) {
41
+ const originalRender = md.render.bind(md);
42
+ const originalRenderInline = md.renderInline.bind(md);
43
+ function preprocessText(text) {
44
+ // Handle numbered lists that come with double line breaks (knowledge article format)
45
+ // This ensures proper continuous numbering instead of separate lists
46
+
47
+ let result = text;
48
+
49
+ // Only process if the text contains the double line break pattern
50
+ // But exclude simple numbered lists (where content after \n\n starts with another number)
51
+ if (!/\d+\.\s+.*?\n\n(?!\d+\.\s)[\s\S]*?(?:\n\n\d+\.|\s*$)/.test(text)) {
52
+ return result;
53
+ }
54
+
55
+ // Convert "1. Item\n\nContent\n\n2. Item" to proper markdown list format
56
+ // Use improved pattern with negative lookahead to exclude cases where content starts with numbered list
57
+ const listPattern = /(\d+\.\s+[^\n]+)(\n\n(?!\d+\.\s)[\s\S]*?)(?=\n\n\d+\.|\s*$)/g;
58
+ if (listPattern.test(result)) {
59
+ // Reset regex state for actual replacement
60
+ listPattern.lastIndex = 0;
61
+ result = result.replace(listPattern, (match, listItem, content) => {
62
+ if (!content) {
63
+ return match;
64
+ }
65
+
66
+ // Format content with proper indentation
67
+ const cleanContent = content.substring(2); // Remove leading \n\n
68
+ const lines = cleanContent.split("\n");
69
+ const indentedContent = lines.map(line => line.trim() ? `${Constants.MARKDOWN_LIST_INDENTATION}${line}` : "").join("\n");
70
+ const lineBreak = disableNewLineMarkdownSupport ? "\n" : "\n\n";
71
+ return `${listItem}${lineBreak}${indentedContent}`;
72
+ });
73
+ }
74
+ return result;
75
+ }
76
+
77
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
+ md.render = function (text, env) {
79
+ const processedText = preprocessText(text);
80
+ return originalRender(processedText, env);
81
+ };
82
+
83
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
84
+ md.renderInline = function (text, env) {
85
+ const processedText = preprocessText(text);
86
+ return originalRenderInline(processedText, env);
87
+ };
88
+ });
89
+
37
90
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
91
  markdown.use(MarkdownItForInline, "url_new_win", "link_open", function (tokens, idx, env) {
39
92
  const targetAttrIndex = tokens[idx].attrIndex(Constants.Target);
@@ -0,0 +1,45 @@
1
+ import { Constants } from "../../../common/Constants";
2
+ import { TelemetryHelper } from "../../../common/telemetry/TelemetryHelper";
3
+ import { LogLevel, TelemetryEvent } from "../../../common/telemetry/TelemetryConstants";
4
+ import { getCustomEventValue, isValidCustomEvent } from "../../../common/utils";
5
+ export const customEventCallback = facadeChatSDK => event => {
6
+ if (!(Constants.payload in event)) return;
7
+ if (isValidCustomEvent(event.payload)) {
8
+ const customEventPayload = event.payload;
9
+ try {
10
+ const customEventValueStr = getCustomEventValue(customEventPayload);
11
+ const customEventName = customEventPayload.customEventName;
12
+ const messageMeta = {
13
+ customEvent: Constants.true,
14
+ customEventName: customEventName,
15
+ customEventValue: customEventValueStr
16
+ };
17
+ const messagePayload = {
18
+ content: "",
19
+ tags: [Constants.Hidden],
20
+ metadata: messageMeta,
21
+ timestamp: new Date()
22
+ };
23
+ facadeChatSDK.sendMessage(messagePayload);
24
+ TelemetryHelper.logActionEventToAllTelemetry(LogLevel.DEBUG, {
25
+ Event: TelemetryEvent.CustomEventAction,
26
+ Description: "Sent customEvent.",
27
+ CustomProperties: {
28
+ customEventName,
29
+ lengthCustomEventValue: customEventValueStr.length
30
+ }
31
+ });
32
+ } catch (error) {
33
+ TelemetryHelper.logActionEventToAllTelemetry(LogLevel.ERROR, {
34
+ Event: TelemetryEvent.CustomEventAction,
35
+ Description: "Failed to process CustomEvent.",
36
+ ExceptionDetails: {
37
+ error
38
+ }
39
+ });
40
+ }
41
+ }
42
+ };
43
+ export const subscribeToSendCustomEvent = (broadcastService, facadeChatSDK, customEventCallback) => {
44
+ broadcastService.getMessageByEventName(Constants.sendCustomEvent).subscribe(customEventCallback(facadeChatSDK));
45
+ };
@@ -32,9 +32,12 @@ 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 createCallActionMiddleware from "../../webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/callActionMiddleware";
36
+ import createCustomEventMiddleware from "../../webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/customEventMiddleware";
35
37
  import { ConversationState } from "../../../contexts/common/ConversationState";
36
38
  import { executeReducer } from "../../../contexts/createReducer";
37
39
  import { createQueueOverflowMiddleware } from "../../webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/queueOverflowHandlerMiddleware";
40
+ import { BroadcastService } from "@microsoft/omnichannel-chat-components";
38
41
 
39
42
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
43
  export const initWebChatComposer = (props, state, dispatch, facadeChatSDK, endChat) => {
@@ -113,7 +116,7 @@ export const initWebChatComposer = (props, state, dispatch, facadeChatSDK, endCh
113
116
  };
114
117
  webChatStore = createStore({},
115
118
  //initial state
116
- 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), createQueueOverflowMiddleware(state, dispatch), 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,
119
+ 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), createCustomEventMiddleware(BroadcastService), createQueueOverflowMiddleware(state, dispatch), 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, createCallActionMiddleware(),
117
120
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
118
121
  ...(((_props$webChatContain7 = props.webChatContainerProps) === null || _props$webChatContain7 === void 0 ? void 0 : _props$webChatContain7.storeMiddlewares) ?? []));
119
122
  WebChatStoreLoader.store = webChatStore;
@@ -148,7 +148,6 @@ const setPreChatAndInitiateChat = async (facadeChatSDK, dispatch, setAdapter, is
148
148
  const optionalParams = {
149
149
  isProactiveChat
150
150
  };
151
- createTrackingForFirstMessage();
152
151
  await initStartChat(facadeChatSDK, dispatch, setAdapter, state, props, optionalParams);
153
152
  };
154
153
 
@@ -199,6 +198,7 @@ const initStartChat = async (facadeChatSDK, dispatch, setAdapter, state, props,
199
198
  const startChatOptionalParams = Object.assign({}, params, optionalParams, defaultOptionalParams);
200
199
  // startTime is used to determine if a message is history or new, better to be set before creating the adapter to get bandwidth
201
200
  const startTime = new Date().getTime();
201
+ createTrackingForFirstMessage();
202
202
  await facadeChatSDK.startChat(startChatOptionalParams);
203
203
  isStartChatSuccessful = true;
204
204
  await createAdapterAndSubscribe(facadeChatSDK, dispatch, setAdapter, startTime, props);
@@ -53,6 +53,7 @@ import { startProactiveChat } from "../common/startProactiveChat";
53
53
  import useChatAdapterStore from "../../../hooks/useChatAdapterStore";
54
54
  import useChatContextStore from "../../../hooks/useChatContextStore";
55
55
  import useFacadeSDKStore from "../../../hooks/useFacadeChatSDKStore";
56
+ import { customEventCallback, subscribeToSendCustomEvent } from "../common/customEventHandler";
56
57
  let uiTimer;
57
58
  export const LiveChatWidgetStateful = props => {
58
59
  var _props$webChatContain, _props$webChatContain2, _props$webChatContain3, _props$webChatContain4, _props$webChatContain5, _props$webChatContain6, _props$webChatContain7, _props$webChatContain8, _props$webChatContain9, _props$styleProps, _props$webChatContain10, _props$webChatContain11, _props$controlProps, _props$controlProps3, _state$appStates7, _props$webChatContain15, _state$appStates14, _props$webChatContain17, _props$webChatContain18, _props$controlProps12, _props$draggableChatW, _props$draggableChatW2, _props$draggableChatW3, _props$draggableChatW4, _props$draggableChatW5, _livechatProps$webCha, _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;
@@ -413,7 +414,7 @@ export const LiveChatWidgetStateful = props => {
413
414
  BroadcastService.getMessageByEventName(BroadcastEvent.StartChat).subscribe(msg => {
414
415
  var _msg$payload5, _msg$payload6, _msg$payload7, _msg$payload9, _inMemoryState$appSta2, _inMemoryState$appSta3, _inMemoryState$appSta4;
415
416
  // If chat is out of operating hours chat widget sets the conversation state to OutOfOffice.
416
- if (state.appStates.outsideOperatingHours === true) {
417
+ if (state.appStates.outsideOperatingHours && state.appStates.conversationState !== ConversationState.Active) {
417
418
  dispatch({
418
419
  type: LiveChatWidgetActionType.SET_MINIMIZED,
419
420
  payload: false
@@ -580,6 +581,9 @@ export const LiveChatWidgetStateful = props => {
580
581
  }
581
582
  });
582
583
 
584
+ // subscribe custom event
585
+ subscribeToSendCustomEvent(BroadcastService, facadeChatSDK, customEventCallback);
586
+
583
587
  // Check for TPC and log in telemetry if blocked
584
588
  isCookieAllowed();
585
589
  return () => {
@@ -0,0 +1,36 @@
1
+ /******
2
+ * CallActionMiddleware
3
+ *
4
+ * Intercepts custom call actions and handles tel: URL navigation
5
+ ******/
6
+
7
+ import { WebChatActionType } from "../../enums/WebChatActionType";
8
+
9
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
10
+ const createCallActionMiddleware = () => () => next => action => {
11
+ // Intercept incoming activities to modify suggested actions with call type
12
+ if (action.type === WebChatActionType.DIRECT_LINE_INCOMING_ACTIVITY) {
13
+ var _action$payload, _activity$suggestedAc;
14
+ const activity = (_action$payload = action.payload) === null || _action$payload === void 0 ? void 0 : _action$payload.activity;
15
+
16
+ // Check if activity has suggested actions with call type
17
+ if (activity !== null && activity !== void 0 && (_activity$suggestedAc = activity.suggestedActions) !== null && _activity$suggestedAc !== void 0 && _activity$suggestedAc.actions) {
18
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
+ activity.suggestedActions.actions = activity.suggestedActions.actions.map(suggestedAction => {
20
+ if (suggestedAction.type === "call") {
21
+ // Convert call action to openUrl with encoded tel URL scheme
22
+ const telUrl = suggestedAction.value;
23
+ const convertedAction = {
24
+ ...suggestedAction,
25
+ type: "openUrl",
26
+ value: `tel:${telUrl}`
27
+ };
28
+ return convertedAction;
29
+ }
30
+ return suggestedAction;
31
+ });
32
+ }
33
+ }
34
+ return next(action);
35
+ };
36
+ export default createCallActionMiddleware;
@@ -0,0 +1,33 @@
1
+ /******
2
+ * CustomEventMiddleware
3
+ *
4
+ * This middleware is invoked when a custom event is received.
5
+ * The callback is then invoked to handle the custom event.
6
+ ******/
7
+
8
+ import { Constants } from "../../../../../common/Constants";
9
+ import { WebChatActionType } from "../../enums/WebChatActionType";
10
+ export const isValidCustomEvent = activity => {
11
+ var _activity$channelData, _activity$channelData2, _activity$channelData3, _activity$channelData4, _activity$channelData5, _activity$channelData6, _activity$channelData7, _activity$from, _activity$channelData8, _activity$channelData9, _activity$channelData10, _activity$channelData11;
12
+ return !!(activity !== null && activity !== void 0 && (_activity$channelData = activity.channelData) !== null && _activity$channelData !== void 0 && (_activity$channelData2 = _activity$channelData.metadata) !== null && _activity$channelData2 !== void 0 && _activity$channelData2.customEvent && typeof (activity === null || activity === void 0 ? void 0 : (_activity$channelData3 = activity.channelData) === null || _activity$channelData3 === void 0 ? void 0 : (_activity$channelData4 = _activity$channelData3.metadata) === null || _activity$channelData4 === void 0 ? void 0 : _activity$channelData4.customEvent) === Constants.String && (activity === null || activity === void 0 ? void 0 : (_activity$channelData5 = activity.channelData) === null || _activity$channelData5 === void 0 ? void 0 : (_activity$channelData6 = _activity$channelData5.metadata) === null || _activity$channelData6 === void 0 ? void 0 : (_activity$channelData7 = _activity$channelData6.customEvent) === null || _activity$channelData7 === void 0 ? void 0 : _activity$channelData7.toLowerCase()) === Constants.true && (activity === null || activity === void 0 ? void 0 : (_activity$from = activity.from) === null || _activity$from === void 0 ? void 0 : _activity$from.role) !== Constants.userMessageTag && typeof (activity === null || activity === void 0 ? void 0 : (_activity$channelData8 = activity.channelData) === null || _activity$channelData8 === void 0 ? void 0 : (_activity$channelData9 = _activity$channelData8.metadata) === null || _activity$channelData9 === void 0 ? void 0 : _activity$channelData9.customEventName) === Constants.String && activity !== null && activity !== void 0 && (_activity$channelData10 = activity.channelData) !== null && _activity$channelData10 !== void 0 && (_activity$channelData11 = _activity$channelData10.metadata) !== null && _activity$channelData11 !== void 0 && _activity$channelData11.customEventValue);
13
+ };
14
+ const createCustomEventMiddleware = broadcastservice => () => next => action => {
15
+ var _action$payload;
16
+ if ((action === null || action === void 0 ? void 0 : action.type) == WebChatActionType.DIRECT_LINE_INCOMING_ACTIVITY && (_action$payload = action.payload) !== null && _action$payload !== void 0 && _action$payload.activity) {
17
+ const activity = action.payload.activity;
18
+ if (isValidCustomEvent(activity)) {
19
+ const customEvent = {
20
+ eventName: Constants.onCustomEvent,
21
+ payload: {
22
+ messageId: activity.messageid ?? activity.id,
23
+ customEventName: activity.channelData.metadata.customEventName,
24
+ customEventValue: activity.channelData.metadata.customEventValue
25
+ }
26
+ };
27
+ broadcastservice.postMessage(customEvent);
28
+ return;
29
+ }
30
+ }
31
+ return next(action);
32
+ };
33
+ export default createCustomEventMiddleware;
@@ -0,0 +1 @@
1
+ export {};