@microsoft/omnichannel-chat-widget 1.8.4-main.b46102f → 1.8.4-main.bedd258

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 (82) hide show
  1. package/lib/cjs/common/Constants.js +4 -0
  2. package/lib/cjs/common/facades/FacadeChatSDK.js +235 -9
  3. package/lib/cjs/common/telemetry/TelemetryConstants.js +10 -0
  4. package/lib/cjs/common/utils.js +92 -16
  5. package/lib/cjs/components/citationpanestateful/CitationPaneStateful.js +2 -1
  6. package/lib/cjs/components/confirmationpanestateful/ConfirmationPaneStateful.js +2 -1
  7. package/lib/cjs/components/emailtranscriptpanestateful/EmailTranscriptPaneStateful.js +32 -13
  8. package/lib/cjs/components/livechatwidget/LiveChatWidget.js +9 -1
  9. package/lib/cjs/components/livechatwidget/common/authHelper.js +44 -4
  10. package/lib/cjs/components/livechatwidget/common/endChat.js +6 -0
  11. package/lib/cjs/components/livechatwidget/common/initWebChatComposer.js +182 -1
  12. package/lib/cjs/components/livechatwidget/common/startChat.js +31 -14
  13. package/lib/cjs/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +112 -3
  14. package/lib/cjs/components/prechatsurveypanestateful/common/defaultStyles/defaultGeneralPreChatSurveyPaneStyleProps.js +2 -1
  15. package/lib/cjs/components/webchatcontainerstateful/WebChatContainerStateful.js +36 -9
  16. package/lib/cjs/components/webchatcontainerstateful/common/defaultProps/defaultMiddlewareLocalizedTexts.js +3 -0
  17. package/lib/cjs/components/webchatcontainerstateful/common/defaultStyles/defaultAdaptiveCardStyles.js +3 -1
  18. package/lib/cjs/components/webchatcontainerstateful/common/defaultStyles/defaultWebChatStyles.js +2 -2
  19. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.js +26 -7
  20. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/attachmentMiddleware.js +2 -1
  21. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/attachments/AdaptiveCardAccessibilityWrapper.js +138 -0
  22. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/defaultStyles/defaultAvatarTextStyles.js +1 -1
  23. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/defaultStyles/defaultSystemMessageStyles.js +8 -3
  24. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/defaultStyles/defaultTimestampRetryStyles.js +8 -1
  25. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/NotDeliveredTimestamp.js +3 -13
  26. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/attachmentSentAnnouncementMiddleware.js +81 -0
  27. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/attachmentUploadValidatorMiddleware.js +33 -1
  28. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/localizedStringsBotInitialsMiddleware.js +9 -3
  29. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/notification/NotificationHandler.js +55 -1
  30. package/lib/cjs/contexts/common/LiveChatWidgetActionType.js +1 -0
  31. package/lib/cjs/contexts/common/LiveChatWidgetContextInitialState.js +6 -1
  32. package/lib/cjs/contexts/createReducer.js +15 -0
  33. package/lib/cjs/plugins/newMessageEventHandler.js +20 -3
  34. package/lib/esm/common/Constants.js +4 -0
  35. package/lib/esm/common/facades/FacadeChatSDK.js +236 -10
  36. package/lib/esm/common/telemetry/TelemetryConstants.js +10 -0
  37. package/lib/esm/common/utils.js +87 -13
  38. package/lib/esm/components/citationpanestateful/CitationPaneStateful.js +2 -1
  39. package/lib/esm/components/confirmationpanestateful/ConfirmationPaneStateful.js +2 -1
  40. package/lib/esm/components/emailtranscriptpanestateful/EmailTranscriptPaneStateful.js +33 -14
  41. package/lib/esm/components/livechatwidget/LiveChatWidget.js +9 -1
  42. package/lib/esm/components/livechatwidget/common/authHelper.js +44 -4
  43. package/lib/esm/components/livechatwidget/common/endChat.js +6 -0
  44. package/lib/esm/components/livechatwidget/common/initWebChatComposer.js +182 -1
  45. package/lib/esm/components/livechatwidget/common/startChat.js +31 -14
  46. package/lib/esm/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +113 -4
  47. package/lib/esm/components/prechatsurveypanestateful/common/defaultStyles/defaultGeneralPreChatSurveyPaneStyleProps.js +2 -1
  48. package/lib/esm/components/webchatcontainerstateful/WebChatContainerStateful.js +37 -10
  49. package/lib/esm/components/webchatcontainerstateful/common/defaultProps/defaultMiddlewareLocalizedTexts.js +3 -0
  50. package/lib/esm/components/webchatcontainerstateful/common/defaultStyles/defaultAdaptiveCardStyles.js +3 -1
  51. package/lib/esm/components/webchatcontainerstateful/common/defaultStyles/defaultWebChatStyles.js +2 -2
  52. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.js +23 -5
  53. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/attachmentMiddleware.js +2 -1
  54. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/attachments/AdaptiveCardAccessibilityWrapper.js +130 -0
  55. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/defaultStyles/defaultAvatarTextStyles.js +1 -1
  56. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/defaultStyles/defaultSystemMessageStyles.js +8 -3
  57. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/defaultStyles/defaultTimestampRetryStyles.js +8 -1
  58. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/NotDeliveredTimestamp.js +3 -13
  59. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/attachmentSentAnnouncementMiddleware.js +74 -0
  60. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/attachmentUploadValidatorMiddleware.js +34 -2
  61. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/localizedStringsBotInitialsMiddleware.js +9 -3
  62. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/notification/NotificationHandler.js +55 -1
  63. package/lib/esm/contexts/common/LiveChatWidgetActionType.js +1 -0
  64. package/lib/esm/contexts/common/LiveChatWidgetContextInitialState.js +6 -1
  65. package/lib/esm/contexts/createReducer.js +15 -0
  66. package/lib/esm/plugins/newMessageEventHandler.js +20 -3
  67. package/lib/types/common/Constants.d.ts +3 -0
  68. package/lib/types/common/facades/FacadeChatSDK.d.ts +29 -0
  69. package/lib/types/common/facades/types/IFacadeChatSDKInput.d.ts +3 -1
  70. package/lib/types/common/telemetry/TelemetryConstants.d.ts +10 -2
  71. package/lib/types/common/utils.d.ts +3 -1
  72. package/lib/types/components/livechatwidget/common/authHelper.d.ts +9 -2
  73. package/lib/types/components/webchatcontainerstateful/interfaces/IAdaptiveCardStyles.d.ts +1 -0
  74. package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.d.ts +2 -0
  75. package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/attachments/AdaptiveCardAccessibilityWrapper.d.ts +18 -0
  76. package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/defaultStyles/defaultSystemMessageStyles.d.ts +3 -0
  77. package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/attachmentSentAnnouncementMiddleware.d.ts +12 -0
  78. package/lib/types/components/webchatcontainerstateful/webchatcontroller/notification/NotificationHandler.d.ts +7 -0
  79. package/lib/types/contexts/common/ILiveChatWidgetContext.d.ts +1 -0
  80. package/lib/types/contexts/common/ILiveChatWidgetLocalizedTexts.d.ts +17 -0
  81. package/lib/types/contexts/common/LiveChatWidgetActionType.d.ts +2 -1
  82. package/package.json +6 -5
@@ -146,6 +146,9 @@ _defineProperty(Constants, "customEventValue", "customEventValue");
146
146
  _defineProperty(Constants, "Hidden", "Hidden");
147
147
  _defineProperty(Constants, "EndConversationDueToOverflow", "endconversationduetooverflow");
148
148
  _defineProperty(Constants, "SkipSessionCloseForPersistentChatFlag", "skipSessionCloseForPersistentChat");
149
+ // Minimum font-size for input fields to prevent iOS Safari auto-zoom on focus
150
+ _defineProperty(Constants, "minInputFontSizePx", 16);
151
+ _defineProperty(Constants, "minInputFontSize", "16px");
149
152
  const Regex = (_class = /*#__PURE__*/_createClass(function Regex() {
150
153
  _classCallCheck(this, Regex);
151
154
  }), _defineProperty(_class, "EmailRegex", "^(?:[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\")@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)*|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\])$"), _class);
@@ -155,6 +158,7 @@ let HtmlIdNames = /*#__PURE__*/_createClass(function HtmlIdNames() {
155
158
  });
156
159
  exports.HtmlIdNames = HtmlIdNames;
157
160
  _defineProperty(HtmlIdNames, "MSLiveChatWidget", "MSLiveChatWidget");
161
+ _defineProperty(HtmlIdNames, "fileSentAnnouncementRegionId", "ms_lcw_file_sent_announcement");
158
162
  let HtmlClassNames = /*#__PURE__*/_createClass(function HtmlClassNames() {
159
163
  _classCallCheck(this, HtmlClassNames);
160
164
  });
@@ -26,6 +26,8 @@ let FacadeChatSDK = /*#__PURE__*/function () {
26
26
  _defineProperty(this, "getAuthToken", void 0);
27
27
  _defineProperty(this, "sdkMocked", void 0);
28
28
  _defineProperty(this, "disableReauthentication", void 0);
29
+ // Stays true so CASE 1 re-triggers on every startChat to set deferInitialAuth
30
+ _defineProperty(this, "pendingMidAuthUnauthenticatedState", false);
29
31
  this.chatSDK = input.chatSDK;
30
32
  this.chatConfig = input.chatConfig;
31
33
  this.getAuthToken = input.getAuthToken;
@@ -50,6 +52,12 @@ let FacadeChatSDK = /*#__PURE__*/function () {
50
52
  value: function destroy() {
51
53
  this.token = null;
52
54
  this.expiration = 0;
55
+ if ((0, _authHelper.isMidAuthEnabled)(this.chatConfig)) {
56
+ this.pendingMidAuthUnauthenticatedState = false;
57
+ this.isAuthenticated = true;
58
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
59
+ this.chatSDK.deferInitialAuth = false;
60
+ }
53
61
  }
54
62
  }, {
55
63
  key: "isTokenSet",
@@ -214,6 +222,10 @@ let FacadeChatSDK = /*#__PURE__*/function () {
214
222
  message: "Token is valid"
215
223
  };
216
224
  }
225
+
226
+ // Token missing or expired - need to get a new one via getAuthToken
227
+ // For mid-auth: getAuthToken receives { isMidAuthEnabled: true } so customer implementations
228
+ // can check portal state and return null for logged-out users
217
229
  _TelemetryHelper.TelemetryHelper.logFacadeChatSDKEventToAllTelemetry(_TelemetryConstants.LogLevel.INFO, {
218
230
  Event: _TelemetryConstants.TelemetryEvent.NewTokenValidationStarted,
219
231
  Description: "Token validation started."
@@ -234,6 +246,7 @@ let FacadeChatSDK = /*#__PURE__*/function () {
234
246
  this.token = "";
235
247
  this.expiration = 0;
236
248
  try {
249
+ var _ring$error, _ring$error2;
237
250
  const ring = await (0, _authHelper.handleAuthentication)(this.chatSDK, this.chatConfig, this.getAuthToken);
238
251
  if ((ring === null || ring === void 0 ? void 0 : ring.result) === true && ring !== null && ring !== void 0 && ring.token) {
239
252
  await this.setToken(ring.token);
@@ -248,18 +261,35 @@ let FacadeChatSDK = /*#__PURE__*/function () {
248
261
  result: true,
249
262
  message: "New Token obtained"
250
263
  };
251
- } else {
252
- var _ring$error, _ring$error2;
253
- _TelemetryHelper.TelemetryHelper.logFacadeChatSDKEventToAllTelemetry(_TelemetryConstants.LogLevel.ERROR, {
254
- Event: _TelemetryConstants.TelemetryEvent.NewTokenValidationFailed,
255
- Description: (_ring$error = ring.error) === null || _ring$error === void 0 ? void 0 : _ring$error.message,
256
- ExceptionDetails: ring === null || ring === void 0 ? void 0 : ring.error
264
+ }
265
+
266
+ // Mid-auth: no token available - set pending flag for startChat to handle
267
+ const isEmptyTokenWithoutError = (0, _utils.isNullOrEmptyString)(ring === null || ring === void 0 ? void 0 : ring.token) && ((ring === null || ring === void 0 ? void 0 : ring.result) === true || (ring === null || ring === void 0 ? void 0 : ring.result) === false && !(ring !== null && ring !== void 0 && ring.error));
268
+ if ((0, _authHelper.isMidAuthEnabled)(this.chatConfig) && isEmptyTokenWithoutError) {
269
+ // Clear Facade and SDK token state so API calls use unauthenticated endpoints
270
+ this.token = "";
271
+ this.expiration = 0;
272
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
273
+ this.chatSDK.authenticatedUserToken = null;
274
+ this.pendingMidAuthUnauthenticatedState = true;
275
+ _TelemetryHelper.TelemetryHelper.logFacadeChatSDKEventToAllTelemetry(_TelemetryConstants.LogLevel.INFO, {
276
+ Event: _TelemetryConstants.TelemetryEvent.NewTokenValidationCompleted,
277
+ Description: "Mid-auth enabled: no token returned; proceeding as unauthenticated"
257
278
  });
258
279
  return {
259
- result: false,
260
- message: (ring === null || ring === void 0 ? void 0 : (_ring$error2 = ring.error) === null || _ring$error2 === void 0 ? void 0 : _ring$error2.message) || "Failed to get token"
280
+ result: true,
281
+ message: "Mid-auth: proceeding as unauthenticated"
261
282
  };
262
283
  }
284
+ _TelemetryHelper.TelemetryHelper.logFacadeChatSDKEventToAllTelemetry(_TelemetryConstants.LogLevel.ERROR, {
285
+ Event: _TelemetryConstants.TelemetryEvent.NewTokenValidationFailed,
286
+ Description: (_ring$error = ring.error) === null || _ring$error === void 0 ? void 0 : _ring$error.message,
287
+ ExceptionDetails: ring === null || ring === void 0 ? void 0 : ring.error
288
+ });
289
+ return {
290
+ result: false,
291
+ message: (ring === null || ring === void 0 ? void 0 : (_ring$error2 = ring.error) === null || _ring$error2 === void 0 ? void 0 : _ring$error2.message) || "Failed to get token"
292
+ };
263
293
  } catch (e) {
264
294
  console.error("Unexpected error while getting token", e);
265
295
  _TelemetryHelper.TelemetryHelper.logFacadeChatSDKEventToAllTelemetry(_TelemetryConstants.LogLevel.ERROR, {
@@ -273,6 +303,155 @@ let FacadeChatSDK = /*#__PURE__*/function () {
273
303
  };
274
304
  }
275
305
  }
306
+
307
+ /**
308
+ * Sets unauthenticated state for mid-auth flow.
309
+ * Clears SDK internal state to prevent reconnection to previous authenticated session.
310
+ */
311
+ }, {
312
+ key: "setMidAuthUnauthenticatedState",
313
+ value: function setMidAuthUnauthenticatedState() {
314
+ var _sdk$chatToken, _sdk$chatToken2;
315
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
316
+ const sdk = this.chatSDK;
317
+ const hadExistingChat = !!((_sdk$chatToken = sdk.chatToken) !== null && _sdk$chatToken !== void 0 && _sdk$chatToken.chatId);
318
+ const previousChatId = (_sdk$chatToken2 = sdk.chatToken) === null || _sdk$chatToken2 === void 0 ? void 0 : _sdk$chatToken2.chatId;
319
+ this.clearAuthState();
320
+
321
+ // Clear SDK internal state for fresh unauthenticated chat
322
+ sdk.chatToken = {};
323
+ sdk.reconnectId = null;
324
+ sdk.requestId = null;
325
+ sdk.sessionId = null;
326
+ sdk.conversation = null;
327
+ _TelemetryHelper.TelemetryHelper.logFacadeChatSDKEventToAllTelemetry(_TelemetryConstants.LogLevel.INFO, {
328
+ Event: _TelemetryConstants.TelemetryEvent.MidConversationAuthReset,
329
+ Description: hadExistingChat ? "Mid-auth without token: local state cleared" : "Mid-auth: initialized as unauthenticated (no prior chat)",
330
+ Data: hadExistingChat ? {
331
+ previousChatId
332
+ } : undefined
333
+ });
334
+ }
335
+
336
+ /** Clears authentication state in both FacadeChatSDK and underlying SDK */
337
+ }, {
338
+ key: "clearAuthState",
339
+ value: function clearAuthState() {
340
+ this.token = "";
341
+ this.expiration = 0;
342
+ this.isAuthenticated = false;
343
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
344
+ this.chatSDK.authenticatedUserToken = null;
345
+ }
346
+
347
+ /**
348
+ * Migrates conversation from unauthenticated to authenticated via authenticateChat.
349
+ * Called after startChat() when user has a valid token but the backend conversation
350
+ * was started as unauthenticated.
351
+ */
352
+ }, {
353
+ key: "migrateConversationToAuthenticated",
354
+ value: async function migrateConversationToAuthenticated() {
355
+ try {
356
+ await this.chatSDK.authenticateChat(this.token, {
357
+ refreshChatToken: true
358
+ });
359
+ this.isAuthenticated = true;
360
+ _TelemetryHelper.TelemetryHelper.logFacadeChatSDKEventToAllTelemetry(_TelemetryConstants.LogLevel.INFO, {
361
+ Event: _TelemetryConstants.TelemetryEvent.MidConversationAuthSucceeded,
362
+ Description: "Mid-auth: authenticateChat completed, conversation migrated to authenticated"
363
+ });
364
+ } catch (e) {
365
+ // Non-fatal: Chat is already active via startChat, will retry on next reconnect
366
+ _TelemetryHelper.TelemetryHelper.logFacadeChatSDKEventToAllTelemetry(_TelemetryConstants.LogLevel.WARN, {
367
+ Event: _TelemetryConstants.TelemetryEvent.MidConversationAuthFailed,
368
+ Description: "Mid-auth: authenticateChat returned error after startChat, chat still active",
369
+ ExceptionDetails: {
370
+ message: e === null || e === void 0 ? void 0 : e.message
371
+ }
372
+ });
373
+ }
374
+ }
375
+
376
+ /**
377
+ * Configures SDK auth state before startChat.
378
+ * CASE 1: Pending unauthenticated (no token) - sets deferInitialAuth=true
379
+ * CASE 2: Authenticated with valid token - sets SDK token and deferInitialAuth based on scenario
380
+ */
381
+ }, {
382
+ key: "configureMidAuthState",
383
+ value: function configureMidAuthState(isReconnect, wasPreviousSessionAuthenticated) {
384
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
385
+ const sdk = this.chatSDK;
386
+
387
+ // CASE 1: No token available (user not logged in)
388
+ // pendingMidAuthUnauthenticatedState stays true until user logs in (cleared in tokenRing)
389
+ if (this.pendingMidAuthUnauthenticatedState) {
390
+ const shouldClear = this.handlePendingUnauthenticatedState(wasPreviousSessionAuthenticated);
391
+ sdk.deferInitialAuth = true;
392
+ _TelemetryHelper.TelemetryHelper.logFacadeChatSDKEventToAllTelemetry(_TelemetryConstants.LogLevel.INFO, {
393
+ Event: _TelemetryConstants.TelemetryEvent.MidConversationAuthReset,
394
+ Description: "Mid-auth configureMidAuthState: CASE 1 - unauthenticated, deferInitialAuth=true",
395
+ Data: {
396
+ isReconnect: String(isReconnect),
397
+ wasPreviousSessionAuthenticated: String(wasPreviousSessionAuthenticated),
398
+ shouldClearReconnectParams: String(shouldClear)
399
+ }
400
+ });
401
+ return {
402
+ shouldClearReconnectParams: shouldClear
403
+ };
404
+ }
405
+
406
+ // CASE 2: Authenticated with valid token
407
+ if (this.isTokenSet() && !this.isTokenExpired()) {
408
+ this.handleAuthenticatedState(isReconnect, wasPreviousSessionAuthenticated);
409
+ }
410
+ return {
411
+ shouldClearReconnectParams: false
412
+ };
413
+ }
414
+
415
+ /**
416
+ * CASE 1 handler: Returns true if reconnect params should be cleared (Auth -> Unauth transition)
417
+ */
418
+ }, {
419
+ key: "handlePendingUnauthenticatedState",
420
+ value: function handlePendingUnauthenticatedState(wasPreviousSessionAuthenticated) {
421
+ if (wasPreviousSessionAuthenticated) {
422
+ // Auth -> Unauth: user logged out, clear state for fresh chat
423
+ this.setMidAuthUnauthenticatedState();
424
+ return true;
425
+ }
426
+
427
+ // Unauth -> Unauth: keep liveChatContext for reconnection
428
+ this.isAuthenticated = false;
429
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
430
+ this.chatSDK.authenticatedUserToken = null;
431
+ return false;
432
+ }
433
+
434
+ /**
435
+ * CASE 2 handler: Sets deferInitialAuth only for reconnects to unauthenticated sessions (need migration).
436
+ * For new chats or reconnects to authenticated sessions, SDK handles auth internally.
437
+ */
438
+ }, {
439
+ key: "handleAuthenticatedState",
440
+ value: function handleAuthenticatedState(isReconnect, wasPreviousSessionAuthenticated) {
441
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
442
+ const sdk = this.chatSDK;
443
+ sdk.authenticatedUserToken = this.token;
444
+ if (isReconnect && !wasPreviousSessionAuthenticated) {
445
+ sdk.deferInitialAuth = true;
446
+ _TelemetryHelper.TelemetryHelper.logFacadeChatSDKEventToAllTelemetry(_TelemetryConstants.LogLevel.INFO, {
447
+ Event: _TelemetryConstants.TelemetryEvent.MidConversationAuthSucceeded,
448
+ Description: "Mid-auth handleAuthenticatedState: CASE 2 - reconnect to unauth session, deferInitialAuth=true (migration needed)"
449
+ });
450
+ } else {
451
+ // Reset to prevent inheriting deferInitialAuth=true from a previous unauthenticated chat
452
+ sdk.deferInitialAuth = false;
453
+ }
454
+ }
276
455
  }, {
277
456
  key: "validateAndExecuteCall",
278
457
  value: async function validateAndExecuteCall(functionName, fn) {
@@ -307,7 +486,53 @@ let FacadeChatSDK = /*#__PURE__*/function () {
307
486
  key: "startChat",
308
487
  value: async function startChat() {
309
488
  let optionalParams = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
310
- return this.validateAndExecuteCall("startChat", () => this.chatSDK.startChat(optionalParams));
489
+ const midAuthEnabled = (0, _authHelper.isMidAuthEnabled)(this.chatConfig);
490
+ const isReconnect = !!optionalParams.liveChatContext || !!optionalParams.reconnectId;
491
+ const wasPreviousSessionAuthenticated = optionalParams.wasAuthenticated === true;
492
+ return this.validateAndExecuteCall("startChat", async () => {
493
+ if (midAuthEnabled) {
494
+ const {
495
+ shouldClearReconnectParams
496
+ } = this.configureMidAuthState(isReconnect, wasPreviousSessionAuthenticated);
497
+ if (shouldClearReconnectParams) {
498
+ delete optionalParams.liveChatContext;
499
+ delete optionalParams.reconnectId;
500
+ }
501
+ }
502
+ await this.chatSDK.startChat(optionalParams);
503
+
504
+ // Migrate to authenticated if needed (reconnects to unauthenticated sessions only)
505
+ if (midAuthEnabled) {
506
+ const shouldMigrateToAuth = isReconnect && this.isTokenSet() && !this.isTokenExpired() && !wasPreviousSessionAuthenticated;
507
+ if (shouldMigrateToAuth) {
508
+ _TelemetryHelper.TelemetryHelper.logFacadeChatSDKEventToAllTelemetry(_TelemetryConstants.LogLevel.INFO, {
509
+ Event: _TelemetryConstants.TelemetryEvent.MidConversationAuthSucceeded,
510
+ Description: "Mid-auth startChat: initiating migration to authenticated",
511
+ Data: {
512
+ isReconnect: String(isReconnect),
513
+ wasPreviousSessionAuthenticated: String(wasPreviousSessionAuthenticated)
514
+ }
515
+ });
516
+ await this.migrateConversationToAuthenticated();
517
+ }
518
+ }
519
+
520
+ // Broadcast final auth state after startChat completes (only on state change)
521
+ if (midAuthEnabled) {
522
+ const isAuthenticatedAfterStart = this.isTokenSet() && !this.isTokenExpired();
523
+ const authStateChanged = !isReconnect || isAuthenticatedAfterStart !== wasPreviousSessionAuthenticated;
524
+ if (authStateChanged) {
525
+ _omnichannelChatComponents.BroadcastService.postMessage({
526
+ eventName: isAuthenticatedAfterStart ? _TelemetryConstants.BroadcastEvent.MidConversationAuthSucceeded : _TelemetryConstants.BroadcastEvent.MidConversationAuthReset,
527
+ payload: {
528
+ isAuthenticated: isAuthenticatedAfterStart,
529
+ isStartChatComplete: true,
530
+ isReconnect
531
+ }
532
+ });
533
+ }
534
+ }
535
+ });
311
536
  }
312
537
  }, {
313
538
  key: "endChat",
@@ -445,6 +670,7 @@ let FacadeChatSDK = /*#__PURE__*/function () {
445
670
  let optionalParams = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
446
671
  return this.validateAndExecuteCall("getAgentAvailability", () => this.chatSDK.getAgentAvailability(optionalParams));
447
672
  }
673
+
448
674
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
449
675
  }, {
450
676
  key: "getReconnectableChats",
@@ -74,6 +74,8 @@ exports.BroadcastEvent = BroadcastEvent;
74
74
  BroadcastEvent["FMLTrackingCompletedAck"] = "FMLTrackingCompletedAck";
75
75
  BroadcastEvent["FMLTrackingCompleted"] = "FMLTrackingCompleted";
76
76
  BroadcastEvent["PersistentConversationReset"] = "PersistentConversationReset";
77
+ BroadcastEvent["MidConversationAuthSucceeded"] = "MidConversationAuthSucceeded";
78
+ BroadcastEvent["MidConversationAuthReset"] = "MidConversationAuthReset";
77
79
  })(BroadcastEvent || (exports.BroadcastEvent = BroadcastEvent = {}));
78
80
  let TelemetryEvent;
79
81
  exports.TelemetryEvent = TelemetryEvent;
@@ -108,6 +110,9 @@ exports.TelemetryEvent = TelemetryEvent;
108
110
  TelemetryEvent["CallingSDKInitFailed"] = "CallingSDKInitFailed";
109
111
  TelemetryEvent["CallingSDKLoadSuccess"] = "CallingSDKLoadSuccess";
110
112
  TelemetryEvent["CallingSDKLoadFailed"] = "CallingSDKLoadFailed";
113
+ TelemetryEvent["MidConversationAuthSucceeded"] = "MidConversationAuthSucceeded";
114
+ TelemetryEvent["MidConversationAuthFailed"] = "MidConversationAuthFailed";
115
+ TelemetryEvent["MidConversationAuthReset"] = "MidConversationAuthReset";
111
116
  TelemetryEvent["GetConversationDetailsCallStarted"] = "GetConversationDetailsCallStarted";
112
117
  TelemetryEvent["GetConversationDetailsCallFailed"] = "GetConversationDetailsCallFailed";
113
118
  TelemetryEvent["EndChatSDKCallFailed"] = "EndChatSDKCallFailed";
@@ -197,6 +202,7 @@ exports.TelemetryEvent = TelemetryEvent;
197
202
  TelemetryEvent["QueueOverflowEvent"] = "QueueOverflowEvent";
198
203
  TelemetryEvent["ProcessingHTMLTextMiddlewareFailed"] = "ProcessingHTMLTextMiddlewareFailed";
199
204
  TelemetryEvent["ProcessingSanitizationMiddlewareFailed"] = "ProcessingSanitizationMiddlewareFailed";
205
+ TelemetryEvent["HTMLSanitized"] = "HTMLSanitized";
200
206
  TelemetryEvent["FormatTagsMiddlewareJSONStringifyFailed"] = "FormatTagsMiddlewareJSONStringifyFailed";
201
207
  TelemetryEvent["AttachmentUploadValidatorMiddlewareFailed"] = "AttachmentUploadValidatorMiddlewareFailed";
202
208
  TelemetryEvent["CitationMiddlewareFailed"] = "CitationMiddlewareFailed";
@@ -326,6 +332,8 @@ exports.TelemetryEvent = TelemetryEvent;
326
332
  TelemetryEvent["SecureEventBusListenerError"] = "SecureEventBusListenerError";
327
333
  TelemetryEvent["SecureEventBusDispatchError"] = "SecureEventBusDispatchError";
328
334
  TelemetryEvent["StartChatComplete"] = "StartChatComplete";
335
+ TelemetryEvent["AgentJoinedConversation"] = "AgentJoinedConversation";
336
+ TelemetryEvent["BrowserTabHidden"] = "BrowserTabHidden";
329
337
  })(TelemetryEvent || (exports.TelemetryEvent = TelemetryEvent = {}));
330
338
  let TelemetryConstants = /*#__PURE__*/function () {
331
339
  function TelemetryConstants() {
@@ -396,6 +404,8 @@ let TelemetryConstants = /*#__PURE__*/function () {
396
404
  case TelemetryEvent.SecureEventBusUnauthorizedDispatch:
397
405
  case TelemetryEvent.SecureEventBusListenerError:
398
406
  case TelemetryEvent.SecureEventBusDispatchError:
407
+ case TelemetryEvent.AgentJoinedConversation:
408
+ case TelemetryEvent.BrowserTabHidden:
399
409
  return ScenarioType.ACTIONS;
400
410
  case TelemetryEvent.StartChatSDKCall:
401
411
  case TelemetryEvent.StartChatEventReceived:
@@ -3,13 +3,13 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
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;
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.announceMessageImmediately = 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
10
  exports.parseAdaptiveCardPayload = exports.newGuid = exports.isValidCustomEvent = exports.isUndefinedOrEmpty = exports.isThisSessionPopout = exports.isNullOrUndefined = exports.isNullOrEmptyString = void 0;
11
11
  exports.parseBooleanFromConfig = parseBooleanFromConfig;
12
- exports.setTabIndices = exports.setOcUserAgent = exports.setFocusOnSendBox = exports.setFocusOnElement = exports.preventFocusToMoveOutOfElement = exports.parseLowerCaseString = void 0;
12
+ exports.setTabIndices = exports.setOcUserAgent = exports.setFocusOnSendBox = exports.setFocusOnElement = exports.setAriaHiddenForSiblings = exports.preventFocusToMoveOutOfElement = exports.parseLowerCaseString = void 0;
13
13
  var _Constants = require("./Constants");
14
14
  var _TelemetryConstants = require("./telemetry/TelemetryConstants");
15
15
  var _omnichannelChatComponents = require("@microsoft/omnichannel-chat-components");
@@ -85,28 +85,69 @@ exports.findAllFocusableElement = findAllFocusableElement;
85
85
  const preventFocusToMoveOutOfElement = elementId => {
86
86
  const container = document.getElementById(elementId);
87
87
  if (!container) {
88
- return;
88
+ return () => {/* no-op */};
89
89
  }
90
90
  const focusableElements = findAllFocusableElement(container);
91
91
  if (!focusableElements) {
92
- return;
92
+ return () => {/* no-op */};
93
93
  }
94
94
  const firstFocusableElement = focusableElements[0];
95
95
  const lastFocusableElement = focusableElements[focusableElements.length - 1];
96
- firstFocusableElement.onkeydown = e => {
97
- if (e.shiftKey && e.key === _KeyCodes.KeyCodes.TAB) {
98
- e.preventDefault();
99
- lastFocusableElement === null || lastFocusableElement === void 0 ? void 0 : lastFocusableElement.focus();
100
- }
101
- };
102
- lastFocusableElement.onkeydown = e => {
103
- if (!e.shiftKey && e.key === _KeyCodes.KeyCodes.TAB) {
104
- e.preventDefault();
105
- firstFocusableElement === null || firstFocusableElement === void 0 ? void 0 : firstFocusableElement.focus();
106
- }
107
- };
96
+ const cleanups = [];
97
+ if (firstFocusableElement === lastFocusableElement) {
98
+ const handler = e => {
99
+ if (e.key === _KeyCodes.KeyCodes.TAB && !e.shiftKey) {
100
+ e.preventDefault();
101
+ firstFocusableElement.focus();
102
+ } else if (e.key === _KeyCodes.KeyCodes.TAB && e.shiftKey) {
103
+ e.preventDefault();
104
+ firstFocusableElement.focus();
105
+ }
106
+ };
107
+ firstFocusableElement.addEventListener("keydown", handler);
108
+ cleanups.push(() => firstFocusableElement.removeEventListener("keydown", handler));
109
+ } else {
110
+ const firstHandler = e => {
111
+ if (e.shiftKey && e.key === _KeyCodes.KeyCodes.TAB) {
112
+ e.preventDefault();
113
+ lastFocusableElement === null || lastFocusableElement === void 0 ? void 0 : lastFocusableElement.focus();
114
+ }
115
+ };
116
+ firstFocusableElement.addEventListener("keydown", firstHandler);
117
+ cleanups.push(() => firstFocusableElement.removeEventListener("keydown", firstHandler));
118
+ const lastHandler = e => {
119
+ if (!e.shiftKey && e.key === _KeyCodes.KeyCodes.TAB) {
120
+ e.preventDefault();
121
+ firstFocusableElement === null || firstFocusableElement === void 0 ? void 0 : firstFocusableElement.focus();
122
+ }
123
+ };
124
+ lastFocusableElement.addEventListener("keydown", lastHandler);
125
+ cleanups.push(() => lastFocusableElement.removeEventListener("keydown", lastHandler));
126
+ }
127
+ return () => cleanups.forEach(fn => fn());
108
128
  };
109
129
  exports.preventFocusToMoveOutOfElement = preventFocusToMoveOutOfElement;
130
+ const setAriaHiddenForSiblings = (elementId, shouldHide, stateMap) => {
131
+ const element = document.getElementById(elementId);
132
+ if (!(element !== null && element !== void 0 && element.parentElement)) return;
133
+ Array.from(element.parentElement.children).forEach(sibling => {
134
+ if (sibling !== element) {
135
+ if (shouldHide) {
136
+ stateMap.set(sibling, sibling.getAttribute("aria-hidden"));
137
+ sibling.setAttribute("aria-hidden", "true");
138
+ } else if (stateMap.has(sibling)) {
139
+ const original = stateMap.get(sibling);
140
+ if (original === null) {
141
+ sibling.removeAttribute("aria-hidden");
142
+ } else {
143
+ sibling.setAttribute("aria-hidden", original);
144
+ }
145
+ stateMap.delete(sibling);
146
+ }
147
+ }
148
+ });
149
+ };
150
+ exports.setAriaHiddenForSiblings = setAriaHiddenForSiblings;
110
151
  const setFocusOnSendBox = () => {
111
152
  const sendBoxSelector = "textarea[data-id=\"webchat-sendbox-input\"]";
112
153
  setFocusOnElement(sendBoxSelector);
@@ -117,6 +158,41 @@ const setFocusOnElement = selector => {
117
158
  element === null || element === void 0 ? void 0 : element.focus();
118
159
  };
119
160
  exports.setFocusOnElement = setFocusOnElement;
161
+ const IMMEDIATE_ANNOUNCEMENT_REGION_ID = "oc-lcw-immediate-announcement";
162
+
163
+ // Announces a message to screen readers via an aria-live="assertive" region
164
+ // attached to document.body — outside the chat widget's DOM subtree — so the
165
+ // screen reader does not have to traverse chat content to reach it.
166
+ const announceMessageImmediately = message => {
167
+ if (!message || typeof document === "undefined") {
168
+ return;
169
+ }
170
+ let region = document.getElementById(IMMEDIATE_ANNOUNCEMENT_REGION_ID);
171
+ if (!region) {
172
+ region = document.createElement("div");
173
+ region.id = IMMEDIATE_ANNOUNCEMENT_REGION_ID;
174
+ region.setAttribute("aria-live", "assertive");
175
+ region.setAttribute("role", "alert");
176
+ region.setAttribute("aria-atomic", "true");
177
+ region.style.position = "absolute";
178
+ region.style.width = "1px";
179
+ region.style.height = "1px";
180
+ region.style.overflow = "hidden";
181
+ region.style.clip = "rect(0 0 0 0)";
182
+ region.style.clipPath = "inset(50%)";
183
+ region.style.whiteSpace = "nowrap";
184
+ document.body.appendChild(region);
185
+ }
186
+ region.textContent = "";
187
+ // Re-set on the next tick so screen readers detect the change even when
188
+ // the same message is announced twice in a row.
189
+ setTimeout(() => {
190
+ if (region) {
191
+ region.textContent = message;
192
+ }
193
+ }, 50);
194
+ };
195
+ exports.announceMessageImmediately = announceMessageImmediately;
120
196
  const escapeHtml = inputString => {
121
197
  const entityMap = {
122
198
  "<": "&lt;",
@@ -44,7 +44,7 @@ const CitationPaneStateful = props => {
44
44
 
45
45
  // Initial focus pattern (mirrors ConfirmationPaneStateful): focus first focusable element (will re-attempt after visibility becomes true)
46
46
  (0, _react.useEffect)(() => {
47
- (0, _utils.preventFocusToMoveOutOfElement)(controlId);
47
+ const cleanup = (0, _utils.preventFocusToMoveOutOfElement)(controlId);
48
48
  const focusableElements = (0, _utils.findAllFocusableElement)(`#${controlId}`);
49
49
  requestAnimationFrame(() => {
50
50
  if (focusableElements && focusableElements.length > 0 && focusableElements[0]) {
@@ -62,6 +62,7 @@ const CitationPaneStateful = props => {
62
62
  Event: _TelemetryConstants.TelemetryEvent.UXCitationPaneCompleted,
63
63
  ElapsedTimeInMilliseconds: uiTimer.milliSecondsElapsed
64
64
  });
65
+ return cleanup;
65
66
  }, []);
66
67
 
67
68
  // Retry focus once pane is actually visible (isReady) in case initial attempt occurred while wrapper was visibility:hidden
@@ -82,7 +82,7 @@ const ConfirmationPaneStateful = props => {
82
82
 
83
83
  // Move focus to the first button
84
84
  (0, _react.useEffect)(() => {
85
- (0, _utils.preventFocusToMoveOutOfElement)(controlProps.id);
85
+ const cleanup = (0, _utils.preventFocusToMoveOutOfElement)(controlProps.id);
86
86
  const focusableElements = (0, _utils.findAllFocusableElement)(`#${controlProps.id}`);
87
87
  requestAnimationFrame(() => {
88
88
  if (focusableElements && focusableElements.length > 0 && focusableElements[0]) {
@@ -100,6 +100,7 @@ const ConfirmationPaneStateful = props => {
100
100
  Event: _TelemetryConstants.TelemetryEvent.UXConfirmationPaneCompleted,
101
101
  ElapsedTimeInMilliseconds: uiTimer.milliSecondsElapsed
102
102
  });
103
+ return cleanup;
103
104
  }, []);
104
105
  return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_DimLayer.DimLayer, {
105
106
  brightness: (controlProps === null || controlProps === void 0 ? void 0 : controlProps.brightnessValueOnDim) ?? "0.2"
@@ -35,16 +35,23 @@ const EmailTranscriptPaneStateful = props => {
35
35
  const [state, dispatch] = (0, _useChatContextStore.default)();
36
36
  const [facadeChatSDK] = (0, _useFacadeChatSDKStore.default)();
37
37
  const [initialEmail, setInitialEmail] = (0, _react.useState)("");
38
- const closeEmailTranscriptPane = () => {
38
+ // restoreFocus=false is used on the submit path: the notification banner
39
+ // (success or error) takes focus via NotificationHandler.setFocusOnNotificationCloseButton,
40
+ // so an intermediate restore to the chat-widget shell would otherwise cause SRs to
41
+ // announce "Enter <widget>" in between the dialog and the banner.
42
+ const closeEmailTranscriptPane = function () {
43
+ let restoreFocus = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
39
44
  dispatch({
40
45
  type: _LiveChatWidgetActionType.LiveChatWidgetActionType.SET_SHOW_EMAIL_TRANSCRIPT_PANE,
41
46
  payload: false
42
47
  });
43
- const previousFocusedElementId = state.appStates.previousElementIdOnFocusBeforeModalOpen;
44
- if (previousFocusedElementId) {
45
- (0, _utils.setFocusOnElement)("#" + previousFocusedElementId);
46
- } else {
47
- (0, _utils.setFocusOnSendBox)();
48
+ if (restoreFocus) {
49
+ const previousFocusedElementId = state.appStates.previousElementIdOnFocusBeforeModalOpen;
50
+ if (previousFocusedElementId) {
51
+ (0, _utils.setFocusOnElement)("#" + previousFocusedElementId);
52
+ } else {
53
+ (0, _utils.setFocusOnSendBox)();
54
+ }
48
55
  }
49
56
  dispatch({
50
57
  type: _LiveChatWidgetActionType.LiveChatWidgetActionType.SET_PREVIOUS_FOCUSED_ELEMENT_ID,
@@ -55,23 +62,31 @@ const EmailTranscriptPaneStateful = props => {
55
62
  const onSend = (0, _react.useCallback)(async email => {
56
63
  var _state$domainStates;
57
64
  const liveChatContext = state === null || state === void 0 ? void 0 : (_state$domainStates = state.domainStates) === null || _state$domainStates === void 0 ? void 0 : _state$domainStates.liveChatContext;
58
- closeEmailTranscriptPane();
65
+ closeEmailTranscriptPane(false);
59
66
  const chatTranscriptBody = {
60
67
  emailAddress: email,
61
68
  attachmentMessage: (props === null || props === void 0 ? void 0 : props.attachmentMessage) ?? "The following attachment was uploaded during the conversation:"
62
69
  };
63
70
  try {
64
- var _state$domainStates2, _state$domainStates2$;
71
+ var _state$domainStates2, _state$domainStates2$, _state$domainStates3, _state$domainStates3$;
65
72
  await (facadeChatSDK === null || facadeChatSDK === void 0 ? void 0 : facadeChatSDK.emailLiveChatTranscript(chatTranscriptBody, {
66
73
  liveChatContext
67
74
  }));
68
- _NotificationHandler.NotificationHandler.notifySuccess(_NotificationScenarios.NotificationScenarios.EmailAddressSaved, (state === null || state === void 0 ? void 0 : (_state$domainStates2 = state.domainStates) === null || _state$domainStates2 === void 0 ? void 0 : (_state$domainStates2$ = _state$domainStates2.middlewareLocalizedTexts) === null || _state$domainStates2$ === void 0 ? void 0 : _state$domainStates2$.MIDDLEWARE_BANNER_FILE_EMAIL_ADDRESS_RECORDED_SUCCESS) ?? (_defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts === null || _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts === void 0 ? void 0 : _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts.MIDDLEWARE_BANNER_FILE_EMAIL_ADDRESS_RECORDED_SUCCESS));
75
+ const successMessage = (state === null || state === void 0 ? void 0 : (_state$domainStates2 = state.domainStates) === null || _state$domainStates2 === void 0 ? void 0 : (_state$domainStates2$ = _state$domainStates2.middlewareLocalizedTexts) === null || _state$domainStates2$ === void 0 ? void 0 : _state$domainStates2$.MIDDLEWARE_BANNER_FILE_EMAIL_ADDRESS_RECORDED_SUCCESS) ?? (_defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts === null || _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts === void 0 ? void 0 : _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts.MIDDLEWARE_BANNER_FILE_EMAIL_ADDRESS_RECORDED_SUCCESS);
76
+ // Announce from a live region at document.body level so the
77
+ // screen reader speaks the confirmation immediately, without
78
+ // traversing the chat transcript on the way to the banner.
79
+ // Prefix with the explicit state word so SR users hear the outcome
80
+ // (visual users already see a success icon on the banner).
81
+ const successSrPrefix = (state === null || state === void 0 ? void 0 : (_state$domainStates3 = state.domainStates) === null || _state$domainStates3 === void 0 ? void 0 : (_state$domainStates3$ = _state$domainStates3.middlewareLocalizedTexts) === null || _state$domainStates3$ === void 0 ? void 0 : _state$domainStates3$.MIDDLEWARE_SR_PREFIX_SUCCESS) ?? _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts.MIDDLEWARE_SR_PREFIX_SUCCESS;
82
+ (0, _utils.announceMessageImmediately)(`${successSrPrefix}${successMessage}`);
83
+ _NotificationHandler.NotificationHandler.notifySuccess(_NotificationScenarios.NotificationScenarios.EmailAddressSaved, successMessage);
69
84
  _TelemetryHelper.TelemetryHelper.logActionEventToAllTelemetry(_TelemetryConstants.LogLevel.INFO, {
70
85
  Event: _TelemetryConstants.TelemetryEvent.EmailTranscriptSent,
71
86
  Description: "Transcript sent to email successfully."
72
87
  });
73
88
  } catch (ex) {
74
- var _state$domainStates3, _state$domainStates3$;
89
+ var _state$domainStates4, _state$domainStates4$, _state$domainStates5, _state$domainStates5$;
75
90
  _TelemetryHelper.TelemetryHelper.logActionEventToAllTelemetry(_TelemetryConstants.LogLevel.ERROR, {
76
91
  Event: _TelemetryConstants.TelemetryEvent.EmailTranscriptFailed,
77
92
  Description: "Email transcript failed.",
@@ -79,8 +94,11 @@ const EmailTranscriptPaneStateful = props => {
79
94
  exception: ex
80
95
  }
81
96
  });
82
- const message = (0, _utils.formatTemplateString)((state === null || state === void 0 ? void 0 : (_state$domainStates3 = state.domainStates) === null || _state$domainStates3 === void 0 ? void 0 : (_state$domainStates3$ = _state$domainStates3.middlewareLocalizedTexts) === null || _state$domainStates3$ === void 0 ? void 0 : _state$domainStates3$.MIDDLEWARE_BANNER_FILE_EMAIL_ADDRESS_RECORDED_ERROR) ?? _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts.MIDDLEWARE_BANNER_FILE_EMAIL_ADDRESS_RECORDED_ERROR, [email]);
83
- _NotificationHandler.NotificationHandler.notifyError(_NotificationScenarios.NotificationScenarios.EmailTranscriptError, (props === null || props === void 0 ? void 0 : props.bannerMessageOnError) ?? message);
97
+ const message = (0, _utils.formatTemplateString)((state === null || state === void 0 ? void 0 : (_state$domainStates4 = state.domainStates) === null || _state$domainStates4 === void 0 ? void 0 : (_state$domainStates4$ = _state$domainStates4.middlewareLocalizedTexts) === null || _state$domainStates4$ === void 0 ? void 0 : _state$domainStates4$.MIDDLEWARE_BANNER_FILE_EMAIL_ADDRESS_RECORDED_ERROR) ?? _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts.MIDDLEWARE_BANNER_FILE_EMAIL_ADDRESS_RECORDED_ERROR, [email]);
98
+ const bannerMessage = (props === null || props === void 0 ? void 0 : props.bannerMessageOnError) ?? message;
99
+ const errorSrPrefix = (state === null || state === void 0 ? void 0 : (_state$domainStates5 = state.domainStates) === null || _state$domainStates5 === void 0 ? void 0 : (_state$domainStates5$ = _state$domainStates5.middlewareLocalizedTexts) === null || _state$domainStates5$ === void 0 ? void 0 : _state$domainStates5$.MIDDLEWARE_SR_PREFIX_ERROR) ?? _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts.MIDDLEWARE_SR_PREFIX_ERROR;
100
+ (0, _utils.announceMessageImmediately)(`${errorSrPrefix}${bannerMessage}`);
101
+ _NotificationHandler.NotificationHandler.notifyError(_NotificationScenarios.NotificationScenarios.EmailTranscriptError, bannerMessage);
84
102
  }
85
103
  }, [props.attachmentMessage, props.bannerMessageOnError, facadeChatSDK, state.domainStates.liveChatContext]);
86
104
  const controlProps = {
@@ -103,7 +121,7 @@ const EmailTranscriptPaneStateful = props => {
103
121
 
104
122
  // Move focus to the first button
105
123
  (0, _react.useEffect)(() => {
106
- (0, _utils.preventFocusToMoveOutOfElement)(controlProps.id);
124
+ const cleanup = (0, _utils.preventFocusToMoveOutOfElement)(controlProps.id);
107
125
  const focusableElements = (0, _utils.findAllFocusableElement)(`#${controlProps.id}`);
108
126
  if (focusableElements) {
109
127
  focusableElements[0].focus();
@@ -118,6 +136,7 @@ const EmailTranscriptPaneStateful = props => {
118
136
  Event: _TelemetryConstants.TelemetryEvent.UXEmailTranscriptPaneCompleted,
119
137
  ElapsedTimeInMilliseconds: uiTimer.milliSecondsElapsed
120
138
  });
139
+ return cleanup;
121
140
  }, [initialEmail]);
122
141
  return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_DimLayer.DimLayer, {
123
142
  brightness: (controlProps === null || controlProps === void 0 ? void 0 : controlProps.brightnessValueOnDim) ?? "0.2"