@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.
- package/README.md +46 -1
- package/lib/cjs/common/Constants.js +6 -0
- package/lib/cjs/common/telemetry/TelemetryConstants.js +1 -0
- package/lib/cjs/common/utils.js +21 -2
- package/lib/cjs/components/chatbuttonstateful/ChatButtonStateful.js +4 -4
- package/lib/cjs/components/livechatwidget/common/createMarkdown.js +54 -1
- package/lib/cjs/components/livechatwidget/common/customEventHandler.js +53 -0
- package/lib/cjs/components/livechatwidget/common/initWebChatComposer.js +4 -1
- package/lib/cjs/components/livechatwidget/common/startChat.js +1 -1
- package/lib/cjs/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +5 -1
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/callActionMiddleware.js +42 -0
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/customEventMiddleware.js +41 -0
- package/lib/cjs/contexts/common/CustomEventType.js +1 -0
- package/lib/cjs/firstresponselatency/FirstMessageTrackerFromBot.js +101 -36
- package/lib/cjs/firstresponselatency/FirstResponseLatencyTracker.js +39 -21
- package/lib/cjs/firstresponselatency/util.js +12 -8
- package/lib/esm/common/Constants.js +6 -0
- package/lib/esm/common/telemetry/TelemetryConstants.js +1 -0
- package/lib/esm/common/utils.js +17 -0
- package/lib/esm/components/chatbuttonstateful/ChatButtonStateful.js +4 -4
- package/lib/esm/components/livechatwidget/common/createMarkdown.js +54 -1
- package/lib/esm/components/livechatwidget/common/customEventHandler.js +45 -0
- package/lib/esm/components/livechatwidget/common/initWebChatComposer.js +4 -1
- package/lib/esm/components/livechatwidget/common/startChat.js +1 -1
- package/lib/esm/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +5 -1
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/callActionMiddleware.js +36 -0
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/customEventMiddleware.js +33 -0
- package/lib/esm/contexts/common/CustomEventType.js +1 -0
- package/lib/esm/firstresponselatency/FirstMessageTrackerFromBot.js +101 -36
- package/lib/esm/firstresponselatency/FirstResponseLatencyTracker.js +39 -21
- package/lib/esm/firstresponselatency/util.js +12 -8
- package/lib/types/common/Constants.d.ts +6 -0
- package/lib/types/common/telemetry/TelemetryConstants.d.ts +1 -0
- package/lib/types/common/utils.d.ts +3 -0
- package/lib/types/components/livechatwidget/common/customEventHandler.d.ts +4 -0
- package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/callActionMiddleware.d.ts +8 -0
- package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/customEventMiddleware.d.ts +22 -0
- package/lib/types/contexts/common/CustomEventType.d.ts +6 -0
- package/lib/types/firstresponselatency/FirstResponseLatencyTracker.d.ts +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -223,6 +223,51 @@ const customizedFooterProp: IFooterProps = {
|
|
|
223
223
|
|
|
224
224
|
> :pushpin: Note that [WebChat hooks](https://github.com/microsoft/BotFramework-WebChat/blob/main/docs/HOOKS.md) can also be used in any custom components.
|
|
225
225
|
|
|
226
|
+
#### Bidirectional Custom Events
|
|
227
|
+
- Sending events from a hosting web page to bots/agents
|
|
228
|
+
- Register a function to post event
|
|
229
|
+
```js
|
|
230
|
+
//define sendCustomEvent function
|
|
231
|
+
const sendCustomEvent = (payload) => {
|
|
232
|
+
const customEvent = {
|
|
233
|
+
eventName: "sendCustomEvent",
|
|
234
|
+
payload
|
|
235
|
+
};
|
|
236
|
+
BroadcastService.postMessage(customEvent);
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
//attach the sendCustomEvent function to window object
|
|
240
|
+
window["sendCustomEvent"] = sendCustomEvent;
|
|
241
|
+
|
|
242
|
+
//invoke the sendCustomEvent function with some customized payload
|
|
243
|
+
window.sendCustomEvent({
|
|
244
|
+
customEventName: "TestEvent",
|
|
245
|
+
customEventValue: {
|
|
246
|
+
boolVar: true,
|
|
247
|
+
displayableVar: {
|
|
248
|
+
isDisplayable: true,
|
|
249
|
+
value: "From C2: "+ new Date().toISOString()
|
|
250
|
+
},
|
|
251
|
+
numberVar: -10.5,
|
|
252
|
+
stringVar: "Hello from C2 str: " + new Date().toISOString()
|
|
253
|
+
}
|
|
254
|
+
})
|
|
255
|
+
```
|
|
256
|
+
- Receiving events from bots/agents
|
|
257
|
+
```js
|
|
258
|
+
//define setOnCustomEvent function
|
|
259
|
+
const setOnCustomEvent = (callback) => {
|
|
260
|
+
BroadcastService.getMessageByEventName("onCustomEvent").subscribe((event) => {
|
|
261
|
+
if (event && typeof callback === "function") {
|
|
262
|
+
callback(event);
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
//set callback function
|
|
268
|
+
setOnCustomEvent((event) => console.log(event));
|
|
269
|
+
```
|
|
270
|
+
|
|
226
271
|
## See Also
|
|
227
272
|
|
|
228
273
|
[Customizations Dev Guide](https://github.com/microsoft/omnichannel-chat-widget/blob/main/docs/customizations/getstarted.md)\
|
|
@@ -232,4 +277,4 @@ const customizedFooterProp: IFooterProps = {
|
|
|
232
277
|
[How to Add Visual Regression Tests](https://github.com/microsoft/omnichannel-chat-widget/blob/main/docs/VisualRegressionTestingGuide.md)\
|
|
233
278
|
[Security](https://github.com/microsoft/omnichannel-chat-widget/blob/main/SECURITY.md)\
|
|
234
279
|
[Third Party Cookie Support](https://github.com/microsoft/omnichannel-chat-widget/blob/main/docs/Tpc.md)\
|
|
235
|
-
[Storybook](https://microsoft.github.io/omnichannel-chat-widget/docs/storybook/)
|
|
280
|
+
[Storybook](https://microsoft.github.io/omnichannel-chat-widget/docs/storybook/)
|
|
@@ -32,6 +32,7 @@ _defineProperty(Constants, "webchatChannelId", "webchat");
|
|
|
32
32
|
_defineProperty(Constants, "markdown", "markdown");
|
|
33
33
|
_defineProperty(Constants, "actionType", "actionType");
|
|
34
34
|
_defineProperty(Constants, "markDownSystemMessageClass", "webchat__basic-transcript__activity-markdown-body");
|
|
35
|
+
_defineProperty(Constants, "MARKDOWN_LIST_INDENTATION", " ");
|
|
35
36
|
_defineProperty(Constants, "String", "string");
|
|
36
37
|
_defineProperty(Constants, "ChatMessagesJson", "chatMessagesJson");
|
|
37
38
|
_defineProperty(Constants, "truePascal", "True");
|
|
@@ -131,6 +132,11 @@ _defineProperty(Constants, "InitContextParamsResponse", "initContextParamsRespon
|
|
|
131
132
|
_defineProperty(Constants, "OCOriginalMessageId", "OriginalMessageId");
|
|
132
133
|
_defineProperty(Constants, "WebchatSequenceIdAttribute", "webchat:sequence-id");
|
|
133
134
|
_defineProperty(Constants, "MessageSequenceIdOverride", "MessageSequenceIdOverride");
|
|
135
|
+
_defineProperty(Constants, "sendCustomEvent", "sendCustomEvent");
|
|
136
|
+
_defineProperty(Constants, "onCustomEvent", "onCustomEvent");
|
|
137
|
+
_defineProperty(Constants, "customEventName", "customEventName");
|
|
138
|
+
_defineProperty(Constants, "customEventValue", "customEventValue");
|
|
139
|
+
_defineProperty(Constants, "Hidden", "Hidden");
|
|
134
140
|
_defineProperty(Constants, "EndConversationDueToOverflow", "endconversationduetooverflow");
|
|
135
141
|
const Regex = (_class = /*#__PURE__*/_createClass(function Regex() {
|
|
136
142
|
_classCallCheck(this, Regex);
|
|
@@ -229,6 +229,7 @@ exports.TelemetryEvent = TelemetryEvent;
|
|
|
229
229
|
TelemetryEvent["SystemMessageReceived"] = "SystemMessageReceived";
|
|
230
230
|
TelemetryEvent["RehydrateMessageReceived"] = "RehydrateMessageReceived";
|
|
231
231
|
TelemetryEvent["CustomContextReceived"] = "CustomContextReceived";
|
|
232
|
+
TelemetryEvent["CustomEventAction"] = "CustomEventAction";
|
|
232
233
|
TelemetryEvent["NetworkDisconnected"] = "NetworkDisconnected";
|
|
233
234
|
TelemetryEvent["NetworkReconnected"] = "NetworkReconnected";
|
|
234
235
|
TelemetryEvent["LinkModePostChatWorkflowStarted"] = "LinkModePostChatWorkflowStarted";
|
package/lib/cjs/common/utils.js
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.getConversationDetailsCall = exports.getBroadcastChannelName = exports.formatTemplateString = exports.findParentFocusableElementsWithoutChildContainer = exports.findAllFocusableElement = exports.extractPreChatSurveyResponseValues = exports.escapeHtml = exports.debounceLeading = exports.createTimer = exports.createFileAndDownload = exports.checkContactIdError = exports.changeLanguageCodeFormatForWebChat = exports.addDelayInMs = void 0;
|
|
6
|
+
exports.getCustomEventValue = exports.getConversationDetailsCall = exports.getBroadcastChannelName = exports.formatTemplateString = exports.findParentFocusableElementsWithoutChildContainer = exports.findAllFocusableElement = exports.extractPreChatSurveyResponseValues = exports.escapeHtml = exports.debounceLeading = exports.createTimer = exports.createFileAndDownload = exports.checkContactIdError = exports.changeLanguageCodeFormatForWebChat = exports.addDelayInMs = void 0;
|
|
7
7
|
exports.getDeviceType = getDeviceType;
|
|
8
8
|
exports.getWidgetEndChatEventName = exports.getWidgetCacheIdfromProps = exports.getWidgetCacheId = exports.getTimestampHourMinute = exports.getStateFromCache = exports.getLocaleDirection = exports.getIconText = exports.getDomain = void 0;
|
|
9
9
|
exports.isEndConversationDueToOverflowActivity = isEndConversationDueToOverflowActivity;
|
|
10
|
-
exports.setTabIndices = exports.setOcUserAgent = exports.setFocusOnSendBox = exports.setFocusOnElement = exports.preventFocusToMoveOutOfElement = exports.parseLowerCaseString = exports.parseAdaptiveCardPayload = exports.newGuid = exports.isUndefinedOrEmpty = exports.isThisSessionPopout = exports.isNullOrUndefined = exports.isNullOrEmptyString = void 0;
|
|
10
|
+
exports.setTabIndices = exports.setOcUserAgent = exports.setFocusOnSendBox = exports.setFocusOnElement = exports.preventFocusToMoveOutOfElement = exports.parseLowerCaseString = exports.parseAdaptiveCardPayload = exports.newGuid = exports.isValidCustomEvent = exports.isUndefinedOrEmpty = exports.isThisSessionPopout = exports.isNullOrUndefined = exports.isNullOrEmptyString = void 0;
|
|
11
11
|
var _Constants = require("./Constants");
|
|
12
12
|
var _TelemetryConstants = require("./telemetry/TelemetryConstants");
|
|
13
13
|
var _omnichannelChatComponents = require("@microsoft/omnichannel-chat-components");
|
|
@@ -497,6 +497,25 @@ function getDeviceType() {
|
|
|
497
497
|
return "standard";
|
|
498
498
|
}
|
|
499
499
|
}
|
|
500
|
+
|
|
501
|
+
//Bots expect a payload containing:
|
|
502
|
+
//1. customEventName: this should be string describe the event name
|
|
503
|
+
//2. customEventValue: given the value is from customer with unknown type, it is required to stringify the payload later
|
|
504
|
+
const isValidCustomEvent = payload => {
|
|
505
|
+
if (_Constants.Constants.customEventName in payload && payload.customEventName && typeof payload.customEventName === _Constants.Constants.String && _Constants.Constants.customEventValue in payload && payload.customEventValue) return true;
|
|
506
|
+
return false;
|
|
507
|
+
};
|
|
508
|
+
exports.isValidCustomEvent = isValidCustomEvent;
|
|
509
|
+
const getCustomEventValue = customEventPayload => {
|
|
510
|
+
let returnVal = "";
|
|
511
|
+
try {
|
|
512
|
+
returnVal = typeof customEventPayload.customEventValue === _Constants.Constants.String ? customEventPayload.customEventValue : JSON.stringify(customEventPayload.customEventValue);
|
|
513
|
+
} catch (error) {
|
|
514
|
+
console.error(error);
|
|
515
|
+
}
|
|
516
|
+
return returnVal;
|
|
517
|
+
};
|
|
518
|
+
exports.getCustomEventValue = getCustomEventValue;
|
|
500
519
|
function isEndConversationDueToOverflowActivity(activity) {
|
|
501
520
|
var _activity$channelData, _activity$channelData2;
|
|
502
521
|
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.Constants.EndConversationDueToOverflow);
|
|
@@ -74,7 +74,6 @@ const ChatButtonStateful = props => {
|
|
|
74
74
|
};
|
|
75
75
|
const outOfOfficeStyleProps = Object.assign({}, _defaultOutOfOfficeChatButtonStyleProps.defaultOutOfOfficeChatButtonStyleProps, outOfOfficeButtonProps === null || outOfOfficeButtonProps === void 0 ? void 0 : outOfOfficeButtonProps.styleProps);
|
|
76
76
|
const controlProps = {
|
|
77
|
-
...(buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.controlProps),
|
|
78
77
|
id: "oc-lcw-chat-button",
|
|
79
78
|
dir: state.domainStates.globalDir,
|
|
80
79
|
titleText: "Let's Chat!",
|
|
@@ -83,7 +82,8 @@ const ChatButtonStateful = props => {
|
|
|
83
82
|
unreadMessageCount: state.appStates.unreadMessageCount ? state.appStates.unreadMessageCount > _Constants.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",
|
|
84
83
|
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,
|
|
85
84
|
// Regular chat button onClick - this will always take precedence
|
|
86
|
-
onClick: () => ref.current()
|
|
85
|
+
onClick: () => ref.current(),
|
|
86
|
+
...(buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.controlProps)
|
|
87
87
|
};
|
|
88
88
|
const outOfOfficeControlProps = {
|
|
89
89
|
// Only take specific properties from outOfOfficeButtonProps, never onClick
|
|
@@ -92,7 +92,6 @@ const ChatButtonStateful = props => {
|
|
|
92
92
|
titleText: (outOfOfficeButtonProps === null || outOfOfficeButtonProps === void 0 ? void 0 : (_outOfOfficeButtonPro = outOfOfficeButtonProps.controlProps) === null || _outOfOfficeButtonPro === void 0 ? void 0 : _outOfOfficeButtonPro.titleText) || "We're Offline",
|
|
93
93
|
subtitleText: (outOfOfficeButtonProps === null || outOfOfficeButtonProps === void 0 ? void 0 : (_outOfOfficeButtonPro2 = outOfOfficeButtonProps.controlProps) === null || _outOfOfficeButtonPro2 === void 0 ? void 0 : _outOfOfficeButtonPro2.subtitleText) || "No agents available",
|
|
94
94
|
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,
|
|
95
|
-
...(outOfOfficeButtonProps === null || outOfOfficeButtonProps === void 0 ? void 0 : outOfOfficeButtonProps.controlProps),
|
|
96
95
|
// Out-of-office specific onClick - this will ALWAYS take precedence
|
|
97
96
|
onClick: () => {
|
|
98
97
|
if (state.appStates.isMinimized) {
|
|
@@ -105,7 +104,8 @@ const ChatButtonStateful = props => {
|
|
|
105
104
|
type: _LiveChatWidgetActionType.LiveChatWidgetActionType.SET_CONVERSATION_STATE,
|
|
106
105
|
payload: _ConversationState.ConversationState.OutOfOffice
|
|
107
106
|
});
|
|
108
|
-
}
|
|
107
|
+
},
|
|
108
|
+
...(outOfOfficeButtonProps === null || outOfOfficeButtonProps === void 0 ? void 0 : outOfOfficeButtonProps.controlProps)
|
|
109
109
|
};
|
|
110
110
|
(0, _react.useEffect)(() => {
|
|
111
111
|
_TelemetryHelper.TelemetryHelper.logLoadingEvent(_TelemetryConstants.LogLevel.INFO, {
|
|
@@ -34,12 +34,65 @@ const createMarkdown = (disableMarkdownMessageFormatting, disableNewLineMarkdown
|
|
|
34
34
|
// Rule to process html blocks and paragraphs
|
|
35
35
|
"html_inline",
|
|
36
36
|
// Rule to process html tags
|
|
37
|
-
"newline"
|
|
37
|
+
"newline",
|
|
38
|
+
// Rule to proceess '\n'
|
|
39
|
+
"list" // Enable list parsing rule
|
|
38
40
|
]);
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
markdown.disable(["strikethrough"]);
|
|
42
44
|
|
|
45
|
+
// Custom plugin to fix numbered list continuity
|
|
46
|
+
markdown.use(function (md) {
|
|
47
|
+
const originalRender = md.render.bind(md);
|
|
48
|
+
const originalRenderInline = md.renderInline.bind(md);
|
|
49
|
+
function preprocessText(text) {
|
|
50
|
+
// Handle numbered lists that come with double line breaks (knowledge article format)
|
|
51
|
+
// This ensures proper continuous numbering instead of separate lists
|
|
52
|
+
|
|
53
|
+
let result = text;
|
|
54
|
+
|
|
55
|
+
// Only process if the text contains the double line break pattern
|
|
56
|
+
// But exclude simple numbered lists (where content after \n\n starts with another number)
|
|
57
|
+
if (!/\d+\.\s+.*?\n\n(?!\d+\.\s)[\s\S]*?(?:\n\n\d+\.|\s*$)/.test(text)) {
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Convert "1. Item\n\nContent\n\n2. Item" to proper markdown list format
|
|
62
|
+
// Use improved pattern with negative lookahead to exclude cases where content starts with numbered list
|
|
63
|
+
const listPattern = /(\d+\.\s+[^\n]+)(\n\n(?!\d+\.\s)[\s\S]*?)(?=\n\n\d+\.|\s*$)/g;
|
|
64
|
+
if (listPattern.test(result)) {
|
|
65
|
+
// Reset regex state for actual replacement
|
|
66
|
+
listPattern.lastIndex = 0;
|
|
67
|
+
result = result.replace(listPattern, (match, listItem, content) => {
|
|
68
|
+
if (!content) {
|
|
69
|
+
return match;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Format content with proper indentation
|
|
73
|
+
const cleanContent = content.substring(2); // Remove leading \n\n
|
|
74
|
+
const lines = cleanContent.split("\n");
|
|
75
|
+
const indentedContent = lines.map(line => line.trim() ? `${_Constants.Constants.MARKDOWN_LIST_INDENTATION}${line}` : "").join("\n");
|
|
76
|
+
const lineBreak = disableNewLineMarkdownSupport ? "\n" : "\n\n";
|
|
77
|
+
return `${listItem}${lineBreak}${indentedContent}`;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
84
|
+
md.render = function (text, env) {
|
|
85
|
+
const processedText = preprocessText(text);
|
|
86
|
+
return originalRender(processedText, env);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
90
|
+
md.renderInline = function (text, env) {
|
|
91
|
+
const processedText = preprocessText(text);
|
|
92
|
+
return originalRenderInline(processedText, env);
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
|
|
43
96
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
44
97
|
markdown.use(_markdownItForInline.default, "url_new_win", "link_open", function (tokens, idx, env) {
|
|
45
98
|
const targetAttrIndex = tokens[idx].attrIndex(_Constants.Constants.Target);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.subscribeToSendCustomEvent = exports.customEventCallback = void 0;
|
|
7
|
+
var _Constants = require("../../../common/Constants");
|
|
8
|
+
var _TelemetryHelper = require("../../../common/telemetry/TelemetryHelper");
|
|
9
|
+
var _TelemetryConstants = require("../../../common/telemetry/TelemetryConstants");
|
|
10
|
+
var _utils = require("../../../common/utils");
|
|
11
|
+
const customEventCallback = facadeChatSDK => event => {
|
|
12
|
+
if (!(_Constants.Constants.payload in event)) return;
|
|
13
|
+
if ((0, _utils.isValidCustomEvent)(event.payload)) {
|
|
14
|
+
const customEventPayload = event.payload;
|
|
15
|
+
try {
|
|
16
|
+
const customEventValueStr = (0, _utils.getCustomEventValue)(customEventPayload);
|
|
17
|
+
const customEventName = customEventPayload.customEventName;
|
|
18
|
+
const messageMeta = {
|
|
19
|
+
customEvent: _Constants.Constants.true,
|
|
20
|
+
customEventName: customEventName,
|
|
21
|
+
customEventValue: customEventValueStr
|
|
22
|
+
};
|
|
23
|
+
const messagePayload = {
|
|
24
|
+
content: "",
|
|
25
|
+
tags: [_Constants.Constants.Hidden],
|
|
26
|
+
metadata: messageMeta,
|
|
27
|
+
timestamp: new Date()
|
|
28
|
+
};
|
|
29
|
+
facadeChatSDK.sendMessage(messagePayload);
|
|
30
|
+
_TelemetryHelper.TelemetryHelper.logActionEventToAllTelemetry(_TelemetryConstants.LogLevel.DEBUG, {
|
|
31
|
+
Event: _TelemetryConstants.TelemetryEvent.CustomEventAction,
|
|
32
|
+
Description: "Sent customEvent.",
|
|
33
|
+
CustomProperties: {
|
|
34
|
+
customEventName,
|
|
35
|
+
lengthCustomEventValue: customEventValueStr.length
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
} catch (error) {
|
|
39
|
+
_TelemetryHelper.TelemetryHelper.logActionEventToAllTelemetry(_TelemetryConstants.LogLevel.ERROR, {
|
|
40
|
+
Event: _TelemetryConstants.TelemetryEvent.CustomEventAction,
|
|
41
|
+
Description: "Failed to process CustomEvent.",
|
|
42
|
+
ExceptionDetails: {
|
|
43
|
+
error
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
exports.customEventCallback = customEventCallback;
|
|
50
|
+
const subscribeToSendCustomEvent = (broadcastService, facadeChatSDK, customEventCallback) => {
|
|
51
|
+
broadcastService.getMessageByEventName(_Constants.Constants.sendCustomEvent).subscribe(customEventCallback(facadeChatSDK));
|
|
52
|
+
};
|
|
53
|
+
exports.subscribeToSendCustomEvent = subscribeToSendCustomEvent;
|
|
@@ -37,9 +37,12 @@ var _htmlPlayerMiddleware = _interopRequireDefault(require("../../webchatcontain
|
|
|
37
37
|
var _htmlTextMiddleware = _interopRequireDefault(require("../../webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/htmlTextMiddleware"));
|
|
38
38
|
var _preProcessingMiddleware = _interopRequireDefault(require("../../webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/preProcessingMiddleware"));
|
|
39
39
|
var _sanitizationMiddleware = _interopRequireDefault(require("../../webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/sanitizationMiddleware"));
|
|
40
|
+
var _callActionMiddleware = _interopRequireDefault(require("../../webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/callActionMiddleware"));
|
|
41
|
+
var _customEventMiddleware = _interopRequireDefault(require("../../webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/customEventMiddleware"));
|
|
40
42
|
var _ConversationState = require("../../../contexts/common/ConversationState");
|
|
41
43
|
var _createReducer = require("../../../contexts/createReducer");
|
|
42
44
|
var _queueOverflowHandlerMiddleware = require("../../webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/queueOverflowHandlerMiddleware");
|
|
45
|
+
var _omnichannelChatComponents = require("@microsoft/omnichannel-chat-components");
|
|
43
46
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
44
47
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
48
|
const initWebChatComposer = (props, state, dispatch, facadeChatSDK, endChat) => {
|
|
@@ -118,7 +121,7 @@ const initWebChatComposer = (props, state, dispatch, facadeChatSDK, endChat) =>
|
|
|
118
121
|
};
|
|
119
122
|
webChatStore = (0, _botframeworkWebchat.createStore)({},
|
|
120
123
|
//initial state
|
|
121
|
-
_preProcessingMiddleware.default, _attachmentProcessingMiddleware.default, (0, _attachmentUploadValidatorMiddleware.default)((_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), (0, _queueOverflowHandlerMiddleware.createQueueOverflowMiddleware)(state, dispatch), (0, _channelDataMiddleware.default)(addConversationalSurveyTagsCallback), (0, _conversationEndMiddleware.default)(conversationEndCallback, startConversationalSurveyCallback, endConversationalSurveyCallback), (0, _dataMaskingMiddleware.default)((_state$domainStates$l3 = state.domainStates.liveChatConfig) === null || _state$domainStates$l3 === void 0 ? void 0 : _state$domainStates$l3.DataMaskingInfo), _messageTimestampMiddleware.createMessageTimeStampMiddleware, _messageSequenceIdOverrideMiddleware.createMessageSequenceIdOverrideMiddleware, _gifUploadMiddleware.default, _htmlPlayerMiddleware.default, (0, _htmlTextMiddleware.default)(honorsTargetInHTMLLinks), (0, _maxMessageSizeValidator.default)(localizedTexts), _sanitizationMiddleware.default,
|
|
124
|
+
_preProcessingMiddleware.default, _attachmentProcessingMiddleware.default, (0, _attachmentUploadValidatorMiddleware.default)((_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), (0, _customEventMiddleware.default)(_omnichannelChatComponents.BroadcastService), (0, _queueOverflowHandlerMiddleware.createQueueOverflowMiddleware)(state, dispatch), (0, _channelDataMiddleware.default)(addConversationalSurveyTagsCallback), (0, _conversationEndMiddleware.default)(conversationEndCallback, startConversationalSurveyCallback, endConversationalSurveyCallback), (0, _dataMaskingMiddleware.default)((_state$domainStates$l3 = state.domainStates.liveChatConfig) === null || _state$domainStates$l3 === void 0 ? void 0 : _state$domainStates$l3.DataMaskingInfo), _messageTimestampMiddleware.createMessageTimeStampMiddleware, _messageSequenceIdOverrideMiddleware.createMessageSequenceIdOverrideMiddleware, _gifUploadMiddleware.default, _htmlPlayerMiddleware.default, (0, _htmlTextMiddleware.default)(honorsTargetInHTMLLinks), (0, _maxMessageSizeValidator.default)(localizedTexts), _sanitizationMiddleware.default, (0, _callActionMiddleware.default)(),
|
|
122
125
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
123
126
|
...(((_props$webChatContain7 = props.webChatContainerProps) === null || _props$webChatContain7 === void 0 ? void 0 : _props$webChatContain7.storeMiddlewares) ?? []));
|
|
124
127
|
_WebChatStoreLoader.WebChatStoreLoader.store = webChatStore;
|
|
@@ -154,7 +154,6 @@ const setPreChatAndInitiateChat = async (facadeChatSDK, dispatch, setAdapter, is
|
|
|
154
154
|
const optionalParams = {
|
|
155
155
|
isProactiveChat
|
|
156
156
|
};
|
|
157
|
-
(0, _FirstMessageTrackerFromBot.createTrackingForFirstMessage)();
|
|
158
157
|
await initStartChat(facadeChatSDK, dispatch, setAdapter, state, props, optionalParams);
|
|
159
158
|
};
|
|
160
159
|
|
|
@@ -206,6 +205,7 @@ const initStartChat = async (facadeChatSDK, dispatch, setAdapter, state, props,
|
|
|
206
205
|
const startChatOptionalParams = Object.assign({}, params, optionalParams, defaultOptionalParams);
|
|
207
206
|
// startTime is used to determine if a message is history or new, better to be set before creating the adapter to get bandwidth
|
|
208
207
|
const startTime = new Date().getTime();
|
|
208
|
+
(0, _FirstMessageTrackerFromBot.createTrackingForFirstMessage)();
|
|
209
209
|
await facadeChatSDK.startChat(startChatOptionalParams);
|
|
210
210
|
isStartChatSuccessful = true;
|
|
211
211
|
await createAdapterAndSubscribe(facadeChatSDK, dispatch, setAdapter, startTime, props);
|
|
@@ -57,6 +57,7 @@ var _startProactiveChat = require("../common/startProactiveChat");
|
|
|
57
57
|
var _useChatAdapterStore = _interopRequireDefault(require("../../../hooks/useChatAdapterStore"));
|
|
58
58
|
var _useChatContextStore = _interopRequireDefault(require("../../../hooks/useChatContextStore"));
|
|
59
59
|
var _useFacadeChatSDKStore = _interopRequireDefault(require("../../../hooks/useFacadeChatSDKStore"));
|
|
60
|
+
var _customEventHandler = require("../common/customEventHandler");
|
|
60
61
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
61
62
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
62
63
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
@@ -421,7 +422,7 @@ const LiveChatWidgetStateful = props => {
|
|
|
421
422
|
_omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.BroadcastEvent.StartChat).subscribe(msg => {
|
|
422
423
|
var _msg$payload5, _msg$payload6, _msg$payload7, _msg$payload9, _inMemoryState$appSta2, _inMemoryState$appSta3, _inMemoryState$appSta4;
|
|
423
424
|
// If chat is out of operating hours chat widget sets the conversation state to OutOfOffice.
|
|
424
|
-
if (state.appStates.outsideOperatingHours
|
|
425
|
+
if (state.appStates.outsideOperatingHours && state.appStates.conversationState !== _ConversationState.ConversationState.Active) {
|
|
425
426
|
dispatch({
|
|
426
427
|
type: _LiveChatWidgetActionType.LiveChatWidgetActionType.SET_MINIMIZED,
|
|
427
428
|
payload: false
|
|
@@ -588,6 +589,9 @@ const LiveChatWidgetStateful = props => {
|
|
|
588
589
|
}
|
|
589
590
|
});
|
|
590
591
|
|
|
592
|
+
// subscribe custom event
|
|
593
|
+
(0, _customEventHandler.subscribeToSendCustomEvent)(_omnichannelChatComponents.BroadcastService, facadeChatSDK, _customEventHandler.customEventCallback);
|
|
594
|
+
|
|
591
595
|
// Check for TPC and log in telemetry if blocked
|
|
592
596
|
(0, _defaultClientDataStoreProvider.isCookieAllowed)();
|
|
593
597
|
return () => {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _WebChatActionType = require("../../enums/WebChatActionType");
|
|
8
|
+
/******
|
|
9
|
+
* CallActionMiddleware
|
|
10
|
+
*
|
|
11
|
+
* Intercepts custom call actions and handles tel: URL navigation
|
|
12
|
+
******/
|
|
13
|
+
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
|
|
15
|
+
const createCallActionMiddleware = () => () => next => action => {
|
|
16
|
+
// Intercept incoming activities to modify suggested actions with call type
|
|
17
|
+
if (action.type === _WebChatActionType.WebChatActionType.DIRECT_LINE_INCOMING_ACTIVITY) {
|
|
18
|
+
var _action$payload, _activity$suggestedAc;
|
|
19
|
+
const activity = (_action$payload = action.payload) === null || _action$payload === void 0 ? void 0 : _action$payload.activity;
|
|
20
|
+
|
|
21
|
+
// Check if activity has suggested actions with call type
|
|
22
|
+
if (activity !== null && activity !== void 0 && (_activity$suggestedAc = activity.suggestedActions) !== null && _activity$suggestedAc !== void 0 && _activity$suggestedAc.actions) {
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
24
|
+
activity.suggestedActions.actions = activity.suggestedActions.actions.map(suggestedAction => {
|
|
25
|
+
if (suggestedAction.type === "call") {
|
|
26
|
+
// Convert call action to openUrl with encoded tel URL scheme
|
|
27
|
+
const telUrl = suggestedAction.value;
|
|
28
|
+
const convertedAction = {
|
|
29
|
+
...suggestedAction,
|
|
30
|
+
type: "openUrl",
|
|
31
|
+
value: `tel:${telUrl}`
|
|
32
|
+
};
|
|
33
|
+
return convertedAction;
|
|
34
|
+
}
|
|
35
|
+
return suggestedAction;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return next(action);
|
|
40
|
+
};
|
|
41
|
+
var _default = createCallActionMiddleware;
|
|
42
|
+
exports.default = _default;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isValidCustomEvent = exports.default = void 0;
|
|
7
|
+
var _Constants = require("../../../../../common/Constants");
|
|
8
|
+
var _WebChatActionType = require("../../enums/WebChatActionType");
|
|
9
|
+
/******
|
|
10
|
+
* CustomEventMiddleware
|
|
11
|
+
*
|
|
12
|
+
* This middleware is invoked when a custom event is received.
|
|
13
|
+
* The callback is then invoked to handle the custom event.
|
|
14
|
+
******/
|
|
15
|
+
|
|
16
|
+
const isValidCustomEvent = activity => {
|
|
17
|
+
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;
|
|
18
|
+
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.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.Constants.true && (activity === null || activity === void 0 ? void 0 : (_activity$from = activity.from) === null || _activity$from === void 0 ? void 0 : _activity$from.role) !== _Constants.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.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);
|
|
19
|
+
};
|
|
20
|
+
exports.isValidCustomEvent = isValidCustomEvent;
|
|
21
|
+
const createCustomEventMiddleware = broadcastservice => () => next => action => {
|
|
22
|
+
var _action$payload;
|
|
23
|
+
if ((action === null || action === void 0 ? void 0 : action.type) == _WebChatActionType.WebChatActionType.DIRECT_LINE_INCOMING_ACTIVITY && (_action$payload = action.payload) !== null && _action$payload !== void 0 && _action$payload.activity) {
|
|
24
|
+
const activity = action.payload.activity;
|
|
25
|
+
if (isValidCustomEvent(activity)) {
|
|
26
|
+
const customEvent = {
|
|
27
|
+
eventName: _Constants.Constants.onCustomEvent,
|
|
28
|
+
payload: {
|
|
29
|
+
messageId: activity.messageid ?? activity.id,
|
|
30
|
+
customEventName: activity.channelData.metadata.customEventName,
|
|
31
|
+
customEventValue: activity.channelData.metadata.customEventValue
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
broadcastservice.postMessage(customEvent);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return next(action);
|
|
39
|
+
};
|
|
40
|
+
var _default = createCustomEventMiddleware;
|
|
41
|
+
exports.default = _default;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -12,54 +12,96 @@ var _util = require("./util");
|
|
|
12
12
|
// with different timeline, therefore this is a functional approach to track the events, instead of a class based approach
|
|
13
13
|
const createTrackingForFirstMessage = () => {
|
|
14
14
|
// Reset the tracking variables
|
|
15
|
-
let
|
|
16
|
-
let stopTracking = false;
|
|
15
|
+
let isTracking = false;
|
|
17
16
|
let startTime = 0;
|
|
18
17
|
let stopTime = 0;
|
|
19
|
-
let stopTrackingMessage;
|
|
18
|
+
let stopTrackingMessage = null;
|
|
20
19
|
let flag = false;
|
|
20
|
+
let trackingTimeoutId;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Checks if the message payload is from a valid sender (not an agent).
|
|
24
|
+
* Returns false if the message is from an agent (tag 'public'), true otherwise.
|
|
25
|
+
*/
|
|
21
26
|
const isMessageFromValidSender = payload => {
|
|
22
27
|
var _payload$tags;
|
|
23
|
-
// agent scenario
|
|
24
28
|
if (payload !== null && payload !== void 0 && (_payload$tags = payload.tags) !== null && _payload$tags !== void 0 && _payload$tags.includes("public")) {
|
|
25
29
|
return false;
|
|
26
30
|
}
|
|
27
31
|
return true;
|
|
28
32
|
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Listener for widget load completion event.
|
|
36
|
+
* Starts tracking the time for the first bot message after widget loads.
|
|
37
|
+
* Sets a 5-second timeout to auto-reset if no bot message is received.
|
|
38
|
+
*/
|
|
29
39
|
const widgetLoadListener = _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.TelemetryEvent.WidgetLoadComplete).subscribe(() => {
|
|
30
|
-
if (
|
|
31
|
-
|
|
40
|
+
if (isTracking) return;
|
|
41
|
+
isTracking = true;
|
|
32
42
|
startTime = new Date().getTime();
|
|
43
|
+
// Start a 5-second timeout to auto-stop tracking if not stopped
|
|
44
|
+
if (trackingTimeoutId) {
|
|
45
|
+
clearTimeout(trackingTimeoutId);
|
|
46
|
+
}
|
|
47
|
+
trackingTimeoutId = setTimeout(() => {
|
|
48
|
+
if (isTracking) {
|
|
49
|
+
// Reset state and disengage, no telemetry or FMLTrackingCompleted
|
|
50
|
+
isTracking = false;
|
|
51
|
+
startTime = 0;
|
|
52
|
+
stopTime = 0;
|
|
53
|
+
stopTrackingMessage = null;
|
|
54
|
+
trackingTimeoutId = undefined;
|
|
55
|
+
disconnectListener();
|
|
56
|
+
}
|
|
57
|
+
}, 10000); //adding more time since it meassures from widget load complete till message received
|
|
33
58
|
});
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Listener for new bot message event.
|
|
62
|
+
* If a valid bot message is received, stops tracking and logs telemetry.
|
|
63
|
+
* If the message is invalid, resets and disengages listeners.
|
|
64
|
+
*/
|
|
34
65
|
const newMessageListener = _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.BroadcastEvent.NewMessageReceived).subscribe(message => {
|
|
35
66
|
const payload = message.payload;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
stopTime = new Date().getTime();
|
|
43
|
-
const elapsedTime = stopTime - startTime;
|
|
44
|
-
stopTracking = true;
|
|
45
|
-
stopTrackingMessage = (0, _util.createTrackingMessage)(payload, "botMessage");
|
|
46
|
-
notifyFMLTrackingCompleted();
|
|
47
|
-
_TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
|
|
48
|
-
Event: _TelemetryConstants.TelemetryEvent.BotFirstMessageLapTrack,
|
|
49
|
-
Description: "First Message from Bot latency tracking",
|
|
50
|
-
CustomProperties: {
|
|
51
|
-
elapsedTime,
|
|
52
|
-
widgetLoadedAt: startTime,
|
|
53
|
-
botMessage: stopTrackingMessage
|
|
54
|
-
}
|
|
55
|
-
});
|
|
67
|
+
if (!isMessageFromValidSender(payload)) {
|
|
68
|
+
// If not valid, stop everything and clean up
|
|
69
|
+
isTracking = false;
|
|
70
|
+
if (trackingTimeoutId) {
|
|
71
|
+
clearTimeout(trackingTimeoutId);
|
|
72
|
+
trackingTimeoutId = undefined;
|
|
56
73
|
}
|
|
74
|
+
disconnectListener();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (isTracking) {
|
|
78
|
+
isTracking = false;
|
|
79
|
+
// Clear the timeout if it exists
|
|
80
|
+
if (trackingTimeoutId) {
|
|
81
|
+
clearTimeout(trackingTimeoutId);
|
|
82
|
+
trackingTimeoutId = undefined;
|
|
83
|
+
}
|
|
84
|
+
stopTime = new Date().getTime();
|
|
85
|
+
const elapsedTime = stopTime - startTime;
|
|
86
|
+
stopTrackingMessage = (0, _util.createTrackingMessage)(payload, "botMessage");
|
|
87
|
+
notifyFMLTrackingCompleted();
|
|
88
|
+
_TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
|
|
89
|
+
Event: _TelemetryConstants.TelemetryEvent.BotFirstMessageLapTrack,
|
|
90
|
+
Description: "First Message from Bot latency tracking",
|
|
91
|
+
CustomProperties: {
|
|
92
|
+
elapsedTime,
|
|
93
|
+
widgetLoadedAt: startTime,
|
|
94
|
+
botMessage: stopTrackingMessage
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
disconnectListener();
|
|
57
98
|
}
|
|
58
|
-
|
|
59
|
-
// this track only first message, if coming from the bot or not
|
|
60
|
-
// the only difference is that it logs only those from bot
|
|
61
|
-
disconnectListener();
|
|
62
99
|
});
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Notifies that FML (First Message Latency) tracking is completed.
|
|
103
|
+
* Retries sending the completion event until acknowledged.
|
|
104
|
+
*/
|
|
63
105
|
const notifyFMLTrackingCompleted = () => {
|
|
64
106
|
ackListener();
|
|
65
107
|
// Retry sending until flag is true, but do not block the main thread
|
|
@@ -74,6 +116,11 @@ const createTrackingForFirstMessage = () => {
|
|
|
74
116
|
}
|
|
75
117
|
}, 100);
|
|
76
118
|
};
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Listener for FMLTrackingCompletedAck event.
|
|
122
|
+
* Sets the flag to true when acknowledgment is received.
|
|
123
|
+
*/
|
|
77
124
|
const ackListener = () => {
|
|
78
125
|
const listen = _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.BroadcastEvent.FMLTrackingCompletedAck).subscribe(() => {
|
|
79
126
|
flag = true;
|
|
@@ -83,22 +130,32 @@ const createTrackingForFirstMessage = () => {
|
|
|
83
130
|
|
|
84
131
|
// 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
|
|
85
132
|
// No need to keep listerning for tracking, enforcing disconnection for the listners
|
|
133
|
+
/**
|
|
134
|
+
* Listener for widget rehydration event.
|
|
135
|
+
* Resets tracking and disconnects listeners when widget is reloaded.
|
|
136
|
+
*/
|
|
86
137
|
const rehydrateListener = _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.TelemetryEvent.RehydrateMessageReceived).subscribe(() => {
|
|
87
|
-
|
|
88
|
-
stopTracking = false;
|
|
138
|
+
isTracking = false;
|
|
89
139
|
disconnectListener();
|
|
90
140
|
});
|
|
91
141
|
|
|
92
142
|
// 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
|
|
93
143
|
// No need to keep listerning for tracking, enforcing disconnection for the listners
|
|
144
|
+
/**
|
|
145
|
+
* Listener for history message event.
|
|
146
|
+
* Resets tracking and disconnects listeners when history is loaded.
|
|
147
|
+
*/
|
|
94
148
|
const historyListener = _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.BroadcastEvent.HistoryMessageReceived).subscribe(() => {
|
|
95
|
-
|
|
96
|
-
stopTracking = false;
|
|
149
|
+
isTracking = false;
|
|
97
150
|
disconnectListener();
|
|
98
151
|
});
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Listener for network disconnection event.
|
|
155
|
+
* Resets tracking, disconnects listeners, and logs a telemetry error.
|
|
156
|
+
*/
|
|
99
157
|
const offlineNetworkListener = _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.TelemetryEvent.NetworkDisconnected).subscribe(() => {
|
|
100
|
-
|
|
101
|
-
stopTracking = false;
|
|
158
|
+
isTracking = false;
|
|
102
159
|
disconnectListener();
|
|
103
160
|
_TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
|
|
104
161
|
Event: _TelemetryConstants.TelemetryEvent.BotFirstMessageLapTrackError,
|
|
@@ -107,7 +164,15 @@ const createTrackingForFirstMessage = () => {
|
|
|
107
164
|
});
|
|
108
165
|
|
|
109
166
|
// this is to ensure that we are not tracking messages that are not part of the current conversation
|
|
167
|
+
/**
|
|
168
|
+
* Disconnects all listeners and clears the tracking timeout.
|
|
169
|
+
* Used for cleanup when tracking is stopped or reset.
|
|
170
|
+
*/
|
|
110
171
|
const disconnectListener = () => {
|
|
172
|
+
if (trackingTimeoutId) {
|
|
173
|
+
clearTimeout(trackingTimeoutId);
|
|
174
|
+
trackingTimeoutId = undefined;
|
|
175
|
+
}
|
|
111
176
|
historyListener.unsubscribe();
|
|
112
177
|
rehydrateListener.unsubscribe();
|
|
113
178
|
newMessageListener.unsubscribe();
|