@microsoft/omnichannel-chat-widget 1.8.4-main.cbab5fc → 1.8.4-main.cd79f08

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 (81) hide show
  1. package/README.md +12 -3
  2. package/lib/cjs/common/Constants.js +2 -0
  3. package/lib/cjs/common/facades/FacadeChatSDK.js +235 -9
  4. package/lib/cjs/common/telemetry/TelemetryConstants.js +13 -0
  5. package/lib/cjs/common/telemetry/loggers/appInsightsLogger.js +7 -7
  6. package/lib/cjs/common/utils/xssUtils.js +23 -51
  7. package/lib/cjs/common/utils.js +15 -2
  8. package/lib/cjs/components/errorboundary/ErrorBoundary.js +2 -1
  9. package/lib/cjs/components/livechatwidget/LiveChatWidget.js +9 -1
  10. package/lib/cjs/components/livechatwidget/common/ActivitySubscriber/BotAuthActivitySubscriber.js +23 -6
  11. package/lib/cjs/components/livechatwidget/common/ChatWidgetEvents.js +1 -0
  12. package/lib/cjs/components/livechatwidget/common/PersistentConversationHandler.js +9 -0
  13. package/lib/cjs/components/livechatwidget/common/authHelper.js +44 -4
  14. package/lib/cjs/components/livechatwidget/common/createAdapter.js +3 -2
  15. package/lib/cjs/components/livechatwidget/common/liveChatConfigUtils.js +36 -4
  16. package/lib/cjs/components/livechatwidget/common/startChat.js +31 -14
  17. package/lib/cjs/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +62 -2
  18. package/lib/cjs/components/postchatsurveypanestateful/PostChatSurveyPaneStateful.js +6 -4
  19. package/lib/cjs/components/proactivechatpanestateful/ProactiveChatPaneStateful.js +0 -1
  20. package/lib/cjs/components/webchatcontainerstateful/WebChatContainerStateful.js +42 -27
  21. package/lib/cjs/components/webchatcontainerstateful/common/activityConverters/convertPersistentChatHistoryMessageToActivity.js +117 -16
  22. package/lib/cjs/components/webchatcontainerstateful/common/defaultStyles/defaultAdaptiveCardStyles.js +3 -1
  23. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/WebChatEventSubscribers.js +13 -2
  24. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/LazyLoadActivity.js +160 -167
  25. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.js +1 -1
  26. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/NotDeliveredTimestamp.js +2 -0
  27. package/lib/cjs/contexts/common/LiveChatWidgetActionType.js +1 -0
  28. package/lib/cjs/contexts/common/LiveChatWidgetContextInitialState.js +6 -1
  29. package/lib/cjs/contexts/createReducer.js +15 -0
  30. package/lib/cjs/controller/componentController.js +13 -1
  31. package/lib/cjs/plugins/newMessageEventHandler.js +20 -3
  32. package/lib/esm/common/Constants.js +2 -0
  33. package/lib/esm/common/facades/FacadeChatSDK.js +236 -10
  34. package/lib/esm/common/telemetry/TelemetryConstants.js +13 -0
  35. package/lib/esm/common/telemetry/loggers/appInsightsLogger.js +7 -7
  36. package/lib/esm/common/utils/xssUtils.js +23 -51
  37. package/lib/esm/common/utils.js +12 -1
  38. package/lib/esm/components/errorboundary/ErrorBoundary.js +4 -2
  39. package/lib/esm/components/livechatwidget/LiveChatWidget.js +9 -1
  40. package/lib/esm/components/livechatwidget/common/ActivitySubscriber/BotAuthActivitySubscriber.js +23 -6
  41. package/lib/esm/components/livechatwidget/common/ChatWidgetEvents.js +1 -0
  42. package/lib/esm/components/livechatwidget/common/PersistentConversationHandler.js +9 -0
  43. package/lib/esm/components/livechatwidget/common/authHelper.js +44 -4
  44. package/lib/esm/components/livechatwidget/common/createAdapter.js +3 -2
  45. package/lib/esm/components/livechatwidget/common/liveChatConfigUtils.js +33 -2
  46. package/lib/esm/components/livechatwidget/common/startChat.js +31 -14
  47. package/lib/esm/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +62 -2
  48. package/lib/esm/components/postchatsurveypanestateful/PostChatSurveyPaneStateful.js +6 -4
  49. package/lib/esm/components/proactivechatpanestateful/ProactiveChatPaneStateful.js +1 -2
  50. package/lib/esm/components/webchatcontainerstateful/WebChatContainerStateful.js +43 -28
  51. package/lib/esm/components/webchatcontainerstateful/common/activityConverters/convertPersistentChatHistoryMessageToActivity.js +117 -16
  52. package/lib/esm/components/webchatcontainerstateful/common/defaultStyles/defaultAdaptiveCardStyles.js +3 -1
  53. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/WebChatEventSubscribers.js +13 -2
  54. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/LazyLoadActivity.js +160 -171
  55. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.js +1 -1
  56. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/NotDeliveredTimestamp.js +2 -0
  57. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/channelDataMiddleware.js +1 -0
  58. package/lib/esm/contexts/common/LiveChatWidgetActionType.js +1 -0
  59. package/lib/esm/contexts/common/LiveChatWidgetContextInitialState.js +6 -1
  60. package/lib/esm/contexts/createReducer.js +15 -0
  61. package/lib/esm/controller/componentController.js +13 -1
  62. package/lib/esm/plugins/newMessageEventHandler.js +20 -3
  63. package/lib/types/common/Constants.d.ts +2 -0
  64. package/lib/types/common/facades/FacadeChatSDK.d.ts +29 -0
  65. package/lib/types/common/facades/types/IFacadeChatSDKInput.d.ts +3 -1
  66. package/lib/types/common/telemetry/TelemetryConstants.d.ts +13 -2
  67. package/lib/types/common/utils/xssUtils.d.ts +5 -21
  68. package/lib/types/common/utils.d.ts +9 -1
  69. package/lib/types/components/errorboundary/ErrorBoundary.d.ts +1 -1
  70. package/lib/types/components/livechatwidget/common/ActivitySubscriber/BotAuthActivitySubscriber.d.ts +1 -0
  71. package/lib/types/components/livechatwidget/common/ChatWidgetEvents.d.ts +2 -1
  72. package/lib/types/components/livechatwidget/common/authHelper.d.ts +9 -2
  73. package/lib/types/components/livechatwidget/common/liveChatConfigUtils.d.ts +11 -0
  74. package/lib/types/components/livechatwidget/interfaces/IBotAuthActivitySubscriberOptionalParams.d.ts +1 -0
  75. package/lib/types/components/webchatcontainerstateful/interfaces/IAdaptiveCardStyles.d.ts +2 -0
  76. package/lib/types/components/webchatcontainerstateful/interfaces/IBotAuthConfig.d.ts +7 -0
  77. package/lib/types/components/webchatcontainerstateful/interfaces/IExtendedChatConffig.d.ts +1 -1
  78. package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/LazyLoadActivity.d.ts +14 -38
  79. package/lib/types/contexts/common/ILiveChatWidgetContext.d.ts +1 -0
  80. package/lib/types/contexts/common/LiveChatWidgetActionType.d.ts +2 -1
  81. package/package.json +15 -6
@@ -60,8 +60,10 @@ let LazyLoadHandler = /*#__PURE__*/function () {
60
60
  // Temporary disable flag
61
61
  // The intersection observer instance
62
62
 
63
- // Scroll operation state
64
- // Current scroll operation tracking
63
+ // Scroll anchoring state (height-delta approach)
64
+ // scrollHeight before content loads
65
+ // scrollTop before content loads
66
+ // Tracks if the first batch has loaded
65
67
  // Prevents concurrent scroll operations (public for event handlers)
66
68
 
67
69
  // Timeout and queue management
@@ -105,6 +107,7 @@ let LazyLoadHandler = /*#__PURE__*/function () {
105
107
  key: "directReset",
106
108
  value: function directReset() {
107
109
  LazyLoadHandler.resetPending = true;
110
+ LazyLoadHandler.initialLoadComplete = false; // New session — next batch is an initial load
108
111
  LazyLoadHandler.setHasMoreHistoryAvailable(true);
109
112
  LazyLoadHandler.unmount();
110
113
  }
@@ -408,16 +411,15 @@ let LazyLoadHandler = /*#__PURE__*/function () {
408
411
 
409
412
  /**
410
413
  * Main lazy load trigger handler
411
- *
414
+ *
412
415
  * This is the core method that executes when the trigger element becomes visible.
413
416
  * It coordinates the entire lazy loading process:
414
- *
417
+ *
415
418
  * 1. Sets flags to prevent concurrent operations
416
- * 2. Dispatches event to fetch more chat history
417
- * 3. Waits for content to load
418
- * 4. Executes scroll adjustment to maintain user position
419
- *
420
- * Timing: Uses 300ms delay to allow content loading before scroll adjustment
419
+ * 2. Captures scroll state BEFORE content loads (for height-delta anchoring)
420
+ * 3. Dispatches event to fetch more chat history
421
+ *
422
+ * Scroll anchoring is triggered by HISTORY_BATCH_LOADED event (not a blind timeout).
421
423
  */
422
424
  }, {
423
425
  key: "handleLazyLoadTrigger",
@@ -431,138 +433,60 @@ let LazyLoadHandler = /*#__PURE__*/function () {
431
433
  LazyLoadHandler.pendingScrollAction = true; // Block new scroll actions
432
434
  LazyLoadHandler.paused = true; // Pause intersection observer
433
435
 
436
+ // Capture scroll geometry BEFORE new content loads — used for height-delta anchoring
437
+ const {
438
+ container,
439
+ isScrollable
440
+ } = LazyLoadHandler.findScrollContainer();
441
+ if (container && isScrollable) {
442
+ LazyLoadHandler.preLoadScrollHeight = container.scrollHeight;
443
+ LazyLoadHandler.preLoadScrollTop = container.scrollTop;
444
+ }
445
+ LazyLoadHandler.logLifecycleEvent(_TelemetryConstants.TelemetryEvent.LCWLazyLoadTriggerFired, "Lazy load trigger fired — fetching history");
446
+
434
447
  // Dispatch custom event to trigger chat history fetching
435
- // This event is handled by other parts of the chat system
436
448
  (0, _dispatchCustomEvent.default)(_ChatWidgetEvents.default.FETCH_PERSISTENT_CHAT_HISTORY);
437
-
438
- // Wait for content to load before performing scroll adjustment
439
- // 200ms provides good balance between responsiveness and content loading
440
- const timeoutId = window.setTimeout(() => {
441
- LazyLoadHandler.retryTimeouts.delete(timeoutId);
442
- LazyLoadHandler.executeReliableScroll();
443
- }, 200); // Reduced from 300ms to 200ms for faster response
444
- LazyLoadHandler.retryTimeouts.add(timeoutId);
445
449
  }
446
450
 
447
451
  /**
448
- * Executes reliable scroll with validation and retry logic
449
- *
450
- * This method implements a robust scroll system that:
451
- * 1. Finds and validates the scroll container
452
- * 2. Calculates target scroll position (current + 35px)
453
- * 3. Sets up scroll state for tracking and retries
454
- * 4. Initiates the scroll attempt process
455
- *
456
- * If no suitable container is found, schedules a retry.
452
+ * Applies height-delta scroll anchoring after new history content is prepended.
453
+ *
454
+ * When content is added above the viewport, the browser keeps scrollTop constant
455
+ * but the content shifts down — making the viewport drift upward.
456
+ * Fix: scrollTop = savedScrollTop + (newScrollHeight - savedScrollHeight)
457
+ * This keeps the user looking at the same content they had before.
457
458
  */
458
459
  }, {
459
- key: "executeReliableScroll",
460
- value: function executeReliableScroll() {
461
- // Guard: Don't execute scroll if no more history is available
462
- if (!LazyLoadHandler.hasMoreHistoryAvailable) {
463
- LazyLoadHandler.finishScrollAction();
464
- return;
465
- }
466
-
467
- // Find container using multiple fallback strategies
460
+ key: "applyScrollAnchor",
461
+ value: function applyScrollAnchor() {
468
462
  const {
469
463
  container,
470
464
  isScrollable
471
465
  } = LazyLoadHandler.findScrollContainer();
472
466
  if (!container || !isScrollable) {
473
- LazyLoadHandler.scheduleScrollRetry();
474
- return;
475
- }
476
-
477
- // Calculate scroll positions
478
- const initialScrollTop = container.scrollTop;
479
- const targetScrollTop = initialScrollTop + 35; // 35px down to maintain position
480
-
481
- // Set up scroll state for tracking and retries
482
- LazyLoadHandler.scrollState = {
483
- container,
484
- initialScrollTop,
485
- targetScrollTop,
486
- attemptCount: 0,
487
- maxAttempts: 5 // Allow up to 5 retry attempts
488
- };
489
-
490
- // Begin the scroll attempt process
491
- LazyLoadHandler.attemptScroll();
492
- }
493
-
494
- /**
495
- * Attempts to perform scroll with verification and retry logic
496
- *
497
- * This method implements a sophisticated scroll system:
498
- *
499
- * 1. Uses requestAnimationFrame for smooth execution
500
- * 2. Performs the scroll operation
501
- * 3. Verifies scroll actually occurred (5px tolerance)
502
- * 4. Retries with exponential backoff if failed
503
- * 5. Continues operation after max attempts
504
- *
505
- * The two-frame approach ensures scroll is applied and then verified
506
- * after the browser has had time to process the scroll change.
507
- */
508
- }, {
509
- key: "attemptScroll",
510
- value: function attemptScroll() {
511
- // Guard: Don't attempt scroll if no more history is available
512
- if (!LazyLoadHandler.hasMoreHistoryAvailable) {
513
467
  LazyLoadHandler.finishScrollAction();
514
468
  return;
515
469
  }
516
- if (!LazyLoadHandler.scrollState) {
517
- LazyLoadHandler.finishScrollAction();
518
- return;
470
+ const newScrollHeight = container.scrollHeight;
471
+ const heightDelta = newScrollHeight - LazyLoadHandler.preLoadScrollHeight;
472
+ if (heightDelta > 0) {
473
+ container.scrollTop = LazyLoadHandler.preLoadScrollTop + heightDelta;
519
474
  }
520
-
521
- // Extract current scroll state
522
- const {
523
- container,
524
- targetScrollTop,
525
- attemptCount,
526
- maxAttempts
527
- } = LazyLoadHandler.scrollState;
528
- LazyLoadHandler.scrollState.attemptCount++;
529
-
530
- // Perform scroll using requestAnimationFrame for smooth execution
531
- // Frame 1: Apply the scroll
532
- requestAnimationFrame(() => {
533
- // Double-check history availability before applying scroll
534
- if (!LazyLoadHandler.hasMoreHistoryAvailable) {
535
- LazyLoadHandler.finishScrollAction();
536
- return;
537
- }
538
- if (container) {
539
- container.scrollTop = targetScrollTop;
540
- }
541
-
542
- // Frame 2: Verify scroll occurred after browser has processed the change
543
- requestAnimationFrame(() => {
544
- // Triple-check history availability before verification
545
- if (!LazyLoadHandler.hasMoreHistoryAvailable) {
546
- LazyLoadHandler.finishScrollAction();
547
- return;
548
- }
549
- const actualScrollTop = container ? container.scrollTop : 0;
550
- const scrollSucceeded = Math.abs(actualScrollTop - targetScrollTop) < 5; // 5px tolerance for success
551
-
552
- if (scrollSucceeded) {
553
- LazyLoadHandler.finishScrollAction();
554
- } else if (attemptCount < maxAttempts) {
555
- // Retry with exponential backoff (100ms * attempt number)
556
- const timeoutId = window.setTimeout(() => {
557
- LazyLoadHandler.retryTimeouts.delete(timeoutId);
558
- LazyLoadHandler.attemptScroll();
559
- }, 100 * attemptCount); // Exponential backoff
560
- LazyLoadHandler.retryTimeouts.add(timeoutId);
561
- } else {
562
- LazyLoadHandler.finishScrollAction();
475
+ try {
476
+ _TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
477
+ Event: _TelemetryConstants.TelemetryEvent.LCWLazyLoadScrollAnchorApplied,
478
+ Description: "Scroll anchor applied after history batch",
479
+ CustomProperties: {
480
+ heightDelta,
481
+ preLoadScrollHeight: LazyLoadHandler.preLoadScrollHeight,
482
+ newScrollHeight,
483
+ newScrollTop: container.scrollTop
563
484
  }
564
485
  });
565
- });
486
+ } catch {
487
+ // Silent fail — don't break scroll anchoring for telemetry issues
488
+ }
489
+ LazyLoadHandler.finishScrollAction();
566
490
  }
567
491
 
568
492
  /**
@@ -576,7 +500,6 @@ let LazyLoadHandler = /*#__PURE__*/function () {
576
500
  key: "finishScrollAction",
577
501
  value: function finishScrollAction() {
578
502
  // Clean up scroll tracking state
579
- LazyLoadHandler.scrollState = null;
580
503
  LazyLoadHandler.pendingScrollAction = false;
581
504
 
582
505
  // Schedule unpause and reset with delay for content stabilization
@@ -588,33 +511,9 @@ let LazyLoadHandler = /*#__PURE__*/function () {
588
511
  LazyLoadHandler.retryTimeouts.add(timeoutId);
589
512
  }
590
513
 
591
- /**
592
- * Schedules retry for failed scroll operations
593
- *
594
- * Used when scroll container is not available or scrollable.
595
- * Provides a longer delay to allow container to become ready.
596
- */
597
- }, {
598
- key: "scheduleScrollRetry",
599
- value: function scheduleScrollRetry() {
600
- // Don't schedule retry if no more history is available
601
- if (!LazyLoadHandler.hasMoreHistoryAvailable) {
602
- LazyLoadHandler.finishScrollAction();
603
- return;
604
- }
605
- const timeoutId = window.setTimeout(() => {
606
- LazyLoadHandler.retryTimeouts.delete(timeoutId);
607
- // Only retry if we're still in a pending scroll action state and have more history
608
- if (LazyLoadHandler.pendingScrollAction && LazyLoadHandler.hasMoreHistoryAvailable) {
609
- LazyLoadHandler.executeReliableScroll();
610
- }
611
- }, 1000); // 1 second delay for container readiness
612
- LazyLoadHandler.retryTimeouts.add(timeoutId);
613
- }
614
-
615
514
  /**
616
515
  * Schedules observer reset for next lazy load cycle
617
- *
516
+ *
618
517
  * After a lazy load operation completes, the observer needs to be reset
619
518
  * to detect the next time the user scrolls to the top.
620
519
  */
@@ -732,7 +631,7 @@ let LazyLoadHandler = /*#__PURE__*/function () {
732
631
  }, {
733
632
  key: "moveScrollDown",
734
633
  value: function moveScrollDown() {
735
- LazyLoadHandler.executeReliableScroll();
634
+ LazyLoadHandler.applyScrollAnchor();
736
635
  }
737
636
 
738
637
  /**
@@ -772,6 +671,9 @@ let LazyLoadHandler = /*#__PURE__*/function () {
772
671
  }, {
773
672
  key: "handleNoMoreHistoryAvailable",
774
673
  value: function handleNoMoreHistoryAvailable() {
674
+ if (!LazyLoadHandler.initialLoadComplete) {
675
+ LazyLoadHandler.initialLoadComplete = true;
676
+ }
775
677
  LazyLoadHandler.setHasMoreHistoryAvailable(false);
776
678
  LazyLoadHandler.paused = true;
777
679
  LazyLoadHandler.pendingScrollAction = false; // Reset this to prevent stuck states
@@ -789,9 +691,6 @@ let LazyLoadHandler = /*#__PURE__*/function () {
789
691
  LazyLoadHandler.observer.disconnect();
790
692
  LazyLoadHandler.observer = null;
791
693
  }
792
-
793
- // Clear scroll state
794
- LazyLoadHandler.scrollState = null;
795
694
  }
796
695
 
797
696
  /**
@@ -813,6 +712,8 @@ let LazyLoadHandler = /*#__PURE__*/function () {
813
712
  LazyLoadHandler.setHasMoreHistoryAvailable(true); // Reset history availability flag
814
713
  LazyLoadHandler.initializationQueue = []; // Clear action queue
815
714
  LazyLoadHandler.resetPending = false; // Clear pending reset flag
715
+ // Note: initialLoadComplete is NOT reset here — it persists across observer cycles.
716
+ // It's only reset on new chat sessions (directReset / PersistentConversationReset).
816
717
 
817
718
  // Reinitialize with faster timing for better responsiveness
818
719
  const timeoutId = window.setTimeout(() => {
@@ -852,8 +753,9 @@ let LazyLoadHandler = /*#__PURE__*/function () {
852
753
  LazyLoadHandler.initialized = false;
853
754
  LazyLoadHandler.paused = false;
854
755
  LazyLoadHandler.pendingScrollAction = false;
855
- LazyLoadHandler.scrollState = null;
856
756
  LazyLoadHandler.isReady = false;
757
+ LazyLoadHandler.preLoadScrollHeight = 0;
758
+ LazyLoadHandler.preLoadScrollTop = 0;
857
759
  LazyLoadHandler.initializationQueue = [];
858
760
  // Note: Don't reset resetPending here as it needs to persist across unmount/mount cycles
859
761
  }
@@ -903,7 +805,9 @@ _defineProperty(LazyLoadHandler, "targetId", "lazy-load-trigger-element");
903
805
  _defineProperty(LazyLoadHandler, "initialized", false);
904
806
  _defineProperty(LazyLoadHandler, "paused", false);
905
807
  _defineProperty(LazyLoadHandler, "observer", null);
906
- _defineProperty(LazyLoadHandler, "scrollState", null);
808
+ _defineProperty(LazyLoadHandler, "preLoadScrollHeight", 0);
809
+ _defineProperty(LazyLoadHandler, "preLoadScrollTop", 0);
810
+ _defineProperty(LazyLoadHandler, "initialLoadComplete", false);
907
811
  _defineProperty(LazyLoadHandler, "pendingScrollAction", false);
908
812
  _defineProperty(LazyLoadHandler, "retryTimeouts", new Set());
909
813
  _defineProperty(LazyLoadHandler, "resetPending", false);
@@ -911,6 +815,7 @@ _defineProperty(LazyLoadHandler, "initTimer", (0, _utils.createTimer)());
911
815
  _defineProperty(LazyLoadHandler, "resetEventListener", _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.BroadcastEvent.PersistentConversationReset).subscribe(() => {
912
816
  LazyLoadHandler.logLifecycleEvent(_TelemetryConstants.TelemetryEvent.LCWLazyLoadReset, "LazyLoad reset triggered");
913
817
  LazyLoadHandler.resetPending = true;
818
+ LazyLoadHandler.initialLoadComplete = false; // New session — next batch is an initial load
914
819
  LazyLoadHandler.setHasMoreHistoryAvailable(true); // Reset this immediately so activityMiddleware doesn't block rendering
915
820
  LazyLoadHandler.unmount(); // Clean up current state immediately
916
821
  }));
@@ -934,19 +839,11 @@ const LazyLoadActivity = props => {
934
839
  setHasMoreHistory(false);
935
840
  };
936
841
 
937
- // Event listener for HISTORY_LOAD_ERROR - hides banner temporarily without disabling future loads
842
+ // Event listener for HISTORY_LOAD_ERROR dismiss banner on any error
938
843
  const handleHistoryLoadError = () => {
939
- // Temporarily hide the banner by pausing, but keep hasMoreHistory true to allow retry
940
- LazyLoadHandler.paused = true;
941
- LazyLoadHandler.pendingScrollAction = false;
942
-
943
- // Re-enable after a delay to allow retry on next scroll
944
- // Note: This timeout is intentionally not tracked as it's scoped to the component lifecycle
945
- window.setTimeout(() => {
946
- LazyLoadHandler.paused = false;
947
- }, 2000); // 2 second delay before allowing retry
948
-
949
- LazyLoadHandler.logLifecycleEvent(_TelemetryConstants.TelemetryEvent.LCWLazyLoadHistoryError, "History load error - will retry on next scroll");
844
+ LazyLoadHandler.logLifecycleEvent(_TelemetryConstants.TelemetryEvent.LCWLazyLoadHistoryError, `History load error - dismissing banner. initialLoadComplete: ${LazyLoadHandler.initialLoadComplete}`);
845
+ LazyLoadHandler.handleNoMoreHistoryAvailable();
846
+ setHasMoreHistory(false);
950
847
  };
951
848
 
952
849
  // Event listener for PersistentConversationReset to sync React state
@@ -957,6 +854,97 @@ const LazyLoadActivity = props => {
957
854
  setHasMoreHistory(true);
958
855
  };
959
856
 
857
+ // Event listener for HISTORY_BATCH_LOADED — applies scroll anchoring after batch is processed
858
+ const handleBatchLoaded = () => {
859
+ LazyLoadHandler.logLifecycleEvent(_TelemetryConstants.TelemetryEvent.LCWLazyLoadBatchReceived, `Batch received — initialLoadComplete: ${LazyLoadHandler.initialLoadComplete}`);
860
+ if (!LazyLoadHandler.initialLoadComplete) {
861
+ // Initial load: use the same height-delta scroll anchoring as subsequent loads.
862
+ // This keeps the user at their current position while history is prepended above.
863
+ LazyLoadHandler.initialLoadComplete = true;
864
+ LazyLoadHandler.pendingScrollAction = false;
865
+ try {
866
+ const {
867
+ container
868
+ } = LazyLoadHandler.findScrollContainer();
869
+ if (container) {
870
+ const savedScrollTop = container.scrollTop;
871
+ const savedScrollHeight = container.scrollHeight;
872
+
873
+ // Freeze viewport while React renders new content above
874
+ container.style.overflow = "hidden";
875
+ let framesRemaining = 6;
876
+ const anchorScroll = () => {
877
+ const newScrollHeight = container.scrollHeight;
878
+ const heightDelta = newScrollHeight - savedScrollHeight;
879
+ if (heightDelta > 0) {
880
+ container.scrollTop = savedScrollTop + heightDelta;
881
+ }
882
+ framesRemaining--;
883
+ if (framesRemaining > 0) {
884
+ requestAnimationFrame(anchorScroll);
885
+ } else {
886
+ container.style.overflow = "";
887
+ LazyLoadHandler.paused = false;
888
+ LazyLoadHandler.scheduleReset();
889
+ }
890
+ };
891
+ requestAnimationFrame(anchorScroll);
892
+ } else {
893
+ LazyLoadHandler.paused = false;
894
+ LazyLoadHandler.scheduleReset();
895
+ }
896
+ } catch {
897
+ LazyLoadHandler.paused = false;
898
+ LazyLoadHandler.scheduleReset();
899
+ }
900
+ LazyLoadHandler.logLifecycleEvent(_TelemetryConstants.TelemetryEvent.LCWLazyLoadInitialLoadComplete, "Initial history load complete — scroll anchored to current position");
901
+ return;
902
+ }
903
+ // Pagination: apply height-delta scroll anchoring repeatedly across frames.
904
+ //
905
+ // react-scroll-to-bottom uses useEffect + rAF to auto-scroll to bottom when new content
906
+ // arrives. This fires at unpredictable timing relative to our callbacks, so a single
907
+ // scrollTop assignment gets overridden intermittently.
908
+ //
909
+ // Fix: re-apply the correct scrollTop across several animation frames. After a few frames,
910
+ // react-scroll-to-bottom detects the user is NOT at the bottom (sticky=false) and stops
911
+ // auto-scrolling. We also temporarily freeze overflow to prevent visible flicker.
912
+ try {
913
+ const {
914
+ container
915
+ } = LazyLoadHandler.findScrollContainer();
916
+ if (container) {
917
+ // Re-capture scrollTop — user may have scrolled during the API fetch
918
+ const savedScrollTop = container.scrollTop;
919
+ const savedScrollHeight = LazyLoadHandler.preLoadScrollHeight;
920
+
921
+ // Freeze viewport to prevent visible flicker between competing scroll positions
922
+ container.style.overflow = "hidden";
923
+ let framesRemaining = 6; // ~100ms at 60fps — enough for react-scroll-to-bottom to settle
924
+ const anchorScroll = () => {
925
+ const newScrollHeight = container.scrollHeight;
926
+ const heightDelta = newScrollHeight - savedScrollHeight;
927
+ if (heightDelta > 0) {
928
+ container.scrollTop = savedScrollTop + heightDelta;
929
+ }
930
+ framesRemaining--;
931
+ if (framesRemaining > 0) {
932
+ requestAnimationFrame(anchorScroll);
933
+ } else {
934
+ // All frames applied — restore overflow and finish
935
+ container.style.overflow = "";
936
+ LazyLoadHandler.finishScrollAction();
937
+ }
938
+ };
939
+ requestAnimationFrame(anchorScroll);
940
+ } else {
941
+ LazyLoadHandler.finishScrollAction();
942
+ }
943
+ } catch {
944
+ LazyLoadHandler.finishScrollAction();
945
+ }
946
+ };
947
+
960
948
  // Add secure event listener for no more history signal
961
949
  const eventBus = _SecureEventBus.default.getInstance();
962
950
  const unsubscribeNoMoreHistory = eventBus.subscribe(_ChatWidgetEvents.default.NO_MORE_HISTORY_AVAILABLE, handleNoMoreHistory);
@@ -964,6 +952,9 @@ const LazyLoadActivity = props => {
964
952
  // Add event listener for history load errors
965
953
  const unsubscribeHistoryError = eventBus.subscribe(_ChatWidgetEvents.default.HISTORY_LOAD_ERROR, handleHistoryLoadError);
966
954
 
955
+ // Add event listener for history batch loaded
956
+ const unsubscribeBatchLoaded = eventBus.subscribe(_ChatWidgetEvents.default.HISTORY_BATCH_LOADED, handleBatchLoaded);
957
+
967
958
  // Add event listener for persistent conversation reset
968
959
  const resetSubscription = _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.BroadcastEvent.PersistentConversationReset).subscribe(handlePersistentConversationReset);
969
960
 
@@ -981,6 +972,7 @@ const LazyLoadActivity = props => {
981
972
  return () => {
982
973
  unsubscribeNoMoreHistory();
983
974
  unsubscribeHistoryError();
975
+ unsubscribeBatchLoaded();
984
976
  resetSubscription.unsubscribe();
985
977
  };
986
978
  }
@@ -1034,6 +1026,7 @@ const LazyLoadActivity = props => {
1034
1026
  // Remove event listeners
1035
1027
  unsubscribeNoMoreHistory();
1036
1028
  unsubscribeHistoryError();
1029
+ unsubscribeBatchLoaded();
1037
1030
  resetSubscription.unsubscribe();
1038
1031
  if (container) {
1039
1032
  container.removeEventListener("scroll", handleScroll);
@@ -133,7 +133,7 @@ const createActivityMiddleware = (renderMarkdown, systemMessageStyleProps, userM
133
133
  if (isTagIncluded(card, _Constants.Constants.conversationDividerTag)) {
134
134
  const conversationDividerLabel = (localizedTexts === null || localizedTexts === void 0 ? void 0 : localizedTexts.CONVERSATION_DIVIDER_ARIA_LABEL) || _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts.CONVERSATION_DIVIDER_ARIA_LABEL;
135
135
  // Pass the computed localized text to the divider component
136
- return /*#__PURE__*/_react.default.createElement(_ConversationDividerActivity.default, {
136
+ return () => /*#__PURE__*/_react.default.createElement(_ConversationDividerActivity.default, {
137
137
  dividerActivityAriaLabel: conversationDividerLabel
138
138
  });
139
139
  }
@@ -17,6 +17,8 @@ var _useChatContextStore = _interopRequireDefault(require("../../../../../../hoo
17
17
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
18
18
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
19
19
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
20
+ /* eslint-disable @typescript-eslint/no-explicit-any */
21
+
20
22
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
23
  const NotDeliveredTimestamp = _ref => {
22
24
  var _state$domainStates$r, _state$domainStates$r2, _state$domainStates$r3, _state$domainStates$r4, _state$domainStates$m, _state$domainStates$m2;
@@ -57,4 +57,5 @@ exports.LiveChatWidgetActionType = LiveChatWidgetActionType;
57
57
  LiveChatWidgetActionType[LiveChatWidgetActionType["SET_CONVERSATIONAL_SURVEY_DISPLAY"] = 47] = "SET_CONVERSATIONAL_SURVEY_DISPLAY";
58
58
  LiveChatWidgetActionType[LiveChatWidgetActionType["GET_IN_MEMORY_STATE"] = 48] = "GET_IN_MEMORY_STATE";
59
59
  LiveChatWidgetActionType[LiveChatWidgetActionType["SET_BOT_AVATAR_INITIALS"] = 49] = "SET_BOT_AVATAR_INITIALS";
60
+ LiveChatWidgetActionType[LiveChatWidgetActionType["SET_USER_AUTHENTICATED"] = 50] = "SET_USER_AUTHENTICATED";
60
61
  })(LiveChatWidgetActionType || (exports.LiveChatWidgetActionType = LiveChatWidgetActionType = {}));
@@ -44,6 +44,10 @@ const getLiveChatWidgetContextInitialState = props => {
44
44
  if (!initialStateFromCache.domainStates.botAvatarInitials) {
45
45
  initialStateFromCache.domainStates.botAvatarInitials = _defaultWebChatStyles.defaultWebChatStyles.botAvatarInitials;
46
46
  }
47
+ // Default isUserAuthenticated to false if not present in cache
48
+ if (initialStateFromCache.appStates.isUserAuthenticated === undefined) {
49
+ initialStateFromCache.appStates.isUserAuthenticated = false;
50
+ }
47
51
  return initialStateFromCache;
48
52
  }
49
53
  const LiveChatWidgetContextInitialState = {
@@ -93,7 +97,8 @@ const getLiveChatWidgetContextInitialState = props => {
93
97
  selectedSurveyMode: null,
94
98
  postChatParticipantType: undefined,
95
99
  isConversationalSurvey: false,
96
- isConversationalSurveyEnabled: false
100
+ isConversationalSurveyEnabled: false,
101
+ isUserAuthenticated: false
97
102
  },
98
103
  uiStates: {
99
104
  showConfirmationPane: false,
@@ -716,6 +716,21 @@ const reducer = (state, action) => {
716
716
  isConversationalSurvey: action.payload
717
717
  }
718
718
  };
719
+ case _LiveChatWidgetActionType.LiveChatWidgetActionType.SET_USER_AUTHENTICATED:
720
+ inMemory = {
721
+ ...inMemory,
722
+ appStates: {
723
+ ...inMemory.appStates,
724
+ isUserAuthenticated: action.payload
725
+ }
726
+ };
727
+ return {
728
+ ...state,
729
+ appStates: {
730
+ ...state.appStates,
731
+ isUserAuthenticated: action.payload
732
+ }
733
+ };
719
734
  case _LiveChatWidgetActionType.LiveChatWidgetActionType.GET_IN_MEMORY_STATE:
720
735
  return inMemory;
721
736
  default:
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.shouldShowWebChatContainer = exports.shouldShowStartChatErrorPane = exports.shouldShowReconnectChatPane = exports.shouldShowProactiveChatPane = exports.shouldShowPreChatSurveyPane = exports.shouldShowPostChatSurveyPane = exports.shouldShowPostChatLoadingPane = exports.shouldShowOutOfOfficeHoursPane = exports.shouldShowLoadingPane = exports.shouldShowHeader = exports.shouldShowFooter = exports.shouldShowEmailTranscriptPane = exports.shouldShowConfirmationPane = exports.shouldShowChatButton = exports.shouldShowCallingContainer = void 0;
7
7
  var _ConversationState = require("../contexts/common/ConversationState");
8
+ var _liveChatConfigUtils = require("../components/livechatwidget/common/liveChatConfigUtils");
8
9
  const shouldShowChatButton = state => {
9
10
  var _state$appStates;
10
11
  return (state.appStates.isMinimized || state.appStates.conversationState === _ConversationState.ConversationState.Closed) && (state === null || state === void 0 ? void 0 : (_state$appStates = state.appStates) === null || _state$appStates === void 0 ? void 0 : _state$appStates.hideStartChatButton) === false; // Do not show chat button in case of popout
@@ -27,7 +28,18 @@ const shouldShowEmailTranscriptPane = state => {
27
28
  };
28
29
  exports.shouldShowEmailTranscriptPane = shouldShowEmailTranscriptPane;
29
30
  const shouldShowWebChatContainer = state => {
30
- return !state.appStates.isMinimized && state.appStates.conversationState === _ConversationState.ConversationState.Active || state.appStates.conversationState === _ConversationState.ConversationState.InActive || state.appStates.conversationState === _ConversationState.ConversationState.Postchat && state.appStates.isConversationalSurveyEnabled && state.appStates.isConversationalSurvey;
31
+ var _state$domainStates;
32
+ const extendedChatConfig = state === null || state === void 0 ? void 0 : (_state$domainStates = state.domainStates) === null || _state$domainStates === void 0 ? void 0 : _state$domainStates.liveChatConfig;
33
+ const persistentHistoryEnabled = (0, _liveChatConfigUtils.shouldLoadPersistentChatHistory)(extendedChatConfig);
34
+ const isActiveOrInactive = state.appStates.conversationState === _ConversationState.ConversationState.Active || state.appStates.conversationState === _ConversationState.ConversationState.InActive;
35
+ const isPostchatSurvey = state.appStates.conversationState === _ConversationState.ConversationState.Postchat && state.appStates.isConversationalSurveyEnabled && state.appStates.isConversationalSurvey;
36
+ if (persistentHistoryEnabled) {
37
+ // Keep WebChat in DOM even when minimized (CSS hides it via visibility)
38
+ // This preserves scroll position across minimize/maximize cycles
39
+ return isActiveOrInactive || isPostchatSurvey;
40
+ }
41
+ // Original behavior - hide WebChat when minimized
42
+ return !state.appStates.isMinimized && (isActiveOrInactive || isPostchatSurvey);
31
43
  };
32
44
  exports.shouldShowWebChatContainer = shouldShowWebChatContainer;
33
45
  const shouldShowLoadingPane = state => {
@@ -20,6 +20,8 @@ const createOnNewAdapterActivityHandler = (chatId, userId, startTime) => {
20
20
  // this is a workaround for the fact that we dont have a way to identify if a message is history or new, and it will provide consistency across different scenarios
21
21
 
22
22
  let isHistoryMessageReceivedEventRaised = false;
23
+ let isInQueue = false;
24
+ let agentJoinLogged = false;
23
25
  const onNewAdapterActivityHandler = activity => {
24
26
  raiseMessageEvent(activity);
25
27
  };
@@ -41,9 +43,14 @@ const createOnNewAdapterActivityHandler = (chatId, userId, startTime) => {
41
43
  });
42
44
  };
43
45
  const systemMessageStrategy = activity => {
44
- var _TelemetryManager$Int3;
46
+ var _activity$channelData, _activity$channelData2, _TelemetryManager$Int3;
45
47
  const payload = (0, _util.buildMessagePayload)(activity, userId);
46
48
  payload.messageType = _Constants2.Constants.systemMessageTag;
49
+
50
+ // Track queue entry for agent-join detection
51
+ if (activity !== null && activity !== void 0 && (_activity$channelData = activity.channelData) !== null && _activity$channelData !== void 0 && (_activity$channelData2 = _activity$channelData.tags) !== null && _activity$channelData2 !== void 0 && _activity$channelData2.includes(_Constants2.Constants.queuePositionMessageTag)) {
52
+ isInQueue = true;
53
+ }
47
54
  if ((0, _util.isHistoryMessage)(activity, startTime)) {
48
55
  var _TelemetryManager$Int2;
49
56
  historyMessageStrategy((0, _util.polyfillMessagePayloadForEvent)(activity, payload, (_TelemetryManager$Int2 = _TelemetryManager.TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int2 === void 0 ? void 0 : _TelemetryManager$Int2.conversationId));
@@ -76,11 +83,11 @@ const createOnNewAdapterActivityHandler = (chatId, userId, startTime) => {
76
83
  }
77
84
  };
78
85
  const isValidMessage = activity => {
79
- var _activity$channelData, _activity$channelData2, _activity$channelData3;
86
+ var _activity$channelData3, _activity$channelData4, _activity$channelData5;
80
87
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
81
88
  const messageHasNoText = !(activity !== null && activity !== void 0 && activity.text);
82
89
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
83
- const messageHasNoTags = !(activity !== null && activity !== void 0 && activity.channelData) || !(activity !== null && activity !== void 0 && (_activity$channelData = activity.channelData) !== null && _activity$channelData !== void 0 && _activity$channelData.tags) || (activity === null || activity === void 0 ? void 0 : (_activity$channelData2 = activity.channelData) === null || _activity$channelData2 === void 0 ? void 0 : (_activity$channelData3 = _activity$channelData2.tags) === null || _activity$channelData3 === void 0 ? void 0 : _activity$channelData3.length) === 0;
90
+ const messageHasNoTags = !(activity !== null && activity !== void 0 && activity.channelData) || !(activity !== null && activity !== void 0 && (_activity$channelData3 = activity.channelData) !== null && _activity$channelData3 !== void 0 && _activity$channelData3.tags) || (activity === null || activity === void 0 ? void 0 : (_activity$channelData4 = activity.channelData) === null || _activity$channelData4 === void 0 ? void 0 : (_activity$channelData5 = _activity$channelData4.tags) === null || _activity$channelData5 === void 0 ? void 0 : _activity$channelData5.length) === 0;
84
91
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
85
92
  const messageHasNoAttachments = !(activity !== null && activity !== void 0 && activity.attachments) || (activity === null || activity === void 0 ? void 0 : activity.attachments.length) === 0;
86
93
  if (messageHasNoTags && messageHasNoText && messageHasNoAttachments) {
@@ -102,6 +109,16 @@ const createOnNewAdapterActivityHandler = (chatId, userId, startTime) => {
102
109
  historyMessageStrategy((0, _util.polyfillMessagePayloadForEvent)(activity, payload, (_TelemetryManager$Int4 = _TelemetryManager.TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int4 === void 0 ? void 0 : _TelemetryManager$Int4.conversationId));
103
110
  return;
104
111
  }
112
+
113
+ // Log agent joining when first non-history message is received after queue
114
+ if (isInQueue && !agentJoinLogged) {
115
+ agentJoinLogged = true;
116
+ isInQueue = false;
117
+ _TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
118
+ Event: _TelemetryConstants.TelemetryEvent.AgentJoinedConversation,
119
+ Description: "First message received from agent after queue"
120
+ });
121
+ }
105
122
  firstResponseLatencyTracker.stopClock(payload);
106
123
  const newMessageReceivedEvent = {
107
124
  eventName: _TelemetryConstants.BroadcastEvent.NewMessageReceived,
@@ -75,6 +75,8 @@ _defineProperty(Constants, "audioMediaRegex", /(\.)(aac|aiff|alac|amr|flac|mp2|m
75
75
  _defineProperty(Constants, "videoMediaRegex", /(\.)(avchd|avi|flv|mpe|mpeg|mpg|mpv|mp4|m4p|m4v|mov|qt|swf|webm|wmv)$/i);
76
76
  _defineProperty(Constants, "chromeSupportedInlineMediaRegex", /(\.)(aac|mp3|wav|mp4)$/i);
77
77
  _defineProperty(Constants, "firefoxSupportedInlineMediaRegex", /(\.)(aac|flac|mp3|wav|mp4|mov)$/i);
78
+ _defineProperty(Constants, "AdaptiveCardType", "adaptivecard");
79
+ _defineProperty(Constants, "SuggestedActionsType", "suggestedactions");
78
80
  // calling container event names
79
81
  _defineProperty(Constants, "CallAdded", "callAdded");
80
82
  _defineProperty(Constants, "LocalVideoStreamAdded", "localVideoStreamAdded");