@microsoft/omnichannel-chat-widget 1.6.2 → 1.6.3-main.25307f2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. package/lib/cjs/common/utils.js +6 -2
  2. package/lib/cjs/components/livechatwidget/common/authHelper.js +4 -1
  3. package/lib/cjs/components/livechatwidget/common/endChat.js +1 -22
  4. package/lib/cjs/components/livechatwidget/common/reconnectChatHelper.js +13 -3
  5. package/lib/cjs/components/livechatwidget/common/startChatErrorHandler.js +5 -0
  6. package/lib/cjs/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +28 -34
  7. package/lib/cjs/components/webchatcontainerstateful/common/utils/isMaskingFromCustomer.js +4 -2
  8. package/lib/cjs/contexts/common/LiveChatWidgetActionType.js +1 -0
  9. package/lib/cjs/contexts/createReducer.js +653 -345
  10. package/lib/esm/common/utils.js +3 -0
  11. package/lib/esm/components/livechatwidget/common/authHelper.js +4 -1
  12. package/lib/esm/components/livechatwidget/common/endChat.js +2 -23
  13. package/lib/esm/components/livechatwidget/common/reconnectChatHelper.js +15 -4
  14. package/lib/esm/components/livechatwidget/common/startChatErrorHandler.js +5 -0
  15. package/lib/esm/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +28 -34
  16. package/lib/esm/components/webchatcontainerstateful/common/utils/isMaskingFromCustomer.js +4 -2
  17. package/lib/esm/contexts/common/LiveChatWidgetActionType.js +1 -0
  18. package/lib/esm/contexts/createReducer.js +650 -343
  19. package/lib/types/common/utils.d.ts +1 -0
  20. package/lib/types/components/livechatwidget/common/reconnectChatHelper.d.ts +1 -1
  21. package/lib/types/contexts/common/LiveChatWidgetActionType.d.ts +2 -1
  22. package/lib/types/contexts/createReducer.d.ts +1 -0
  23. package/package.json +2 -2
@@ -413,4 +413,7 @@ export const formatTemplateString = (templateMessage, values) => {
413
413
  return templateMessage.replace(/{(\d+)}/g, (match, index) => {
414
414
  return typeof values[index] !== "undefined" ? values[index] : match;
415
415
  });
416
+ };
417
+ export const parseLowerCaseString = property => {
418
+ return String(property).toLowerCase();
416
419
  };
@@ -1,6 +1,7 @@
1
1
  import { LogLevel, TelemetryEvent } from "../../../common/telemetry/TelemetryConstants";
2
2
  import { TelemetryHelper } from "../../../common/telemetry/TelemetryHelper";
3
3
  import { isNullOrEmptyString } from "../../../common/utils";
4
+ import { WidgetLoadCustomErrorString } from "../../../common/Constants";
4
5
  const getAuthClientFunction = chatConfig => {
5
6
  let authClientFunction = undefined;
6
7
  if (chatConfig !== null && chatConfig !== void 0 && chatConfig.LiveChatConfigAuthSettings) {
@@ -25,10 +26,12 @@ const handleAuthentication = async (chatSDK, chatConfig, getAuthToken) => {
25
26
  });
26
27
  return true;
27
28
  } else {
29
+ // instead of returning false, it's more appropiate to thrown an error to force error handling on the caller side
30
+ // this will help to avoid the error to be ignored and the chat to be started
28
31
  TelemetryHelper.logActionEvent(LogLevel.ERROR, {
29
32
  Event: TelemetryEvent.ReceivedNullOrEmptyToken
30
33
  });
31
- return false;
34
+ throw new Error(WidgetLoadCustomErrorString.AuthenticationFailedErrorString);
32
35
  }
33
36
  }
34
37
  return false;
@@ -1,6 +1,6 @@
1
1
  import { ConfirmationState, Constants, ConversationEndEntity, ParticipantType, PrepareEndChatDescriptionConstants } from "../../../common/Constants";
2
2
  import { LogLevel, TelemetryEvent } from "../../../common/telemetry/TelemetryConstants";
3
- import { getAuthClientFunction, handleAuthentication } from "./authHelper";
3
+ import { handleAuthentication } from "./authHelper";
4
4
  import { getConversationDetailsCall, getWidgetEndChatEventName } from "../../../common/utils";
5
5
  import { getPostChatContext, initiatePostChat } from "./renderSurveyHelpers";
6
6
  import { BroadcastService } from "@microsoft/omnichannel-chat-components";
@@ -113,7 +113,7 @@ const endChat = async (props, chatSDK, state, dispatch, setAdapter, setWebChatSt
113
113
  Event: TelemetryEvent.EndChatSDKCall
114
114
  });
115
115
  //Get auth token again if chat continued for longer time, otherwise gets 401 error
116
- await handleAuthenticationIfEnabled(props, chatSDK);
116
+ await handleAuthentication(chatSDK, props.chatConfig, props.getAuthToken);
117
117
  await (chatSDK === null || chatSDK === void 0 ? void 0 : chatSDK.endChat());
118
118
  } catch (ex) {
119
119
  TelemetryHelper.logSDKEvent(LogLevel.ERROR, {
@@ -296,27 +296,6 @@ const closeChatWidget = (dispatch, props, state) => {
296
296
  });
297
297
  };
298
298
 
299
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
300
- const handleAuthenticationIfEnabled = async (props, chatSDK) => {
301
- //Unable to end chat if token has expired
302
- if (props.getAuthToken) {
303
- const authClientFunction = getAuthClientFunction(props.chatConfig);
304
- if (props.getAuthToken && authClientFunction) {
305
- // set auth token to chat sdk before end chat
306
- const authSuccess = await handleAuthentication(chatSDK, props.chatConfig, props.getAuthToken);
307
- if (!authSuccess) {
308
- TelemetryHelper.logActionEvent(LogLevel.ERROR, {
309
- Event: TelemetryEvent.GetAuthTokenFailed,
310
- ExceptionDetails: {
311
- exception: "Unable to get auth token during end chat"
312
- }
313
- });
314
- throw new Error("handleAuthenticationIfEnabled:Failed to get authentication token");
315
- }
316
- }
317
- }
318
- };
319
-
320
299
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
321
300
  const chatTokenCleanUp = async dispatch => {
322
301
  //Just do cleanup here
@@ -3,10 +3,12 @@ import { BroadcastEvent, LogLevel, TelemetryEvent } from "../../../common/teleme
3
3
  import { checkContactIdError, isNullOrEmptyString, isNullOrUndefined } from "../../../common/utils";
4
4
  import { handleAuthentication, removeAuthTokenProvider } from "./authHelper";
5
5
  import { BroadcastService } from "@microsoft/omnichannel-chat-components";
6
- import { ConversationMode } from "../../../common/Constants";
6
+ import { ConversationMode, WidgetLoadCustomErrorString } from "../../../common/Constants";
7
7
  import { ConversationState } from "../../../contexts/common/ConversationState";
8
8
  import { LiveChatWidgetActionType } from "../../../contexts/common/LiveChatWidgetActionType";
9
9
  import { TelemetryHelper } from "../../../common/telemetry/TelemetryHelper";
10
+ import { handleStartChatError } from "./startChatErrorHandler";
11
+
10
12
  // Return value: should start normal chat flow when reconnect is enabled
11
13
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
14
  const handleChatReconnect = async (chatSDK, props, dispatch, setAdapter, initStartChat, state) => {
@@ -15,7 +17,7 @@ const handleChatReconnect = async (chatSDK, props, dispatch, setAdapter, initSta
15
17
  const isAuthenticatedChat = (_props$chatConfig = props.chatConfig) !== null && _props$chatConfig !== void 0 && (_props$chatConfig$Liv = _props$chatConfig.LiveChatConfigAuthSettings) !== null && _props$chatConfig$Liv !== void 0 && _props$chatConfig$Liv.msdyn_javascriptclientfunction ? true : false;
16
18
 
17
19
  // Get chat reconnect context
18
- const reconnectChatContext = await getChatReconnectContext(chatSDK, props.chatConfig, props, isAuthenticatedChat);
20
+ const reconnectChatContext = await getChatReconnectContext(chatSDK, props.chatConfig, props, isAuthenticatedChat, dispatch);
19
21
 
20
22
  // Redirect if enabled
21
23
  if (reconnectChatContext !== null && reconnectChatContext !== void 0 && reconnectChatContext.redirectURL) {
@@ -50,7 +52,7 @@ const handleChatReconnect = async (chatSDK, props, dispatch, setAdapter, initSta
50
52
  };
51
53
 
52
54
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
53
- const getChatReconnectContext = async (chatSDK, chatConfig, props, isAuthenticatedChat) => {
55
+ const getChatReconnectContext = async (chatSDK, chatConfig, props, isAuthenticatedChat, dispatch) => {
54
56
  try {
55
57
  var _props$reconnectChatP4;
56
58
  TelemetryHelper.logSDKEvent(LogLevel.INFO, {
@@ -62,6 +64,7 @@ const getChatReconnectContext = async (chatSDK, chatConfig, props, isAuthenticat
62
64
  };
63
65
  // Get auth token for getting chat reconnect context
64
66
  if (isAuthenticatedChat) {
67
+ // handle authentication will throw error if auth token is not available, so no need to check for response
65
68
  await handleAuthentication(chatSDK, chatConfig, props.getAuthToken);
66
69
  }
67
70
  const reconnectChatContext = await (chatSDK === null || chatSDK === void 0 ? void 0 : chatSDK.getChatReconnectContext(chatReconnectOptionalParams));
@@ -71,7 +74,9 @@ const getChatReconnectContext = async (chatSDK, chatConfig, props, isAuthenticat
71
74
  removeAuthTokenProvider(chatSDK);
72
75
  }
73
76
  return reconnectChatContext;
74
- } catch (error) {
77
+ }
78
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
79
+ catch (error) {
75
80
  checkContactIdError(error);
76
81
  TelemetryHelper.logSDKEvent(LogLevel.ERROR, {
77
82
  Event: TelemetryEvent.GetChatReconnectContextSDKCallFailed,
@@ -79,6 +84,12 @@ const getChatReconnectContext = async (chatSDK, chatConfig, props, isAuthenticat
79
84
  exception: error
80
85
  }
81
86
  });
87
+
88
+ // when auth token is not available, propagate the error to stop the execution and ensure error pane is loaded
89
+ if ((error === null || error === void 0 ? void 0 : error.message) == WidgetLoadCustomErrorString.AuthenticationFailedErrorString) {
90
+ handleStartChatError(dispatch, chatSDK, props, new Error(WidgetLoadCustomErrorString.AuthenticationFailedErrorString), false);
91
+ throw error;
92
+ }
82
93
  }
83
94
  };
84
95
 
@@ -24,6 +24,11 @@ export const handleStartChatError = (dispatch, chatSDK, props, ex, isStartChatSu
24
24
  type: LiveChatWidgetActionType.SET_START_CHAT_FAILURE_TYPE,
25
25
  payload: StartChatFailureType.AuthSetupError
26
26
  });
27
+ // set conversation to error to enforce error UI pane
28
+ dispatch({
29
+ type: LiveChatWidgetActionType.SET_CONVERSATION_STATE,
30
+ payload: ConversationState.Error
31
+ });
27
32
  logWidgetLoadCompleteWithError(ex);
28
33
  }
29
34
  if (ex.message === WidgetLoadCustomErrorString.NetworkErrorString) {
@@ -53,6 +53,7 @@ import useChatSDKStore from "../../../hooks/useChatSDKStore";
53
53
  import { defaultAdaptiveCardStyles } from "../../webchatcontainerstateful/common/defaultStyles/defaultAdaptiveCardStyles";
54
54
  import StartChatErrorPaneStateful from "../../startchaterrorpanestateful/StartChatErrorPaneStateful";
55
55
  import { StartChatFailureType } from "../../../contexts/common/StartChatFailureType";
56
+ import { executeReducer } from "../../../contexts/createReducer";
56
57
  export const LiveChatWidgetStateful = props => {
57
58
  var _props$webChatContain, _props$styleProps, _props$controlProps, _props$controlProps3, _state$appStates7, _props$webChatContain5, _state$appStates14, _props$webChatContain6, _props$controlProps12, _props$draggableChatW, _props$draggableChatW2, _props$draggableChatW3, _props$draggableChatW4, _props$draggableChatW5, _props$webChatContain7, _props$webChatContain8, _props$webChatContain9, _props$webChatContain10, _livechatProps$webCha, _livechatProps$styleP, _livechatProps$contro, _livechatProps$contro2, _livechatProps$compon, _livechatProps$contro3, _livechatProps$compon2, _livechatProps$contro4, _livechatProps$compon3, _livechatProps$contro5, _livechatProps$compon4, _livechatProps$contro6, _livechatProps$compon5, _livechatProps$contro7, _livechatProps$compon6, _livechatProps$contro8, _livechatProps$compon7, _livechatProps$contro9, _livechatProps$compon8, _livechatProps$contro10, _livechatProps$contro11, _livechatProps$compon9, _livechatProps$contro12, _livechatProps$compon10, _livechatProps$contro13, _livechatProps$compon11, _livechatProps$compon12, _livechatProps$compon13;
58
59
  const [state, dispatch] = useChatContextStore();
@@ -117,8 +118,12 @@ export const LiveChatWidgetStateful = props => {
117
118
  const isReconnectTriggered = async () => {
118
119
  if (isReconnectEnabled(props.chatConfig) === true && !isPersistentEnabled(props.chatConfig)) {
119
120
  const noValidReconnectId = await handleChatReconnect(chatSDK, props, dispatch, setAdapter, initStartChat, state);
121
+ const inMemoryState = executeReducer(state, {
122
+ type: LiveChatWidgetActionType.GET_IN_MEMORY_STATE,
123
+ payload: null
124
+ });
120
125
  // If chat reconnect has kicked in chat state will become Active or Reconnect. So just exit, else go next
121
- if (!noValidReconnectId && (state.appStates.conversationState === ConversationState.Active || state.appStates.conversationState === ConversationState.ReconnectChat)) {
126
+ if (!noValidReconnectId && (inMemoryState.appStates.conversationState === ConversationState.Active || inMemoryState.appStates.conversationState === ConversationState.ReconnectChat)) {
122
127
  return true;
123
128
  }
124
129
  }
@@ -332,7 +337,7 @@ export const LiveChatWidgetStateful = props => {
332
337
 
333
338
  // Start chat from SDK Event
334
339
  BroadcastService.getMessageByEventName(BroadcastEvent.StartChat).subscribe(msg => {
335
- var _msg$payload4, _msg$payload5, _msg$payload6;
340
+ var _msg$payload4, _msg$payload5, _msg$payload6, _inMemoryState$appSta, _inMemoryState$appSta2, _inMemoryState$appSta3, _inMemoryState$appSta4;
336
341
  // If the startChat event is not initiated by the same tab. Ignore the call
337
342
  if (!isNullOrUndefined(msg === null || msg === void 0 ? void 0 : (_msg$payload4 = msg.payload) === null || _msg$payload4 === void 0 ? void 0 : _msg$payload4.runtimeId) && (msg === null || msg === void 0 ? void 0 : (_msg$payload5 = msg.payload) === null || _msg$payload5 === void 0 ? void 0 : _msg$payload5.runtimeId) !== TelemetryManager.InternalTelemetryData.lcwRuntimeId) {
338
343
  return;
@@ -360,12 +365,13 @@ export const LiveChatWidgetStateful = props => {
360
365
  Event: TelemetryEvent.StartChatEventRecevied,
361
366
  Description: "Start chat event received."
362
367
  });
368
+ const inMemoryState = executeReducer(state, {
369
+ type: LiveChatWidgetActionType.GET_IN_MEMORY_STATE,
370
+ payload: null
371
+ });
363
372
 
364
- // DataStoreManager.clientDataStore?.swtichToSessionStorage(true);
365
- const persistedState = getStateFromCache(getWidgetCacheIdfromProps(props));
366
-
367
- // Chat not found in cache - scenario: explicitly clearing cache and calling startChat SDK method
368
- if (persistedState === undefined) {
373
+ // Only initiate new chat if widget runtime state is one of the followings
374
+ if (((_inMemoryState$appSta = inMemoryState.appStates) === null || _inMemoryState$appSta === void 0 ? void 0 : _inMemoryState$appSta.conversationState) === ConversationState.Closed || ((_inMemoryState$appSta2 = inMemoryState.appStates) === null || _inMemoryState$appSta2 === void 0 ? void 0 : _inMemoryState$appSta2.conversationState) === ConversationState.InActive || ((_inMemoryState$appSta3 = inMemoryState.appStates) === null || _inMemoryState$appSta3 === void 0 ? void 0 : _inMemoryState$appSta3.conversationState) === ConversationState.Postchat) {
369
375
  BroadcastService.postMessage({
370
376
  eventName: BroadcastEvent.ChatInitiated
371
377
  });
@@ -373,33 +379,21 @@ export const LiveChatWidgetStateful = props => {
373
379
  return;
374
380
  }
375
381
 
376
- // Chat exist in cache
377
- if (persistedState) {
378
- var _persistedState$appSt, _persistedState$appSt2, _persistedState$appSt3, _persistedState$appSt4;
379
- // Only initiate new chat if widget state in cache in one of the followings
380
- if (((_persistedState$appSt = persistedState.appStates) === null || _persistedState$appSt === void 0 ? void 0 : _persistedState$appSt.conversationState) === ConversationState.Closed || ((_persistedState$appSt2 = persistedState.appStates) === null || _persistedState$appSt2 === void 0 ? void 0 : _persistedState$appSt2.conversationState) === ConversationState.InActive || ((_persistedState$appSt3 = persistedState.appStates) === null || _persistedState$appSt3 === void 0 ? void 0 : _persistedState$appSt3.conversationState) === ConversationState.Postchat) {
381
- BroadcastService.postMessage({
382
- eventName: BroadcastEvent.ChatInitiated
383
- });
384
- prepareStartChat(props, chatSDK, stateWithUpdatedContext, dispatch, setAdapter);
385
- return;
386
- }
387
-
388
- // If minimized, maximize the chat
389
- if ((persistedState === null || persistedState === void 0 ? void 0 : (_persistedState$appSt4 = persistedState.appStates) === null || _persistedState$appSt4 === void 0 ? void 0 : _persistedState$appSt4.isMinimized) === true) {
390
- var _persistedState$domai, _persistedState$domai2, _persistedState$domai3, _persistedState$domai4;
391
- dispatch({
392
- type: LiveChatWidgetActionType.SET_MINIMIZED,
393
- payload: false
394
- });
395
- BroadcastService.postMessage({
396
- eventName: BroadcastEvent.MaximizeChat,
397
- payload: {
398
- height: persistedState === null || persistedState === void 0 ? void 0 : (_persistedState$domai = persistedState.domainStates) === null || _persistedState$domai === void 0 ? void 0 : (_persistedState$domai2 = _persistedState$domai.widgetSize) === null || _persistedState$domai2 === void 0 ? void 0 : _persistedState$domai2.height,
399
- width: persistedState === null || persistedState === void 0 ? void 0 : (_persistedState$domai3 = persistedState.domainStates) === null || _persistedState$domai3 === void 0 ? void 0 : (_persistedState$domai4 = _persistedState$domai3.widgetSize) === null || _persistedState$domai4 === void 0 ? void 0 : _persistedState$domai4.width
400
- }
401
- });
402
- }
382
+ // If minimized, maximize the chat
383
+ if ((inMemoryState === null || inMemoryState === void 0 ? void 0 : (_inMemoryState$appSta4 = inMemoryState.appStates) === null || _inMemoryState$appSta4 === void 0 ? void 0 : _inMemoryState$appSta4.isMinimized) === true) {
384
+ var _inMemoryState$domain, _inMemoryState$domain2, _inMemoryState$domain3, _inMemoryState$domain4;
385
+ dispatch({
386
+ type: LiveChatWidgetActionType.SET_MINIMIZED,
387
+ payload: false
388
+ });
389
+ BroadcastService.postMessage({
390
+ eventName: BroadcastEvent.MaximizeChat,
391
+ payload: {
392
+ height: inMemoryState === null || inMemoryState === void 0 ? void 0 : (_inMemoryState$domain = inMemoryState.domainStates) === null || _inMemoryState$domain === void 0 ? void 0 : (_inMemoryState$domain2 = _inMemoryState$domain.widgetSize) === null || _inMemoryState$domain2 === void 0 ? void 0 : _inMemoryState$domain2.height,
393
+ width: inMemoryState === null || inMemoryState === void 0 ? void 0 : (_inMemoryState$domain3 = inMemoryState.domainStates) === null || _inMemoryState$domain3 === void 0 ? void 0 : (_inMemoryState$domain4 = _inMemoryState$domain3.widgetSize) === null || _inMemoryState$domain4 === void 0 ? void 0 : _inMemoryState$domain4.width
394
+ }
395
+ });
396
+ return;
403
397
  }
404
398
  });
405
399
 
@@ -1,3 +1,4 @@
1
+ import { isNullOrUndefined, parseLowerCaseString } from "../../../../common/utils";
1
2
  export const isMaskingforCustomer = maskingInfo => {
2
3
  var _maskingInfo$setting;
3
4
  // If the masking info (containing masking setting and masking rules) is missing or empty, return false.
@@ -8,8 +9,9 @@ export const isMaskingforCustomer = maskingInfo => {
8
9
 
9
10
  // If the masking rules are provided and;
10
11
  // If the masking setting is NOT null and masking for customer is NOT null, return the configuration
11
- if ((_maskingInfo$setting = maskingInfo.setting) !== null && _maskingInfo$setting !== void 0 && _maskingInfo$setting.msdyn_maskforcustomer) {
12
- return maskingInfo.setting.msdyn_maskforcustomer;
12
+ if (!isNullOrUndefined((_maskingInfo$setting = maskingInfo.setting) === null || _maskingInfo$setting === void 0 ? void 0 : _maskingInfo$setting.msdyn_maskforcustomer)) {
13
+ var _maskingInfo$setting2;
14
+ return parseLowerCaseString(maskingInfo === null || maskingInfo === void 0 ? void 0 : (_maskingInfo$setting2 = maskingInfo.setting) === null || _maskingInfo$setting2 === void 0 ? void 0 : _maskingInfo$setting2.msdyn_maskforcustomer) === "true";
13
15
  }
14
16
 
15
17
  // In all other cases, even if masking setting is missing, return true to apply the masking for backward compatibility (i.e. in old versions, OC does not have masking info settings)
@@ -45,4 +45,5 @@ export let LiveChatWidgetActionType;
45
45
  LiveChatWidgetActionType[LiveChatWidgetActionType["SET_SURVEY_MODE"] = 42] = "SET_SURVEY_MODE";
46
46
  LiveChatWidgetActionType[LiveChatWidgetActionType["SET_CONFIRMATION_STATE"] = 43] = "SET_CONFIRMATION_STATE";
47
47
  LiveChatWidgetActionType[LiveChatWidgetActionType["SET_POST_CHAT_PARTICIPANT_TYPE"] = 44] = "SET_POST_CHAT_PARTICIPANT_TYPE";
48
+ LiveChatWidgetActionType[LiveChatWidgetActionType["GET_IN_MEMORY_STATE"] = 45] = "GET_IN_MEMORY_STATE";
48
49
  })(LiveChatWidgetActionType || (LiveChatWidgetActionType = {}));