@microsoft/omnichannel-chat-widget 1.8.4-main.2e1ef38 → 1.8.4-main.40cbfab
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 +12 -3
- package/lib/cjs/common/telemetry/TelemetryConstants.js +8 -0
- package/lib/cjs/components/livechatwidget/common/ChatWidgetEvents.js +1 -0
- package/lib/cjs/components/livechatwidget/common/PersistentConversationHandler.js +9 -0
- package/lib/cjs/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +29 -1
- package/lib/cjs/components/webchatcontainerstateful/WebChatContainerStateful.js +11 -3
- package/lib/cjs/components/webchatcontainerstateful/common/activityConverters/convertPersistentChatHistoryMessageToActivity.js +92 -19
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/WebChatEventSubscribers.js +13 -2
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/LazyLoadActivity.js +160 -167
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.js +1 -1
- package/lib/cjs/controller/componentController.js +13 -1
- package/lib/cjs/plugins/newMessageEventHandler.js +20 -3
- package/lib/esm/common/telemetry/TelemetryConstants.js +8 -0
- package/lib/esm/components/livechatwidget/common/ChatWidgetEvents.js +1 -0
- package/lib/esm/components/livechatwidget/common/PersistentConversationHandler.js +9 -0
- package/lib/esm/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +29 -1
- package/lib/esm/components/webchatcontainerstateful/WebChatContainerStateful.js +11 -3
- package/lib/esm/components/webchatcontainerstateful/common/activityConverters/convertPersistentChatHistoryMessageToActivity.js +92 -19
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/WebChatEventSubscribers.js +13 -2
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/LazyLoadActivity.js +160 -171
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.js +1 -1
- package/lib/esm/controller/componentController.js +13 -1
- package/lib/esm/plugins/newMessageEventHandler.js +20 -3
- package/lib/types/common/telemetry/TelemetryConstants.d.ts +7 -1
- package/lib/types/components/livechatwidget/common/ChatWidgetEvents.d.ts +2 -1
- package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/LazyLoadActivity.d.ts +14 -38
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -177,7 +177,7 @@ Header's and Footer's child components consist of three parts:
|
|
|
177
177
|
1. "middleGroup" - adding child components in the middle of the Header/Footer
|
|
178
178
|
1. "rightGroup" - adding child components at the right of the Header/Footer
|
|
179
179
|
|
|
180
|
-
By default Header has the header icon and title on the left and minimize and close buttons on the right, and Footer has Download Transcript and Email Transcript buttons on the left and audio notification button on the right. These components can be overriden with [ComponentOverrides](#
|
|
180
|
+
By default Header has the header icon and title on the left and minimize and close buttons on the right, and Footer has Download Transcript and Email Transcript buttons on the left and audio notification button on the right. These components can be overriden with [ComponentOverrides](#componentoverrides). In addition, other custom child components can be added to both Header and Footer by creating custom react nodes and adding them to attributes "leftGroup", "middleGroup" or "rightGroup" of "controlProps".
|
|
181
181
|
|
|
182
182
|
```js
|
|
183
183
|
const buttonStyleProps: IButtonStyles = {
|
|
@@ -224,8 +224,10 @@ const customizedFooterProp: IFooterProps = {
|
|
|
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
226
|
#### Bidirectional Custom Events
|
|
227
|
+
|
|
227
228
|
- Sending events from a hosting web page to bots/agents
|
|
228
|
-
|
|
229
|
+
- Register a function to post event
|
|
230
|
+
|
|
229
231
|
```js
|
|
230
232
|
//define sendCustomEvent function
|
|
231
233
|
const sendCustomEvent = (payload) => {
|
|
@@ -253,7 +255,9 @@ const customizedFooterProp: IFooterProps = {
|
|
|
253
255
|
}
|
|
254
256
|
})
|
|
255
257
|
```
|
|
256
|
-
|
|
258
|
+
|
|
259
|
+
- Receiving events from bots/agents
|
|
260
|
+
|
|
257
261
|
```js
|
|
258
262
|
//define setOnCustomEvent function
|
|
259
263
|
const setOnCustomEvent = (callback) => {
|
|
@@ -269,8 +273,10 @@ const customizedFooterProp: IFooterProps = {
|
|
|
269
273
|
```
|
|
270
274
|
|
|
271
275
|
#### Trigger initiateEndChat event
|
|
276
|
+
|
|
272
277
|
Customer can trigger the initiateEndChat event via BroadcastService to end a chat session.
|
|
273
278
|
When needed, the payload below could be triggered:
|
|
279
|
+
|
|
274
280
|
```js
|
|
275
281
|
const endChatEvent = {
|
|
276
282
|
eventName: "InitiateEndChat",
|
|
@@ -283,18 +289,21 @@ BroadcastService.postMessage(endChatEvent);
|
|
|
283
289
|
|
|
284
290
|
The payload of the event is optional, only needed when force closing of a persistent chat session is not required.
|
|
285
291
|
When chat widget receives the event without any payload, it will:
|
|
292
|
+
|
|
286
293
|
1. set the widget to closed state, the widget panel will be minimized. Post chat survey will not be displayed.
|
|
287
294
|
2. trigger a sessionclose service network request to OmniChannel services.
|
|
288
295
|
|
|
289
296
|
If skipSessionCloseForPersistentChat is set to true. The session close network request will not be triggered, instead, if postChat survey is available, post chat survey will be displayed.
|
|
290
297
|
|
|
291
298
|
After successfully processed initiateEndChat event. The CloseChat event is broadcasted.
|
|
299
|
+
|
|
292
300
|
```js
|
|
293
301
|
BroadcastService.getMessageByEventName("CloseChat").subscribe(async (msg) => {
|
|
294
302
|
console.log("close chat received: ", msg);
|
|
295
303
|
//more actions to unmount component and resources
|
|
296
304
|
})
|
|
297
305
|
```
|
|
306
|
+
|
|
298
307
|
## See Also
|
|
299
308
|
|
|
300
309
|
[Customizations Dev Guide](https://github.com/microsoft/omnichannel-chat-widget/blob/main/docs/customizations/getstarted.md)\
|
|
@@ -318,10 +318,16 @@ exports.TelemetryEvent = TelemetryEvent;
|
|
|
318
318
|
TelemetryEvent["LCWLazyLoadNoMoreHistory"] = "LCWLazyLoadNoMoreHistory";
|
|
319
319
|
TelemetryEvent["LCWLazyLoadHistoryError"] = "LCWLazyLoadHistoryError";
|
|
320
320
|
TelemetryEvent["LCWLazyLoadDestroyed"] = "LCWLazyLoadDestroyed";
|
|
321
|
+
TelemetryEvent["LCWLazyLoadTriggerFired"] = "LCWLazyLoadTriggerFired";
|
|
322
|
+
TelemetryEvent["LCWLazyLoadBatchReceived"] = "LCWLazyLoadBatchReceived";
|
|
323
|
+
TelemetryEvent["LCWLazyLoadInitialLoadComplete"] = "LCWLazyLoadInitialLoadComplete";
|
|
324
|
+
TelemetryEvent["LCWLazyLoadScrollAnchorApplied"] = "LCWLazyLoadScrollAnchorApplied";
|
|
321
325
|
TelemetryEvent["SecureEventBusUnauthorizedDispatch"] = "SecureEventBusUnauthorizedDispatch";
|
|
322
326
|
TelemetryEvent["SecureEventBusListenerError"] = "SecureEventBusListenerError";
|
|
323
327
|
TelemetryEvent["SecureEventBusDispatchError"] = "SecureEventBusDispatchError";
|
|
324
328
|
TelemetryEvent["StartChatComplete"] = "StartChatComplete";
|
|
329
|
+
TelemetryEvent["AgentJoinedConversation"] = "AgentJoinedConversation";
|
|
330
|
+
TelemetryEvent["BrowserTabHidden"] = "BrowserTabHidden";
|
|
325
331
|
})(TelemetryEvent || (exports.TelemetryEvent = TelemetryEvent = {}));
|
|
326
332
|
let TelemetryConstants = /*#__PURE__*/function () {
|
|
327
333
|
function TelemetryConstants() {
|
|
@@ -392,6 +398,8 @@ let TelemetryConstants = /*#__PURE__*/function () {
|
|
|
392
398
|
case TelemetryEvent.SecureEventBusUnauthorizedDispatch:
|
|
393
399
|
case TelemetryEvent.SecureEventBusListenerError:
|
|
394
400
|
case TelemetryEvent.SecureEventBusDispatchError:
|
|
401
|
+
case TelemetryEvent.AgentJoinedConversation:
|
|
402
|
+
case TelemetryEvent.BrowserTabHidden:
|
|
395
403
|
return ScenarioType.ACTIONS;
|
|
396
404
|
case TelemetryEvent.StartChatSDKCall:
|
|
397
405
|
case TelemetryEvent.StartChatEventReceived:
|
|
@@ -10,6 +10,7 @@ var ChatWidgetEvents;
|
|
|
10
10
|
ChatWidgetEvents["FETCH_PERSISTENT_CHAT_HISTORY"] = "CHAT_WIDGET/FETCH_PERSISTENT_CHAT_HISTORY";
|
|
11
11
|
ChatWidgetEvents["NO_MORE_HISTORY_AVAILABLE"] = "CHAT_WIDGET/NO_MORE_HISTORY_AVAILABLE";
|
|
12
12
|
ChatWidgetEvents["HISTORY_LOAD_ERROR"] = "CHAT_WIDGET/HISTORY_LOAD_ERROR";
|
|
13
|
+
ChatWidgetEvents["HISTORY_BATCH_LOADED"] = "CHAT_WIDGET/HISTORY_BATCH_LOADED";
|
|
13
14
|
})(ChatWidgetEvents || (ChatWidgetEvents = {}));
|
|
14
15
|
var _default = ChatWidgetEvents;
|
|
15
16
|
exports.default = _default;
|
|
@@ -133,6 +133,12 @@ let PersistentConversationHandler = /*#__PURE__*/function () {
|
|
|
133
133
|
}
|
|
134
134
|
const messagesDescOrder = (_ref = [...messages]) === null || _ref === void 0 ? void 0 : _ref.reverse();
|
|
135
135
|
this.processHistoryMessages(messagesDescOrder);
|
|
136
|
+
|
|
137
|
+
// Signal that a batch of history messages has been added to the store.
|
|
138
|
+
// LazyLoadActivity subscribes to this to apply scroll anchoring after render.
|
|
139
|
+
(0, _dispatchCustomEvent.default)(_ChatWidgetEvents.default.HISTORY_BATCH_LOADED, {
|
|
140
|
+
messageCount: messages.length
|
|
141
|
+
});
|
|
136
142
|
_TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
|
|
137
143
|
Event: _TelemetryConstants.TelemetryEvent.LCWPersistentHistoryPullCompleted,
|
|
138
144
|
Description: "History pull completed successfully",
|
|
@@ -241,6 +247,9 @@ let PersistentConversationHandler = /*#__PURE__*/function () {
|
|
|
241
247
|
value: function processMessageToActivity(message) {
|
|
242
248
|
try {
|
|
243
249
|
const activity = (0, _convertPersistentChatHistoryMessageToActivity.default)(message);
|
|
250
|
+
if (!activity) {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
244
253
|
activity.id = activity.id || `activity-${this.count}`;
|
|
245
254
|
activity.channelData = {
|
|
246
255
|
...activity.channelData,
|
|
@@ -54,6 +54,7 @@ var _initConfirmationPropsComposer = require("../common/initConfirmationPropsCom
|
|
|
54
54
|
var _initWebChatComposer = require("../common/initWebChatComposer");
|
|
55
55
|
var _defaultCacheManager = require("../../../common/storage/default/defaultCacheManager");
|
|
56
56
|
var _setPostChatContextAndLoadSurvey = require("../common/setPostChatContextAndLoadSurvey");
|
|
57
|
+
var _liveChatConfigUtils = require("../common/liveChatConfigUtils");
|
|
57
58
|
var _startProactiveChat = require("../common/startProactiveChat");
|
|
58
59
|
var _useChatAdapterStore = _interopRequireDefault(require("../../../hooks/useChatAdapterStore"));
|
|
59
60
|
var _useChatContextStore = _interopRequireDefault(require("../../../hooks/useChatContextStore"));
|
|
@@ -736,7 +737,15 @@ const LiveChatWidgetStateful = props => {
|
|
|
736
737
|
if (state.appStates.isMinimized) {
|
|
737
738
|
_ActivityStreamHandler.ActivityStreamHandler.cork();
|
|
738
739
|
} else {
|
|
739
|
-
|
|
740
|
+
var _state$domainStates4;
|
|
741
|
+
const extendedChatConfig = state === null || state === void 0 ? void 0 : (_state$domainStates4 = state.domainStates) === null || _state$domainStates4 === void 0 ? void 0 : _state$domainStates4.liveChatConfig;
|
|
742
|
+
if ((0, _liveChatConfigUtils.shouldLoadPersistentChatHistory)(extendedChatConfig)) {
|
|
743
|
+
requestAnimationFrame(() => {
|
|
744
|
+
setTimeout(() => _ActivityStreamHandler.ActivityStreamHandler.uncork(), 500);
|
|
745
|
+
});
|
|
746
|
+
} else {
|
|
747
|
+
setTimeout(() => _ActivityStreamHandler.ActivityStreamHandler.uncork(), 500);
|
|
748
|
+
}
|
|
740
749
|
}
|
|
741
750
|
}, [state.appStates.isMinimized]);
|
|
742
751
|
|
|
@@ -893,6 +902,25 @@ const LiveChatWidgetStateful = props => {
|
|
|
893
902
|
}
|
|
894
903
|
});
|
|
895
904
|
}, []);
|
|
905
|
+
|
|
906
|
+
// Reliable browser close detection via visibilitychange + sendBeacon
|
|
907
|
+
// visibilitychange fires while the page is still alive (unlike beforeunload),
|
|
908
|
+
// so telemetry calls complete reliably. False positives (tab switch, minimize)
|
|
909
|
+
// are filtered in Kusto by checking if this is the last event in the session.
|
|
910
|
+
(0, _react2.useEffect)(() => {
|
|
911
|
+
const handleVisibilityChange = () => {
|
|
912
|
+
if (document.visibilityState === "hidden" && state.appStates.conversationState === _ConversationState.ConversationState.Active) {
|
|
913
|
+
_TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
|
|
914
|
+
Event: _TelemetryConstants.TelemetryEvent.BrowserTabHidden,
|
|
915
|
+
Description: "Browser tab hidden during active conversation"
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
};
|
|
919
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
920
|
+
return () => {
|
|
921
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
922
|
+
};
|
|
923
|
+
}, [state.appStates.conversationState]);
|
|
896
924
|
const initiateEndChatOnBrowserUnload = () => {
|
|
897
925
|
var _DataStoreManager$cli;
|
|
898
926
|
_TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
|
|
@@ -151,10 +151,18 @@ const WebChatContainerStateful = props => {
|
|
|
151
151
|
document.addEventListener("click", clickHandler);
|
|
152
152
|
return () => document.removeEventListener("click", clickHandler);
|
|
153
153
|
}, [state]);
|
|
154
|
+
const minimizedStyles = state.appStates.isMinimized ? shouldLoadPersistentHistoryMessages ? {
|
|
155
|
+
visibility: "hidden",
|
|
156
|
+
position: "absolute",
|
|
157
|
+
width: 0,
|
|
158
|
+
height: 0,
|
|
159
|
+
overflow: "hidden",
|
|
160
|
+
pointerEvents: "none"
|
|
161
|
+
} : {
|
|
162
|
+
display: "none"
|
|
163
|
+
} : {};
|
|
154
164
|
const containerStyles = {
|
|
155
|
-
root: Object.assign({}, _defaultWebChatContainerStatefulProps.defaultWebChatContainerStatefulProps.containerStyles, webChatContainerProps === null || webChatContainerProps === void 0 ? void 0 : webChatContainerProps.containerStyles,
|
|
156
|
-
display: state.appStates.isMinimized ? "none" : ""
|
|
157
|
-
}) // Use this instead of removing WebChat from the picture so that the activity observer inside the adapter is not invoked
|
|
165
|
+
root: Object.assign({}, _defaultWebChatContainerStatefulProps.defaultWebChatContainerStatefulProps.containerStyles, webChatContainerProps === null || webChatContainerProps === void 0 ? void 0 : webChatContainerProps.containerStyles, minimizedStyles) // Use visibility-based hiding instead of display:none to preserve scroll position across minimize/maximize
|
|
158
166
|
};
|
|
159
167
|
|
|
160
168
|
const localizedTexts = {
|
|
@@ -24,15 +24,24 @@ const convertStringValueToInt = value => {
|
|
|
24
24
|
|
|
25
25
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
26
|
const convertPersistentChatHistoryMessageToActivity = message => {
|
|
27
|
-
var _from$user, _from$
|
|
27
|
+
var _from$application, _from$user, _from$application2;
|
|
28
28
|
const {
|
|
29
29
|
additionalData,
|
|
30
30
|
attachments,
|
|
31
|
+
botContentType,
|
|
31
32
|
content,
|
|
32
33
|
created,
|
|
33
34
|
from,
|
|
34
35
|
transcriptOriginalMessageId
|
|
35
36
|
} = message;
|
|
37
|
+
const isFromCustomer = (from === null || from === void 0 ? void 0 : (_from$application = from.application) === null || _from$application === void 0 ? void 0 : _from$application.displayName) === "Customer";
|
|
38
|
+
|
|
39
|
+
// Filter out customer responses to adaptive cards (e.g., form submissions like {"value":{"goPaperless":"yes"}}).
|
|
40
|
+
// These are the customer's postBack/messageBack replies to bot adaptive cards — not displayable content.
|
|
41
|
+
// In live chat, webchat hides these natively; in history we must filter them explicitly.
|
|
42
|
+
if (isFromCustomer && botContentType === "azurebotservice.adaptivecard") {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
36
45
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
37
46
|
const activity = {
|
|
38
47
|
..._botActivity.default,
|
|
@@ -69,43 +78,107 @@ const convertPersistentChatHistoryMessageToActivity = message => {
|
|
|
69
78
|
name: from.user.displayName
|
|
70
79
|
};
|
|
71
80
|
}
|
|
72
|
-
if ((from === null || from === void 0 ? void 0 : (_from$
|
|
81
|
+
if ((from === null || from === void 0 ? void 0 : (_from$application2 = from.application) === null || _from$application2 === void 0 ? void 0 : _from$application2.displayName) === "Customer") {
|
|
73
82
|
activity.from = {
|
|
74
83
|
role: "user",
|
|
75
84
|
name: from.application.displayName
|
|
76
85
|
};
|
|
77
86
|
}
|
|
78
87
|
if (content) {
|
|
79
|
-
var
|
|
80
|
-
// Check if content contains adaptive card or rich card JSON using SupportedAdaptiveCards enum
|
|
81
|
-
const isAdaptiveCard = content.toLowerCase().includes(_Constants.Constants.AdaptiveCardType);
|
|
82
|
-
const isSuggestedActions = content.toLowerCase().includes(_Constants.Constants.SuggestedActionsType);
|
|
83
|
-
const containsSupportedCard = Object.values(_SupportedAdaptiveCards.SupportedAdaptiveCards).some(type => content.toLowerCase().includes(type.toLowerCase()));
|
|
88
|
+
var _parsedContent, _parsedContent$value;
|
|
84
89
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
85
90
|
let parsedContent;
|
|
86
91
|
try {
|
|
87
92
|
parsedContent = JSON.parse(content);
|
|
88
93
|
} catch (error) {
|
|
89
|
-
console.error("Failed to parse content as JSON:", error);
|
|
90
94
|
parsedContent = null; // fall back to normal text handling
|
|
91
95
|
}
|
|
92
96
|
|
|
93
97
|
// Check if this is a customer form submission response (e.g., RichObjectMessage_Form)
|
|
94
98
|
// These should be ignored as they are form submission data, not displayable content
|
|
95
|
-
const isFromCustomer = (from === null || from === void 0 ? void 0 : (_from$application2 = from.application) === null || _from$application2 === void 0 ? void 0 : _from$application2.displayName) === "Customer";
|
|
96
99
|
if (isFromCustomer && ((_parsedContent = parsedContent) === null || _parsedContent === void 0 ? void 0 : (_parsedContent$value = _parsedContent.value) === null || _parsedContent$value === void 0 ? void 0 : _parsedContent$value.type) === "RichObjectMessage_Form") {
|
|
97
100
|
return null;
|
|
98
101
|
}
|
|
99
|
-
if (parsedContent &&
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
102
|
+
if (parsedContent && typeof parsedContent === "object") {
|
|
103
|
+
var _parsedContent$sugges;
|
|
104
|
+
// Structural detection: check the parsed object's properties directly
|
|
105
|
+
const hasAttachments = Array.isArray(parsedContent.attachments) && parsedContent.attachments.length > 0;
|
|
106
|
+
const hasSuggestedActions = Array.isArray((_parsedContent$sugges = parsedContent.suggestedActions) === null || _parsedContent$sugges === void 0 ? void 0 : _parsedContent$sugges.actions) && parsedContent.suggestedActions.actions.length > 0;
|
|
107
|
+
const isRawAdaptiveCardBody = parsedContent.type === "AdaptiveCard";
|
|
108
|
+
|
|
109
|
+
// Filter out suggested-action-only messages from history.
|
|
110
|
+
// WebChat only renders suggestedActions for the most recent activity, so in history
|
|
111
|
+
// these render as empty "Suggested reply" text bubbles with no actionable buttons.
|
|
112
|
+
if (hasSuggestedActions && !hasAttachments) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Substring detection: check the raw content string for known card type patterns
|
|
117
|
+
const contentLower = content.toLowerCase();
|
|
118
|
+
const isAdaptiveCard = contentLower.includes(_Constants.Constants.AdaptiveCardType);
|
|
119
|
+
const isSuggestedActions = contentLower.includes(_Constants.Constants.SuggestedActionsType);
|
|
120
|
+
const containsSupportedCard = Object.values(_SupportedAdaptiveCards.SupportedAdaptiveCards).some(type => contentLower.includes(type.toLowerCase()));
|
|
121
|
+
|
|
122
|
+
// If the content is a raw adaptive card body (type: "AdaptiveCard"), wrap it as an attachment
|
|
123
|
+
// so webchat can render it properly instead of treating it as an unknown activity type
|
|
124
|
+
if (isRawAdaptiveCardBody) {
|
|
125
|
+
return {
|
|
126
|
+
...activity,
|
|
127
|
+
text: "",
|
|
128
|
+
attachments: [{
|
|
129
|
+
contentType: _SupportedAdaptiveCards.SupportedAdaptiveCards.Adaptive,
|
|
130
|
+
content: parsedContent
|
|
131
|
+
}],
|
|
132
|
+
timestamp,
|
|
133
|
+
channelData: {
|
|
134
|
+
...activity.channelData,
|
|
135
|
+
"webchat:sequence-id": webchatSequenceId
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Detect rich content using both structural checks and substring matching
|
|
141
|
+
if (hasAttachments || hasSuggestedActions || isAdaptiveCard || isSuggestedActions || containsSupportedCard) {
|
|
142
|
+
var _activity$from;
|
|
143
|
+
// Preserve from.role from the base activity — parsedContent.from may lack the role property
|
|
144
|
+
// which webchat needs to determine how to render the message (bot vs user)
|
|
145
|
+
const preservedFrom = {
|
|
146
|
+
...activity.from,
|
|
147
|
+
...(parsedContent.from || {}),
|
|
148
|
+
role: ((_activity$from = activity.from) === null || _activity$from === void 0 ? void 0 : _activity$from.role) || "bot"
|
|
149
|
+
};
|
|
150
|
+
return {
|
|
151
|
+
...activity,
|
|
152
|
+
...parsedContent,
|
|
153
|
+
from: preservedFrom,
|
|
154
|
+
timestamp,
|
|
155
|
+
channelData: {
|
|
156
|
+
...activity.channelData,
|
|
157
|
+
"webchat:sequence-id": webchatSequenceId
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// If parsedContent is a webchat activity (type: "message") but didn't match any specific card check,
|
|
163
|
+
// still treat it as a rich activity to avoid displaying raw JSON as text
|
|
164
|
+
if (parsedContent.type === "message" && (parsedContent.attachments || parsedContent.suggestedActions || parsedContent.value)) {
|
|
165
|
+
var _activity$from2;
|
|
166
|
+
const preservedFrom = {
|
|
167
|
+
...activity.from,
|
|
168
|
+
...(parsedContent.from || {}),
|
|
169
|
+
role: ((_activity$from2 = activity.from) === null || _activity$from2 === void 0 ? void 0 : _activity$from2.role) || "bot"
|
|
170
|
+
};
|
|
171
|
+
return {
|
|
172
|
+
...activity,
|
|
173
|
+
...parsedContent,
|
|
174
|
+
from: preservedFrom,
|
|
175
|
+
timestamp,
|
|
176
|
+
channelData: {
|
|
177
|
+
...activity.channelData,
|
|
178
|
+
"webchat:sequence-id": webchatSequenceId
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
}
|
|
109
182
|
}
|
|
110
183
|
|
|
111
184
|
// Plain text message
|
package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/WebChatEventSubscribers.js
CHANGED
|
@@ -76,15 +76,26 @@ const WebChatEventSubscribers = () => {
|
|
|
76
76
|
// Dispatch events when connection is established
|
|
77
77
|
setTimeout(() => {
|
|
78
78
|
(0, _dispatchCustomEvent.default)(_ChatWidgetEvents.default.FETCH_PERSISTENT_CHAT_HISTORY);
|
|
79
|
+
// The trigger activity renders the "Loading previous messages..." banner
|
|
80
|
+
// via LazyLoadActivity. We set webchat:sequence-id to 1 and timestamp
|
|
81
|
+
// to epoch+1ms so WebChat sorts this activity BEFORE all history messages
|
|
82
|
+
// (which have sequence-ids based on transcriptOriginalMessageId timestamps).
|
|
83
|
+
// Without these, WebChat places activities without a sequence-id at the
|
|
84
|
+
// end of the transcript, causing the banner to appear at the bottom and
|
|
85
|
+
// breaking the IntersectionObserver pagination trigger (the observer only
|
|
86
|
+
// fires on visibility transitions — if the element starts visible at the
|
|
87
|
+
// bottom, the initial fire is blocked by the paused state and never
|
|
88
|
+
// re-fires since there is no transition).
|
|
79
89
|
(0, _dispatchCustomEvent.default)(_ChatWidgetEvents.default.ADD_ACTIVITY, {
|
|
80
90
|
activity: {
|
|
81
91
|
from: {
|
|
82
92
|
role: "bot"
|
|
83
93
|
},
|
|
84
|
-
timestamp:
|
|
94
|
+
timestamp: new Date(1).toISOString(),
|
|
85
95
|
type: "message",
|
|
86
96
|
channelData: {
|
|
87
|
-
tags: [_Constants.Constants.persistentChatHistoryMessagePullTriggerTag]
|
|
97
|
+
tags: [_Constants.Constants.persistentChatHistoryMessagePullTriggerTag],
|
|
98
|
+
"webchat:sequence-id": 1
|
|
88
99
|
}
|
|
89
100
|
}
|
|
90
101
|
});
|