@microsoft/omnichannel-chat-widget 1.8.4-main.7bdb634 → 1.8.4-main.c687f88
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 +27 -0
- package/lib/cjs/common/Constants.js +3 -0
- package/lib/cjs/common/telemetry/AppInsightsEvents.js +14 -9
- package/lib/cjs/common/telemetry/TelemetryConstants.js +15 -2
- package/lib/cjs/common/telemetry/TelemetryManager.js +10 -7
- package/lib/cjs/common/telemetry/loggers/appInsightsLogger.js +29 -13
- package/lib/cjs/common/utils.js +14 -1
- package/lib/cjs/components/chatbuttonstateful/ChatButtonStateful.js +16 -4
- package/lib/cjs/components/citationpanestateful/CitationPaneStateful.js +20 -1
- package/lib/cjs/components/errorboundary/ErrorBoundary.js +2 -1
- package/lib/cjs/components/headerstateful/HeaderStateful.js +8 -2
- package/lib/cjs/components/livechatwidget/common/ChatWidgetEvents.js +1 -1
- package/lib/cjs/components/livechatwidget/common/PersistentConversationHandler.js +26 -20
- package/lib/cjs/components/livechatwidget/common/defaultProps/defaultPersistentChatHistoryProps.js +1 -2
- package/lib/cjs/components/livechatwidget/common/endChat.js +26 -9
- package/lib/cjs/components/livechatwidget/common/initWebChatComposer.js +9 -2
- package/lib/cjs/components/livechatwidget/common/liveChatConfigUtils.js +36 -4
- package/lib/cjs/components/livechatwidget/common/registerTelemetryLoggers.js +3 -0
- package/lib/cjs/components/livechatwidget/common/renderSurveyHelpers.js +2 -2
- package/lib/cjs/components/livechatwidget/common/setPostChatContextAndLoadSurvey.js +3 -3
- package/lib/cjs/components/livechatwidget/common/startChat.js +5 -1
- package/lib/cjs/components/livechatwidget/common/startChatErrorHandler.js +24 -4
- package/lib/cjs/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +124 -28
- package/lib/cjs/components/postchatsurveypanestateful/PostChatSurveyPaneStateful.js +37 -8
- package/lib/cjs/components/prechatsurveypanestateful/PreChatSurveyPaneStateful.js +12 -3
- package/lib/cjs/components/webchatcontainerstateful/WebChatContainerStateful.js +28 -30
- package/lib/cjs/components/webchatcontainerstateful/common/activityConverters/convertPersistentChatHistoryMessageToActivity.js +12 -12
- package/lib/cjs/components/webchatcontainerstateful/common/defaultProps/defaultMiddlewareLocalizedTexts.js +2 -1
- package/lib/cjs/components/webchatcontainerstateful/hooks/usePersistentChatHistory.js +1 -3
- package/lib/cjs/components/webchatcontainerstateful/interfaces/IExtendedChatConffig.js +1 -0
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/WebChatEventSubscribers.js +6 -7
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/ConversationDividerActivity.js +30 -1
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/LazyLoadActivity.js +21 -1
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.js +7 -2
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/NotDeliveredTimestamp.js +3 -0
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/citationsMiddleware.js +6 -1
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/localizedStringsBotInitialsMiddleware.js +29 -7
- package/lib/cjs/contexts/common/LiveChatWidgetActionType.js +1 -0
- package/lib/cjs/contexts/common/LiveChatWidgetContextInitialState.js +7 -1
- package/lib/cjs/contexts/createReducer.js +15 -0
- package/lib/cjs/firstresponselatency/FirstMessageTrackerFromBot.js +3 -2
- package/lib/cjs/firstresponselatency/FirstResponseLatencyTracker.js +6 -2
- package/lib/cjs/plugins/newMessageEventHandler.js +4 -1
- package/lib/esm/common/Constants.js +3 -0
- package/lib/esm/common/telemetry/AppInsightsEvents.js +14 -9
- package/lib/esm/common/telemetry/TelemetryConstants.js +13 -1
- package/lib/esm/common/telemetry/TelemetryManager.js +10 -7
- package/lib/esm/common/telemetry/loggers/appInsightsLogger.js +30 -14
- package/lib/esm/common/utils.js +11 -0
- package/lib/esm/components/chatbuttonstateful/ChatButtonStateful.js +17 -5
- package/lib/esm/components/citationpanestateful/CitationPaneStateful.js +20 -1
- package/lib/esm/components/errorboundary/ErrorBoundary.js +4 -2
- package/lib/esm/components/headerstateful/HeaderStateful.js +9 -3
- package/lib/esm/components/livechatwidget/common/ChatWidgetEvents.js +1 -1
- package/lib/esm/components/livechatwidget/common/PersistentConversationHandler.js +26 -20
- package/lib/esm/components/livechatwidget/common/defaultProps/defaultPersistentChatHistoryProps.js +1 -2
- package/lib/esm/components/livechatwidget/common/endChat.js +26 -9
- package/lib/esm/components/livechatwidget/common/initWebChatComposer.js +9 -2
- package/lib/esm/components/livechatwidget/common/liveChatConfigUtils.js +33 -2
- package/lib/esm/components/livechatwidget/common/registerTelemetryLoggers.js +3 -0
- package/lib/esm/components/livechatwidget/common/renderSurveyHelpers.js +2 -2
- package/lib/esm/components/livechatwidget/common/setPostChatContextAndLoadSurvey.js +3 -3
- package/lib/esm/components/livechatwidget/common/startChat.js +7 -3
- package/lib/esm/components/livechatwidget/common/startChatErrorHandler.js +23 -4
- package/lib/esm/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +125 -29
- package/lib/esm/components/postchatsurveypanestateful/PostChatSurveyPaneStateful.js +39 -10
- package/lib/esm/components/prechatsurveypanestateful/PreChatSurveyPaneStateful.js +13 -4
- package/lib/esm/components/webchatcontainerstateful/WebChatContainerStateful.js +29 -34
- package/lib/esm/components/webchatcontainerstateful/common/activityConverters/convertPersistentChatHistoryMessageToActivity.js +12 -12
- package/lib/esm/components/webchatcontainerstateful/common/defaultProps/defaultMiddlewareLocalizedTexts.js +2 -1
- package/lib/esm/components/webchatcontainerstateful/hooks/usePersistentChatHistory.js +1 -3
- package/lib/esm/components/webchatcontainerstateful/interfaces/IExtendedChatConffig.js +1 -0
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/WebChatEventSubscribers.js +6 -7
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/ConversationDividerActivity.js +30 -1
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/LazyLoadActivity.js +21 -1
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.js +7 -2
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/NotDeliveredTimestamp.js +3 -0
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/channelDataMiddleware.js +1 -0
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/citationsMiddleware.js +6 -1
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/localizedStringsBotInitialsMiddleware.js +29 -7
- package/lib/esm/contexts/common/LiveChatWidgetActionType.js +1 -0
- package/lib/esm/contexts/common/LiveChatWidgetContextInitialState.js +7 -1
- package/lib/esm/contexts/createReducer.js +15 -0
- package/lib/esm/firstresponselatency/FirstMessageTrackerFromBot.js +3 -2
- package/lib/esm/firstresponselatency/FirstResponseLatencyTracker.js +6 -2
- package/lib/esm/plugins/newMessageEventHandler.js +4 -1
- package/lib/types/common/Constants.d.ts +3 -0
- package/lib/types/common/telemetry/TelemetryConstants.d.ts +12 -1
- package/lib/types/common/telemetry/interfaces/IInternalTelemetryData.d.ts +1 -0
- package/lib/types/common/utils.d.ts +9 -1
- package/lib/types/components/errorboundary/ErrorBoundary.d.ts +1 -1
- package/lib/types/components/livechatwidget/common/ChatWidgetEvents.d.ts +1 -1
- package/lib/types/components/livechatwidget/common/liveChatConfigUtils.d.ts +11 -0
- package/lib/types/components/livechatwidget/common/startChatErrorHandler.d.ts +1 -0
- package/lib/types/components/livechatwidget/interfaces/IPersistentChatHistoryProps.d.ts +5 -1
- package/lib/types/components/webchatcontainerstateful/interfaces/IExtendedChatConffig.d.ts +15 -0
- package/lib/types/components/webchatcontainerstateful/webchatcontroller/WebChatEventSubscribers.d.ts +1 -2
- package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/LazyLoadActivity.d.ts +1 -1
- package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.d.ts +2 -1
- package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/localizedStringsBotInitialsMiddleware.d.ts +1 -1
- package/lib/types/contexts/common/ILiveChatWidgetContext.d.ts +1 -0
- package/lib/types/contexts/common/ILiveChatWidgetLocalizedTexts.d.ts +5 -0
- package/lib/types/contexts/common/LiveChatWidgetActionType.d.ts +2 -1
- package/package.json +3 -3
|
@@ -30,7 +30,7 @@ let PersistentConversationHandler = /*#__PURE__*/function () {
|
|
|
30
30
|
_defineProperty(this, "facadeChatSDK", void 0);
|
|
31
31
|
_defineProperty(this, "lastMessage", null);
|
|
32
32
|
_defineProperty(this, "count", 0);
|
|
33
|
-
_defineProperty(this, "pageSize",
|
|
33
|
+
_defineProperty(this, "pageSize", defaultPersistentChatHistoryProps.pageSize);
|
|
34
34
|
_defineProperty(this, "isCurrentlyPulling", false);
|
|
35
35
|
_defineProperty(this, "pageTokenInTransitSet", new Set());
|
|
36
36
|
_defineProperty(this, "resetEventListener", BroadcastService.getMessageByEventName(BroadcastEvent.PersistentConversationReset).subscribe(() => {
|
|
@@ -42,18 +42,21 @@ let PersistentConversationHandler = /*#__PURE__*/function () {
|
|
|
42
42
|
Event: TelemetryEvent.LCWPersistentConversationHandlerInitialized,
|
|
43
43
|
Description: "PersistentConversationHandler initialized",
|
|
44
44
|
CustomProperties: {
|
|
45
|
-
pageSize:
|
|
45
|
+
pageSize: defaultPersistentChatHistoryProps.pageSize
|
|
46
46
|
}
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
49
|
_createClass(PersistentConversationHandler, [{
|
|
50
50
|
key: "appliedPropsHandler",
|
|
51
51
|
value: function appliedPropsHandler(props) {
|
|
52
|
+
var _this$appliedProps;
|
|
52
53
|
this.appliedProps = {
|
|
53
54
|
...defaultPersistentChatHistoryProps,
|
|
54
55
|
...props
|
|
55
56
|
};
|
|
56
|
-
|
|
57
|
+
|
|
58
|
+
// if the props is not existent or is not a number then default to defaultPersistentChatHistoryProps.pageSize
|
|
59
|
+
this.pageSize = ((_this$appliedProps = this.appliedProps) === null || _this$appliedProps === void 0 ? void 0 : _this$appliedProps.pageSize) !== undefined && !isNaN(this.appliedProps.pageSize) ? this.appliedProps.pageSize : defaultPersistentChatHistoryProps.pageSize;
|
|
57
60
|
}
|
|
58
61
|
}, {
|
|
59
62
|
key: "reset",
|
|
@@ -98,12 +101,23 @@ let PersistentConversationHandler = /*#__PURE__*/function () {
|
|
|
98
101
|
try {
|
|
99
102
|
var _ref;
|
|
100
103
|
const messages = await this.fetchHistoryMessages();
|
|
101
|
-
|
|
104
|
+
|
|
105
|
+
// Handle error case - null indicates an error occurred
|
|
106
|
+
// Don't mark as last pull to allow retry on next attempt
|
|
107
|
+
if (messages == null) {
|
|
108
|
+
TelemetryHelper.logActionEvent(LogLevel.WARN, {
|
|
109
|
+
Event: TelemetryEvent.LCWPersistentHistoryReturnedNull,
|
|
110
|
+
Description: "History pull returned null - Possible error occurred, will retry on next scroll",
|
|
111
|
+
ElapsedTimeInMilliseconds: pullTimer.milliSecondsElapsed
|
|
112
|
+
});
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Handle legitimate end of history - empty array
|
|
117
|
+
if (messages.length === 0) {
|
|
102
118
|
this.isLastPull = true;
|
|
103
119
|
// Dispatch event to notify UI that no more history is available
|
|
104
120
|
dispatchCustomEvent(ChatWidgetEvents.NO_MORE_HISTORY_AVAILABLE);
|
|
105
|
-
// Also hide the loading banner
|
|
106
|
-
dispatchCustomEvent(ChatWidgetEvents.HIDE_LOADING_BANNER);
|
|
107
121
|
TelemetryHelper.logActionEvent(LogLevel.INFO, {
|
|
108
122
|
Event: TelemetryEvent.LCWPersistentHistoryPullCompleted,
|
|
109
123
|
Description: "History pull completed - no more messages",
|
|
@@ -113,9 +127,6 @@ let PersistentConversationHandler = /*#__PURE__*/function () {
|
|
|
113
127
|
}
|
|
114
128
|
const messagesDescOrder = (_ref = [...messages]) === null || _ref === void 0 ? void 0 : _ref.reverse();
|
|
115
129
|
this.processHistoryMessages(messagesDescOrder);
|
|
116
|
-
|
|
117
|
-
// Dispatch event to hide the loading banner after messages are processed
|
|
118
|
-
dispatchCustomEvent(ChatWidgetEvents.HIDE_LOADING_BANNER);
|
|
119
130
|
TelemetryHelper.logActionEvent(LogLevel.INFO, {
|
|
120
131
|
Event: TelemetryEvent.LCWPersistentHistoryPullCompleted,
|
|
121
132
|
Description: "History pull completed successfully",
|
|
@@ -171,7 +182,6 @@ let PersistentConversationHandler = /*#__PURE__*/function () {
|
|
|
171
182
|
if (!this.shouldPull()) {
|
|
172
183
|
// Dispatch event to ensure banner is hidden when no more pulls are needed
|
|
173
184
|
dispatchCustomEvent(ChatWidgetEvents.NO_MORE_HISTORY_AVAILABLE);
|
|
174
|
-
dispatchCustomEvent(ChatWidgetEvents.HIDE_LOADING_BANNER);
|
|
175
185
|
return [];
|
|
176
186
|
}
|
|
177
187
|
const options = {
|
|
@@ -197,24 +207,20 @@ let PersistentConversationHandler = /*#__PURE__*/function () {
|
|
|
197
207
|
this.isLastPull = true;
|
|
198
208
|
// Dispatch event when we reach the end of available history
|
|
199
209
|
dispatchCustomEvent(ChatWidgetEvents.NO_MORE_HISTORY_AVAILABLE);
|
|
200
|
-
// Also hide the loading banner
|
|
201
|
-
dispatchCustomEvent(ChatWidgetEvents.HIDE_LOADING_BANNER);
|
|
202
210
|
return [];
|
|
203
211
|
}
|
|
204
|
-
dispatchCustomEvent(ChatWidgetEvents.HIDE_LOADING_BANNER);
|
|
205
212
|
return messages;
|
|
206
213
|
} catch (error) {
|
|
207
214
|
TelemetryHelper.logSDKEvent(LogLevel.ERROR, {
|
|
208
215
|
Event: TelemetryEvent.FetchPersistentChatHistoryFailed,
|
|
209
216
|
ExceptionDetails: error
|
|
210
217
|
});
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
//
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
return [];
|
|
218
|
+
|
|
219
|
+
// On error, dispatch HISTORY_LOAD_ERROR to hide loading banner without marking conversation as ended
|
|
220
|
+
// This allows recovery on the next attempt (e.g., transient network errors)
|
|
221
|
+
// Return null to distinguish error from legitimate empty history
|
|
222
|
+
dispatchCustomEvent(ChatWidgetEvents.HISTORY_LOAD_ERROR);
|
|
223
|
+
return null;
|
|
218
224
|
}
|
|
219
225
|
}
|
|
220
226
|
}, {
|
|
@@ -6,6 +6,8 @@ import { BroadcastService } from "@microsoft/omnichannel-chat-components";
|
|
|
6
6
|
import { ConversationState } from "../../../contexts/common/ConversationState";
|
|
7
7
|
import { LazyLoadHandler } from "../../webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/LazyLoadActivity";
|
|
8
8
|
import { LiveChatWidgetActionType } from "../../../contexts/common/LiveChatWidgetActionType";
|
|
9
|
+
import { NotificationHandler } from "../../webchatcontainerstateful/webchatcontroller/notification/NotificationHandler";
|
|
10
|
+
import { NotificationScenarios } from "../../webchatcontainerstateful/webchatcontroller/enums/NotificationScenarios";
|
|
9
11
|
import { TelemetryHelper } from "../../../common/telemetry/TelemetryHelper";
|
|
10
12
|
import { TelemetryManager } from "../../../common/telemetry/TelemetryManager";
|
|
11
13
|
import { WebChatStoreLoader } from "../../webchatcontainerstateful/webchatcontroller/WebChatStoreLoader";
|
|
@@ -195,6 +197,18 @@ const endChat = async (props, facadeChatSDK, state, dispatch, setAdapter, setWeb
|
|
|
195
197
|
facadeChatSDK.destroy();
|
|
196
198
|
}
|
|
197
199
|
}
|
|
200
|
+
|
|
201
|
+
//moving logic below to before processing skipCloseChat logic to avoid race conditions of postMessage for endChatEvent for other tabs vs postMessage for CloseChat
|
|
202
|
+
//TODO: clarify if this postMessageToOtherTab actually works in production.
|
|
203
|
+
if (postMessageToOtherTab) {
|
|
204
|
+
const endChatEventName = await getEndChatEventName(facadeChatSDK, props);
|
|
205
|
+
BroadcastService.postMessage({
|
|
206
|
+
eventName: endChatEventName,
|
|
207
|
+
payload: {
|
|
208
|
+
runtimeId: TelemetryManager.InternalTelemetryData.lcwRuntimeId
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
198
212
|
if (!skipCloseChat) {
|
|
199
213
|
try {
|
|
200
214
|
var _props$webChatContain;
|
|
@@ -234,17 +248,17 @@ const endChat = async (props, facadeChatSDK, state, dispatch, setAdapter, setWeb
|
|
|
234
248
|
});
|
|
235
249
|
closeChatWidget(dispatch, setWebChatStyles, props);
|
|
236
250
|
facadeChatSDK.destroy();
|
|
251
|
+
|
|
252
|
+
//always post the close chat event after chat closed and cleanup completed
|
|
253
|
+
BroadcastService.postMessage({
|
|
254
|
+
eventName: BroadcastEvent.CloseChat
|
|
255
|
+
});
|
|
256
|
+
TelemetryHelper.logActionEvent(LogLevel.INFO, {
|
|
257
|
+
Event: TelemetryEvent.CloseChatCall,
|
|
258
|
+
Description: "Broadcasted close chat event"
|
|
259
|
+
});
|
|
237
260
|
}
|
|
238
261
|
}
|
|
239
|
-
if (postMessageToOtherTab) {
|
|
240
|
-
const endChatEventName = await getEndChatEventName(facadeChatSDK, props);
|
|
241
|
-
BroadcastService.postMessage({
|
|
242
|
-
eventName: endChatEventName,
|
|
243
|
-
payload: {
|
|
244
|
-
runtimeId: TelemetryManager.InternalTelemetryData.lcwRuntimeId
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
262
|
};
|
|
249
263
|
export const callingStateCleanUp = dispatch => {
|
|
250
264
|
dispatch({
|
|
@@ -322,6 +336,9 @@ export const closeChatStateCleanUp = dispatch => {
|
|
|
322
336
|
payload: {}
|
|
323
337
|
});
|
|
324
338
|
|
|
339
|
+
// Dismiss the chat disconnect notification banner if it was shown
|
|
340
|
+
NotificationHandler.dismissNotification(NotificationScenarios.ChatDisconnect);
|
|
341
|
+
|
|
325
342
|
// Clear live chat context only if chat widget is fully closed to support transcript calls after sessionclose is called
|
|
326
343
|
dispatch({
|
|
327
344
|
type: LiveChatWidgetActionType.SET_LIVE_CHAT_CONTEXT,
|
|
@@ -120,7 +120,14 @@ export const initWebChatComposer = (props, state, dispatch, facadeChatSDK, endCh
|
|
|
120
120
|
};
|
|
121
121
|
webChatStore = createStore({},
|
|
122
122
|
//initial state
|
|
123
|
-
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, createCitationsMiddleware(state, dispatch), gifUploadMiddleware, htmlPlayerMiddleware, htmlTextMiddleware(honorsTargetInHTMLLinks), createMaxMessageSizeValidator(localizedTexts), sanitizationMiddleware, createCallActionMiddleware(),
|
|
123
|
+
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, createCitationsMiddleware(state, dispatch), gifUploadMiddleware, htmlPlayerMiddleware, htmlTextMiddleware(honorsTargetInHTMLLinks), createMaxMessageSizeValidator(localizedTexts), sanitizationMiddleware, createCallActionMiddleware(),
|
|
124
|
+
// Pass a callback so middleware can push initials into React context for reactivity
|
|
125
|
+
localizedStringsBotInitialsMiddleware(initials => {
|
|
126
|
+
dispatch({
|
|
127
|
+
type: LiveChatWidgetActionType.SET_BOT_AVATAR_INITIALS,
|
|
128
|
+
payload: initials
|
|
129
|
+
});
|
|
130
|
+
}),
|
|
124
131
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
125
132
|
...(((_props$webChatContain7 = props.webChatContainerProps) === null || _props$webChatContain7 === void 0 ? void 0 : _props$webChatContain7.storeMiddlewares) ?? []));
|
|
126
133
|
WebChatStoreLoader.store = webChatStore;
|
|
@@ -163,7 +170,7 @@ export const initWebChatComposer = (props, state, dispatch, facadeChatSDK, endCh
|
|
|
163
170
|
dir: state.domainStates.globalDir,
|
|
164
171
|
locale: changeLanguageCodeFormatForWebChat(getLocaleStringFromId((_state$domainStates$l4 = state.domainStates.liveChatConfig) === null || _state$domainStates$l4 === void 0 ? void 0 : (_state$domainStates$l5 = _state$domainStates$l4.ChatWidgetLanguage) === null || _state$domainStates$l5 === void 0 ? void 0 : _state$domainStates$l5.msdyn_localeid)),
|
|
165
172
|
store: webChatStore,
|
|
166
|
-
activityMiddleware: (_props$webChatContain11 = props.webChatContainerProps) !== null && _props$webChatContain11 !== void 0 && (_props$webChatContain12 = _props$webChatContain11.renderingMiddlewareProps) !== null && _props$webChatContain12 !== void 0 && _props$webChatContain12.disableActivityMiddleware ? undefined : createActivityMiddleware(renderMarkdown, (_state$domainStates$r = state.domainStates.renderingMiddlewareProps) === null || _state$domainStates$r === void 0 ? void 0 : _state$domainStates$r.systemMessageStyleProps, (_state$domainStates$r2 = state.domainStates.renderingMiddlewareProps) === null || _state$domainStates$r2 === void 0 ? void 0 : _state$domainStates$r2.userMessageStyleProps),
|
|
173
|
+
activityMiddleware: (_props$webChatContain11 = props.webChatContainerProps) !== null && _props$webChatContain11 !== void 0 && (_props$webChatContain12 = _props$webChatContain11.renderingMiddlewareProps) !== null && _props$webChatContain12 !== void 0 && _props$webChatContain12.disableActivityMiddleware ? undefined : createActivityMiddleware(renderMarkdown, (_state$domainStates$r = state.domainStates.renderingMiddlewareProps) === null || _state$domainStates$r === void 0 ? void 0 : _state$domainStates$r.systemMessageStyleProps, (_state$domainStates$r2 = state.domainStates.renderingMiddlewareProps) === null || _state$domainStates$r2 === void 0 ? void 0 : _state$domainStates$r2.userMessageStyleProps, localizedTexts),
|
|
167
174
|
attachmentMiddleware: (_props$webChatContain13 = props.webChatContainerProps) !== null && _props$webChatContain13 !== void 0 && (_props$webChatContain14 = _props$webChatContain13.renderingMiddlewareProps) !== null && _props$webChatContain14 !== void 0 && _props$webChatContain14.disableAttachmentMiddleware ? undefined : createAttachmentMiddleware(((_state$domainStates$r3 = state.domainStates.renderingMiddlewareProps) === null || _state$domainStates$r3 === void 0 ? void 0 : (_state$domainStates$r4 = _state$domainStates$r3.attachmentProps) === null || _state$domainStates$r4 === void 0 ? void 0 : _state$domainStates$r4.enableInlinePlaying) ?? defaultAttachmentProps.enableInlinePlaying),
|
|
168
175
|
activityStatusMiddleware: (_props$webChatContain15 = props.webChatContainerProps) !== null && _props$webChatContain15 !== void 0 && (_props$webChatContain16 = _props$webChatContain15.renderingMiddlewareProps) !== null && _props$webChatContain16 !== void 0 && _props$webChatContain16.disableActivityStatusMiddleware ? undefined : createActivityStatusMiddleware(getLocaleStringFromId((_state$domainStates$l6 = state.domainStates.liveChatConfig) === null || _state$domainStates$l6 === void 0 ? void 0 : (_state$domainStates$l7 = _state$domainStates$l6.ChatWidgetLanguage) === null || _state$domainStates$l7 === void 0 ? void 0 : _state$domainStates$l7.msdyn_localeid)),
|
|
169
176
|
toastMiddleware: (_props$webChatContain17 = props.webChatContainerProps) !== null && _props$webChatContain17 !== void 0 && (_props$webChatContain18 = _props$webChatContain17.renderingMiddlewareProps) !== null && _props$webChatContain18 !== void 0 && _props$webChatContain18.disableToastMiddleware ? undefined : createToastMiddleware(props.notificationPaneProps, endChat),
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import { isNullOrUndefined, parseBooleanFromConfig } from "../../../common/utils";
|
|
1
2
|
import { ConversationMode } from "../../../common/Constants";
|
|
2
|
-
import { isNullOrUndefined } from "../../../common/utils";
|
|
3
3
|
export const isPostChatSurveyEnabled = async facadeChatSDK => {
|
|
4
4
|
var _chatConfig$LiveWSAnd;
|
|
5
5
|
const chatConfig = await facadeChatSDK.getLiveChatConfig();
|
|
6
6
|
const postChatEnabled = (_chatConfig$LiveWSAnd = chatConfig.LiveWSAndLiveChatEngJoin) === null || _chatConfig$LiveWSAnd === void 0 ? void 0 : _chatConfig$LiveWSAnd.msdyn_postconversationsurveyenable.toString().toLowerCase();
|
|
7
7
|
return postChatEnabled === "true";
|
|
8
8
|
};
|
|
9
|
+
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
11
|
export const getPostChatSurveyConfig = async facadeChatSDK => {
|
|
10
12
|
var _chatConfig$LiveWSAnd2, _chatConfig$LiveWSAnd3, _chatConfig$LiveWSAnd4, _chatConfig$LiveWSAnd5, _chatConfig$LiveWSAnd6, _chatConfig$LiveWSAnd7, _chatConfig$LiveWSAnd8, _chatConfig$LiveWSAnd9, _chatConfig$LiveWSAnd10;
|
|
11
13
|
const chatConfig = await facadeChatSDK.getLiveChatConfig();
|
|
@@ -26,5 +28,34 @@ export const isPersistentChatEnabled = conversationMode => {
|
|
|
26
28
|
if (isNullOrUndefined(conversationMode)) {
|
|
27
29
|
return false;
|
|
28
30
|
}
|
|
29
|
-
return (conversationMode === null || conversationMode === void 0 ? void 0 : conversationMode.toString()
|
|
31
|
+
return (conversationMode === null || conversationMode === void 0 ? void 0 : conversationMode.toString()) === ConversationMode.Persistent;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Determines if persistent chat history should be loaded based on all required conditions.
|
|
36
|
+
*
|
|
37
|
+
* @param extendedChatConfig - The extended chat configuration object
|
|
38
|
+
* @returns true if ALL conditions are met:
|
|
39
|
+
* 1. Conversation mode must be Persistent ("192350001")
|
|
40
|
+
* 2. History is enabled in admin config (msdyn_enablepersistentchatpreviousconversations)
|
|
41
|
+
* 3. History is enabled via feature flag (lcwPersistentChatHistoryEnabled)
|
|
42
|
+
*/
|
|
43
|
+
export const shouldLoadPersistentChatHistory = extendedChatConfig => {
|
|
44
|
+
var _extendedChatConfig$L, _extendedChatConfig$L2, _extendedChatConfig$L3;
|
|
45
|
+
// CRITICAL: First check if conversation mode is persistent
|
|
46
|
+
// Only persistent mode ("192350001") should allow history loading
|
|
47
|
+
const isPersistentChatEnabledForWidget = isPersistentChatEnabled(extendedChatConfig === null || extendedChatConfig === void 0 ? void 0 : (_extendedChatConfig$L = extendedChatConfig.LiveWSAndLiveChatEngJoin) === null || _extendedChatConfig$L === void 0 ? void 0 : _extendedChatConfig$L.msdyn_conversationmode);
|
|
48
|
+
if (!isPersistentChatEnabledForWidget) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Check if history is enabled in admin config (handles both boolean and string "true"/"false")
|
|
53
|
+
const isHistoryEnabledInConfig = parseBooleanFromConfig(extendedChatConfig === null || extendedChatConfig === void 0 ? void 0 : (_extendedChatConfig$L2 = extendedChatConfig.LiveWSAndLiveChatEngJoin) === null || _extendedChatConfig$L2 === void 0 ? void 0 : _extendedChatConfig$L2.msdyn_enablepersistentchatpreviousconversations);
|
|
54
|
+
if (!isHistoryEnabledInConfig) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Check if history is enabled via feature flag (handles both boolean and string "true"/"false")
|
|
59
|
+
const isHistoryEnabledViaFCB = parseBooleanFromConfig(extendedChatConfig === null || extendedChatConfig === void 0 ? void 0 : (_extendedChatConfig$L3 = extendedChatConfig.LcwFcbConfiguration) === null || _extendedChatConfig$L3 === void 0 ? void 0 : _extendedChatConfig$L3.lcwPersistentChatHistoryEnabled);
|
|
60
|
+
return isHistoryEnabledViaFCB;
|
|
30
61
|
};
|
|
@@ -24,7 +24,10 @@ export const registerTelemetryLoggers = (props, dispatch) => {
|
|
|
24
24
|
appInsightsConfig: Object.assign({}, defaultAppInsightsConfig, props.appInsightsConfig)
|
|
25
25
|
};
|
|
26
26
|
if (props.chatConfig) {
|
|
27
|
+
var _props$chatConfig$Liv;
|
|
27
28
|
telemetryData = TelemetryHelper.addChatConfigDataToTelemetry(props === null || props === void 0 ? void 0 : props.chatConfig, telemetryData);
|
|
29
|
+
//store AppInsights instrumentation key from chatConfig if present
|
|
30
|
+
telemetryData.chatConfigAppInsightsKey = (_props$chatConfig$Liv = props.chatConfig.LiveWSAndLiveChatEngJoin) === null || _props$chatConfig$Liv === void 0 ? void 0 : _props$chatConfig$Liv.AppInsightsInstrumentationKey;
|
|
28
31
|
}
|
|
29
32
|
if (!((_props$chatSDK = props.chatSDK) !== null && _props$chatSDK !== void 0 && (_props$chatSDK$omnich = _props$chatSDK.omnichannelConfig) !== null && _props$chatSDK$omnich !== void 0 && _props$chatSDK$omnich.orgId) || ((_props$chatSDK2 = props.chatSDK) === null || _props$chatSDK2 === void 0 ? void 0 : (_props$chatSDK2$omnic = _props$chatSDK2.omnichannelConfig) === null || _props$chatSDK2$omnic === void 0 ? void 0 : _props$chatSDK2$omnic.orgId.trim().length) === 0) {
|
|
30
33
|
throw new Error("orgId is undefined in ChatSDK");
|
|
@@ -149,7 +149,7 @@ const getPostChatContext = async (facadeChatSDK, state, dispatch) => {
|
|
|
149
149
|
if ((state === null || state === void 0 ? void 0 : (_state$domainStates2 = state.domainStates) === null || _state$domainStates2 === void 0 ? void 0 : _state$domainStates2.postChatContext) === undefined) {
|
|
150
150
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
151
151
|
const context = await facadeChatSDK.getPostChatSurveyContext();
|
|
152
|
-
TelemetryHelper.
|
|
152
|
+
TelemetryHelper.logSDKEvent(LogLevel.INFO, {
|
|
153
153
|
Event: TelemetryEvent.PostChatContextCallSucceed,
|
|
154
154
|
Description: PostChatSurveyTelemetryMessage.PostChatContextCallSucceed
|
|
155
155
|
});
|
|
@@ -167,7 +167,7 @@ const getPostChatContext = async (facadeChatSDK, state, dispatch) => {
|
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
} catch (error) {
|
|
170
|
-
TelemetryHelper.
|
|
170
|
+
TelemetryHelper.logSDKEvent(LogLevel.ERROR, {
|
|
171
171
|
Event: TelemetryEvent.PostChatContextCallFailed,
|
|
172
172
|
Description: PostChatSurveyTelemetryMessage.PostChatContextCallFailed,
|
|
173
173
|
ExceptionDetails: {
|
|
@@ -19,13 +19,13 @@ export const setPostChatContextAndLoadSurvey = async (facadeChatSDK, dispatch, p
|
|
|
19
19
|
const postChatEnabled = postChatConfig.postChatEnabled;
|
|
20
20
|
if (postChatEnabled) {
|
|
21
21
|
if (!persistedChat) {
|
|
22
|
-
TelemetryHelper.
|
|
22
|
+
TelemetryHelper.logSDKEvent(LogLevel.INFO, {
|
|
23
23
|
Event: TelemetryEvent.PostChatContextCallStarted,
|
|
24
24
|
Description: PostChatSurveyTelemetryMessage.PostChatContextCallStarted
|
|
25
25
|
});
|
|
26
26
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
27
27
|
const context = await facadeChatSDK.getPostChatSurveyContext();
|
|
28
|
-
TelemetryHelper.
|
|
28
|
+
TelemetryHelper.logSDKEvent(LogLevel.INFO, {
|
|
29
29
|
Event: TelemetryEvent.PostChatContextCallSucceed,
|
|
30
30
|
Description: PostChatSurveyTelemetryMessage.PostChatContextCallSucceed
|
|
31
31
|
});
|
|
@@ -42,7 +42,7 @@ export const setPostChatContextAndLoadSurvey = async (facadeChatSDK, dispatch, p
|
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
} catch (ex) {
|
|
45
|
-
TelemetryHelper.
|
|
45
|
+
TelemetryHelper.logSDKEvent(LogLevel.ERROR, {
|
|
46
46
|
Event: TelemetryEvent.PostChatContextCallFailed,
|
|
47
47
|
Description: PostChatSurveyTelemetryMessage.PostChatContextCallFailed,
|
|
48
48
|
ExceptionDetails: {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { BroadcastEvent, LogLevel, TelemetryEvent } from "../../../common/telemetry/TelemetryConstants";
|
|
1
|
+
import { BroadcastEvent, ConversationStage, LogLevel, TelemetryEvent } from "../../../common/telemetry/TelemetryConstants";
|
|
2
2
|
import { Constants, LiveWorkItemState, WidgetLoadTelemetryMessage } from "../../../common/Constants";
|
|
3
3
|
import { TelemetryManager, TelemetryTimers } from "../../../common/telemetry/TelemetryManager";
|
|
4
4
|
import { checkContactIdError, createTimer, getConversationDetailsCall, getStateFromCache, getWidgetCacheIdfromProps, isNullOrEmptyString, isNullOrUndefined, isUndefinedOrEmpty } from "../../../common/utils";
|
|
5
5
|
import { handleChatReconnect, isPersistentEnabled, isReconnectEnabled } from "./reconnectChatHelper";
|
|
6
|
-
import { handleStartChatError, logWidgetLoadComplete } from "./startChatErrorHandler";
|
|
6
|
+
import { handleStartChatError, logStartChatComplete, logWidgetLoadComplete } from "./startChatErrorHandler";
|
|
7
7
|
import { ActivityStreamHandler } from "./ActivityStreamHandler";
|
|
8
8
|
import { BroadcastService } from "@microsoft/omnichannel-chat-components";
|
|
9
9
|
import { ConversationState } from "../../../contexts/common/ConversationState";
|
|
@@ -174,7 +174,10 @@ const initStartChat = async (facadeChatSDK, dispatch, setAdapter, state, props,
|
|
|
174
174
|
TelemetryTimers.WidgetLoadTimer = createTimer();
|
|
175
175
|
TelemetryHelper.logLoadingEventToAllTelemetry(LogLevel.INFO, {
|
|
176
176
|
Event: TelemetryEvent.WidgetLoadStarted,
|
|
177
|
-
Description: "Widget start chat started."
|
|
177
|
+
Description: "Widget start chat started.",
|
|
178
|
+
CustomProperties: {
|
|
179
|
+
ConversationStage: ConversationStage.Initialization
|
|
180
|
+
}
|
|
178
181
|
});
|
|
179
182
|
|
|
180
183
|
//Check if chat retrieved from cache
|
|
@@ -202,6 +205,7 @@ const initStartChat = async (facadeChatSDK, dispatch, setAdapter, state, props,
|
|
|
202
205
|
const startTime = new Date().getTime();
|
|
203
206
|
createTrackingForFirstMessage();
|
|
204
207
|
await facadeChatSDK.startChat(startChatOptionalParams);
|
|
208
|
+
logStartChatComplete();
|
|
205
209
|
isStartChatSuccessful = true;
|
|
206
210
|
await createAdapterAndSubscribe(facadeChatSDK, dispatch, setAdapter, startTime, props);
|
|
207
211
|
} catch (error) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ChatSDKError, ChatSDKErrorName } from "@microsoft/omnichannel-chat-sdk";
|
|
2
|
-
import { LogLevel, TelemetryEvent } from "../../../common/telemetry/TelemetryConstants";
|
|
2
|
+
import { ConversationStage, LogLevel, TelemetryEvent } from "../../../common/telemetry/TelemetryConstants";
|
|
3
3
|
import { PrepareEndChatDescriptionConstants, WidgetLoadCustomErrorString, WidgetLoadTelemetryMessage } from "../../../common/Constants";
|
|
4
4
|
import { callingStateCleanUp, chatSDKStateCleanUp, closeChatStateCleanUp, endChatStateCleanUp } from "./endChat";
|
|
5
5
|
import { ConversationState } from "../../../contexts/common/ConversationState";
|
|
@@ -118,7 +118,10 @@ const logWidgetLoadFailed = ex => {
|
|
|
118
118
|
Event: TelemetryEvent.WidgetLoadFailed,
|
|
119
119
|
Description: "Widget load complete with error",
|
|
120
120
|
ExceptionDetails: exDetails,
|
|
121
|
-
ElapsedTimeInMilliseconds: TelemetryTimers === null || TelemetryTimers === void 0 ? void 0 : (_TelemetryTimers$Widg = TelemetryTimers.WidgetLoadTimer) === null || _TelemetryTimers$Widg === void 0 ? void 0 : _TelemetryTimers$Widg.milliSecondsElapsed
|
|
121
|
+
ElapsedTimeInMilliseconds: TelemetryTimers === null || TelemetryTimers === void 0 ? void 0 : (_TelemetryTimers$Widg = TelemetryTimers.WidgetLoadTimer) === null || _TelemetryTimers$Widg === void 0 ? void 0 : _TelemetryTimers$Widg.milliSecondsElapsed,
|
|
122
|
+
CustomProperties: {
|
|
123
|
+
ConversationStage: ConversationStage.Initialization
|
|
124
|
+
}
|
|
122
125
|
});
|
|
123
126
|
};
|
|
124
127
|
export const logWidgetLoadComplete = additionalMessage => {
|
|
@@ -133,6 +136,16 @@ export const logWidgetLoadComplete = additionalMessage => {
|
|
|
133
136
|
ElapsedTimeInMilliseconds: TelemetryTimers === null || TelemetryTimers === void 0 ? void 0 : (_TelemetryTimers$Widg2 = TelemetryTimers.WidgetLoadTimer) === null || _TelemetryTimers$Widg2 === void 0 ? void 0 : _TelemetryTimers$Widg2.milliSecondsElapsed
|
|
134
137
|
});
|
|
135
138
|
};
|
|
139
|
+
export const logStartChatComplete = additionalMessage => {
|
|
140
|
+
let descriptionString = "Start chat complete";
|
|
141
|
+
if (additionalMessage) {
|
|
142
|
+
descriptionString += `. ${additionalMessage}`;
|
|
143
|
+
}
|
|
144
|
+
TelemetryHelper.logLoadingEvent(LogLevel.INFO, {
|
|
145
|
+
Event: TelemetryEvent.StartChatComplete,
|
|
146
|
+
Description: descriptionString
|
|
147
|
+
});
|
|
148
|
+
};
|
|
136
149
|
const logWidgetLoadCompleteWithError = ex => {
|
|
137
150
|
var _TelemetryTimers$Widg3;
|
|
138
151
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -149,7 +162,10 @@ const logWidgetLoadCompleteWithError = ex => {
|
|
|
149
162
|
Event: TelemetryEvent.WidgetLoadFailed,
|
|
150
163
|
Description: "Widget load complete with error",
|
|
151
164
|
ExceptionDetails: exDetails,
|
|
152
|
-
ElapsedTimeInMilliseconds: TelemetryTimers === null || TelemetryTimers === void 0 ? void 0 : (_TelemetryTimers$Widg3 = TelemetryTimers.WidgetLoadTimer) === null || _TelemetryTimers$Widg3 === void 0 ? void 0 : _TelemetryTimers$Widg3.milliSecondsElapsed
|
|
165
|
+
ElapsedTimeInMilliseconds: TelemetryTimers === null || TelemetryTimers === void 0 ? void 0 : (_TelemetryTimers$Widg3 = TelemetryTimers.WidgetLoadTimer) === null || _TelemetryTimers$Widg3 === void 0 ? void 0 : _TelemetryTimers$Widg3.milliSecondsElapsed,
|
|
166
|
+
CustomProperties: {
|
|
167
|
+
ConversationStage: ConversationStage.Initialization
|
|
168
|
+
}
|
|
153
169
|
});
|
|
154
170
|
};
|
|
155
171
|
export const logWidgetLoadWithUnexpectedError = ex => {
|
|
@@ -177,7 +193,10 @@ export const logWidgetLoadWithUnexpectedError = ex => {
|
|
|
177
193
|
Event: TelemetryEvent.WidgetLoadFailed,
|
|
178
194
|
Description: "Widget load with unexpected error",
|
|
179
195
|
ExceptionDetails: exDetails,
|
|
180
|
-
ElapsedTimeInMilliseconds: TelemetryTimers === null || TelemetryTimers === void 0 ? void 0 : (_TelemetryTimers$Widg4 = TelemetryTimers.WidgetLoadTimer) === null || _TelemetryTimers$Widg4 === void 0 ? void 0 : _TelemetryTimers$Widg4.milliSecondsElapsed
|
|
196
|
+
ElapsedTimeInMilliseconds: TelemetryTimers === null || TelemetryTimers === void 0 ? void 0 : (_TelemetryTimers$Widg4 = TelemetryTimers.WidgetLoadTimer) === null || _TelemetryTimers$Widg4 === void 0 ? void 0 : _TelemetryTimers$Widg4.milliSecondsElapsed,
|
|
197
|
+
CustomProperties: {
|
|
198
|
+
ConversationStage: ConversationStage.Initialization
|
|
199
|
+
}
|
|
181
200
|
});
|
|
182
201
|
};
|
|
183
202
|
|