@microsoft/omnichannel-chat-widget 1.8.3 → 1.8.4-main.424a580

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 (132) hide show
  1. package/lib/cjs/common/Constants.js +5 -0
  2. package/lib/cjs/common/facades/FacadeChatSDK.js +6 -0
  3. package/lib/cjs/common/telemetry/AppInsightsEvents.js +4 -5
  4. package/lib/cjs/common/telemetry/TelemetryConstants.js +44 -2
  5. package/lib/cjs/common/telemetry/loggers/appInsightsLogger.js +26 -10
  6. package/lib/cjs/common/utils/SecureEventBus.js +307 -0
  7. package/lib/cjs/common/utils/dispatchCustomEvent.js +25 -0
  8. package/lib/cjs/components/chatbuttonstateful/ChatButtonStateful.js +16 -4
  9. package/lib/cjs/components/citationpanestateful/CitationPaneStateful.js +20 -1
  10. package/lib/cjs/components/headerstateful/HeaderStateful.js +8 -2
  11. package/lib/cjs/components/livechatwidget/common/ActivitySubscriber/AddActivitySubscriber.js +127 -0
  12. package/lib/cjs/components/livechatwidget/common/ChatWidgetEvents.js +15 -0
  13. package/lib/cjs/components/livechatwidget/common/PersistentConversationHandler.js +287 -0
  14. package/lib/cjs/components/livechatwidget/common/createAdapter.js +2 -0
  15. package/lib/cjs/components/livechatwidget/common/defaultProps/defaultPersistentChatHistoryProps.js +18 -0
  16. package/lib/cjs/components/livechatwidget/common/endChat.js +7 -1
  17. package/lib/cjs/components/livechatwidget/common/initWebChatComposer.js +16 -7
  18. package/lib/cjs/components/livechatwidget/common/renderSurveyHelpers.js +2 -2
  19. package/lib/cjs/components/livechatwidget/common/setPostChatContextAndLoadSurvey.js +3 -3
  20. package/lib/cjs/components/livechatwidget/common/startChat.js +5 -1
  21. package/lib/cjs/components/livechatwidget/common/startChatErrorHandler.js +24 -4
  22. package/lib/cjs/components/livechatwidget/interfaces/IPersistentChatHistoryProps.js +1 -0
  23. package/lib/cjs/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +22 -3
  24. package/lib/cjs/components/postchatsurveypanestateful/PostChatSurveyPaneStateful.js +29 -2
  25. package/lib/cjs/components/prechatsurveypanestateful/PreChatSurveyPaneStateful.js +12 -3
  26. package/lib/cjs/components/webchatcontainerstateful/WebChatContainerStateful.js +36 -5
  27. package/lib/cjs/components/webchatcontainerstateful/common/activities/botActivity.js +14 -0
  28. package/lib/cjs/components/webchatcontainerstateful/common/activities/conversationDividerActivity.js +17 -0
  29. package/lib/cjs/components/webchatcontainerstateful/common/activityConverters/convertPersistentChatHistoryMessageToActivity.js +97 -0
  30. package/lib/cjs/components/webchatcontainerstateful/common/defaultProps/defaultMiddlewareLocalizedTexts.js +3 -1
  31. package/lib/cjs/components/webchatcontainerstateful/common/defaultProps/defaultWebChatStatefulProps.js +1 -1
  32. package/lib/cjs/components/webchatcontainerstateful/hooks/usePersistentChatHistory.js +59 -0
  33. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/WebChatEventSubscribers.js +122 -0
  34. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/Constants.js +10 -0
  35. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/ConversationDividerActivity.js +47 -0
  36. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/LazyLoadActivity.js +1038 -0
  37. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/LoadInlineBannerActivity.js +34 -0
  38. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.js +50 -1
  39. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityStatusMiddleware.js +16 -2
  40. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/defaultStyles/defaultInLineBannerStyle.js +20 -0
  41. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/DeliveredTimestamp.js +2 -2
  42. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/HistoryMessageTimestamp.js +59 -0
  43. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/NotDeliveredTimestamp.js +5 -3
  44. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/SendingTimestamp.js +2 -2
  45. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/localizedStringsBotInitialsMiddleware.js +29 -7
  46. package/lib/cjs/contexts/common/LiveChatWidgetActionType.js +1 -0
  47. package/lib/cjs/contexts/common/LiveChatWidgetContextInitialState.js +7 -1
  48. package/lib/cjs/contexts/createReducer.js +15 -0
  49. package/lib/cjs/firstresponselatency/FirstMessageTrackerFromBot.js +3 -2
  50. package/lib/cjs/firstresponselatency/FirstResponseLatencyTracker.js +6 -2
  51. package/lib/cjs/plugins/newMessageEventHandler.js +4 -1
  52. package/lib/esm/common/Constants.js +5 -0
  53. package/lib/esm/common/facades/FacadeChatSDK.js +6 -0
  54. package/lib/esm/common/telemetry/AppInsightsEvents.js +4 -5
  55. package/lib/esm/common/telemetry/TelemetryConstants.js +42 -1
  56. package/lib/esm/common/telemetry/loggers/appInsightsLogger.js +27 -11
  57. package/lib/esm/common/utils/SecureEventBus.js +328 -0
  58. package/lib/esm/common/utils/dispatchCustomEvent.js +18 -0
  59. package/lib/esm/components/chatbuttonstateful/ChatButtonStateful.js +17 -5
  60. package/lib/esm/components/citationpanestateful/CitationPaneStateful.js +20 -1
  61. package/lib/esm/components/headerstateful/HeaderStateful.js +9 -3
  62. package/lib/esm/components/livechatwidget/common/ActivitySubscriber/AddActivitySubscriber.js +120 -0
  63. package/lib/esm/components/livechatwidget/common/ChatWidgetEvents.js +8 -0
  64. package/lib/esm/components/livechatwidget/common/PersistentConversationHandler.js +280 -0
  65. package/lib/esm/components/livechatwidget/common/createAdapter.js +2 -0
  66. package/lib/esm/components/livechatwidget/common/defaultProps/defaultPersistentChatHistoryProps.js +11 -0
  67. package/lib/esm/components/livechatwidget/common/endChat.js +7 -1
  68. package/lib/esm/components/livechatwidget/common/initWebChatComposer.js +16 -7
  69. package/lib/esm/components/livechatwidget/common/renderSurveyHelpers.js +2 -2
  70. package/lib/esm/components/livechatwidget/common/setPostChatContextAndLoadSurvey.js +3 -3
  71. package/lib/esm/components/livechatwidget/common/startChat.js +7 -3
  72. package/lib/esm/components/livechatwidget/common/startChatErrorHandler.js +23 -4
  73. package/lib/esm/components/livechatwidget/interfaces/IPersistentChatHistoryProps.js +1 -0
  74. package/lib/esm/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +23 -4
  75. package/lib/esm/components/postchatsurveypanestateful/PostChatSurveyPaneStateful.js +31 -4
  76. package/lib/esm/components/prechatsurveypanestateful/PreChatSurveyPaneStateful.js +13 -4
  77. package/lib/esm/components/webchatcontainerstateful/WebChatContainerStateful.js +39 -5
  78. package/lib/esm/components/webchatcontainerstateful/common/activities/botActivity.js +7 -0
  79. package/lib/esm/components/webchatcontainerstateful/common/activities/conversationDividerActivity.js +9 -0
  80. package/lib/esm/components/webchatcontainerstateful/common/activityConverters/convertPersistentChatHistoryMessageToActivity.js +90 -0
  81. package/lib/esm/components/webchatcontainerstateful/common/defaultProps/defaultMiddlewareLocalizedTexts.js +3 -1
  82. package/lib/esm/components/webchatcontainerstateful/common/defaultProps/defaultWebChatStatefulProps.js +2 -2
  83. package/lib/esm/components/webchatcontainerstateful/hooks/usePersistentChatHistory.js +51 -0
  84. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/WebChatEventSubscribers.js +115 -0
  85. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/Constants.js +3 -0
  86. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/ConversationDividerActivity.js +39 -0
  87. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/LazyLoadActivity.js +1060 -0
  88. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/LoadInlineBannerActivity.js +25 -0
  89. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.js +48 -1
  90. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityStatusMiddleware.js +13 -0
  91. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/defaultStyles/defaultInLineBannerStyle.js +13 -0
  92. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/DeliveredTimestamp.js +1 -1
  93. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/HistoryMessageTimestamp.js +52 -0
  94. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/NotDeliveredTimestamp.js +3 -2
  95. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/SendingTimestamp.js +1 -1
  96. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/localizedStringsBotInitialsMiddleware.js +29 -7
  97. package/lib/esm/contexts/common/LiveChatWidgetActionType.js +1 -0
  98. package/lib/esm/contexts/common/LiveChatWidgetContextInitialState.js +7 -1
  99. package/lib/esm/contexts/createReducer.js +15 -0
  100. package/lib/esm/firstresponselatency/FirstMessageTrackerFromBot.js +3 -2
  101. package/lib/esm/firstresponselatency/FirstResponseLatencyTracker.js +6 -2
  102. package/lib/esm/plugins/newMessageEventHandler.js +4 -1
  103. package/lib/types/common/Constants.d.ts +4 -0
  104. package/lib/types/common/facades/FacadeChatSDK.d.ts +3 -1
  105. package/lib/types/common/telemetry/TelemetryConstants.d.ts +39 -2
  106. package/lib/types/common/utils/SecureEventBus.d.ts +159 -0
  107. package/lib/types/common/utils/dispatchCustomEvent.d.ts +2 -0
  108. package/lib/types/components/livechatwidget/common/ActivitySubscriber/AddActivitySubscriber.d.ts +45 -0
  109. package/lib/types/components/livechatwidget/common/ChatWidgetEvents.d.ts +7 -0
  110. package/lib/types/components/livechatwidget/common/PersistentConversationHandler.d.ts +28 -0
  111. package/lib/types/components/livechatwidget/common/defaultProps/defaultPersistentChatHistoryProps.d.ts +2 -0
  112. package/lib/types/components/livechatwidget/common/startChatErrorHandler.d.ts +1 -0
  113. package/lib/types/components/livechatwidget/interfaces/ILiveChatWidgetProps.d.ts +2 -0
  114. package/lib/types/components/livechatwidget/interfaces/IPersistentChatHistoryProps.d.ts +12 -0
  115. package/lib/types/components/webchatcontainerstateful/common/activities/botActivity.d.ts +7 -0
  116. package/lib/types/components/webchatcontainerstateful/common/activities/conversationDividerActivity.d.ts +10 -0
  117. package/lib/types/components/webchatcontainerstateful/common/activityConverters/convertPersistentChatHistoryMessageToActivity.d.ts +2 -0
  118. package/lib/types/components/webchatcontainerstateful/hooks/usePersistentChatHistory.d.ts +4 -0
  119. package/lib/types/components/webchatcontainerstateful/webchatcontroller/WebChatEventSubscribers.d.ts +7 -0
  120. package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/Constants.d.ts +3 -0
  121. package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/ConversationDividerActivity.d.ts +4 -0
  122. package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/LazyLoadActivity.d.ts +326 -0
  123. package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/LoadInlineBannerActivity.d.ts +8 -0
  124. package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.d.ts +2 -1
  125. package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityStatusMiddleware.d.ts +1 -0
  126. package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/defaultStyles/defaultInLineBannerStyle.d.ts +2 -0
  127. package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/HistoryMessageTimestamp.d.ts +2 -0
  128. package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/localizedStringsBotInitialsMiddleware.d.ts +1 -1
  129. package/lib/types/contexts/common/ILiveChatWidgetContext.d.ts +1 -0
  130. package/lib/types/contexts/common/ILiveChatWidgetLocalizedTexts.d.ts +6 -0
  131. package/lib/types/contexts/common/LiveChatWidgetActionType.d.ts +2 -1
  132. package/package.json +3 -3
@@ -0,0 +1,1060 @@
1
+ function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
3
+ function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
4
+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
5
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
6
+ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
7
+ function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
8
+ /**
9
+ * LazyLoadActivity Component
10
+ *
11
+ * This component implements a sophisticated lazy loading system for chat history in the webchat widget.
12
+ * It uses an IntersectionObserver to detect when the user scrolls near the top of the chat container
13
+ * and then triggers the loading of previous chat messages.
14
+ *
15
+ * Key Features:
16
+ * - Intersection Observer based detection for optimal performance
17
+ * - Reliable scroll management with retry mechanisms
18
+ * - Immediate user feedback even during initialization delays
19
+ * - Handles edge cases like minimize/maximize widget scenarios
20
+ * - Memory leak prevention with proper cleanup
21
+ *
22
+ * Architecture:
23
+ * - LazyLoadHandler: Static class managing all lazy load logic
24
+ * - LazyLoadActivity: React component providing the UI and lifecycle integration for lazy loading
25
+ *
26
+ * Flow:
27
+ * 1. Component renders a trigger element at the top of chat history
28
+ * 2. IntersectionObserver watches when this element becomes visible
29
+ * 3. When visible, dispatches event to fetch more chat history
30
+ * 4. Performs controlled scroll to maintain user position
31
+ * 5. Resets observer for next lazy load cycle
32
+ */
33
+
34
+ import { LogLevel, TelemetryEvent } from "../../../../../../common/telemetry/TelemetryConstants";
35
+ import React, { useEffect, useState } from "react";
36
+ import { BroadcastEvent } from "../../../../../../common/telemetry/TelemetryConstants";
37
+ import { BroadcastService } from "@microsoft/omnichannel-chat-components";
38
+ import ChatWidgetEvents from "../../../../../livechatwidget/common/ChatWidgetEvents";
39
+ import { LazyLoadActivityConstants } from "./Constants";
40
+ import LoadInlineBannerActivity from "./LoadInlineBannerActivity";
41
+ import { TelemetryHelper } from "../../../../../../common/telemetry/TelemetryHelper";
42
+ import { createTimer } from "../../../../../../common/utils";
43
+ import dispatchCustomEvent from "../../../../../../common/utils/dispatchCustomEvent";
44
+ import SecureEventBus from "../../../../../../common/utils/SecureEventBus";
45
+
46
+ /*
47
+ * Interface defining the state of a scroll operation
48
+ * Used to track and verify scroll actions for reliability
49
+ */
50
+ /**
51
+ * LazyLoadHandler - Static class managing all lazy loading functionality
52
+ *
53
+ * This class uses a singleton pattern with static methods to ensure consistent
54
+ * state management across component re-renders and widget lifecycle events.
55
+ *
56
+ * State Management:
57
+ * - initialized: Tracks if the IntersectionObserver has been set up
58
+ * - paused: Temporarily disables lazy loading (during initialization/loading)
59
+ * - pendingScrollAction: Prevents overlapping scroll operations
60
+ * - isReady: Indicates when the system is fully ready for user interactions
61
+ * - initializationQueue: Stores user actions while system initializes
62
+ *
63
+ * Reliability Features:
64
+ * - Retry mechanisms with exponential backoff
65
+ * - Scroll verification with tolerance checking
66
+ * - Multiple container detection strategies
67
+ * - Immediate user feedback during delays
68
+ * - Proper memory management and cleanup
69
+ */
70
+ let LazyLoadHandler = /*#__PURE__*/function () {
71
+ function LazyLoadHandler() {
72
+ _classCallCheck(this, LazyLoadHandler);
73
+ }
74
+ _createClass(LazyLoadHandler, null, [{
75
+ key: "logLifecycleEvent",
76
+ value:
77
+ // DOM element identifiers
78
+ // Main widget container
79
+ // Intersection trigger element
80
+
81
+ // Observer and initialization state
82
+ // Observer setup completion flag
83
+ // Temporary disable flag
84
+ // The intersection observer instance
85
+
86
+ // Scroll operation state
87
+ // Current scroll operation tracking
88
+ // Prevents concurrent scroll operations
89
+
90
+ // Timeout and queue management
91
+ // Tracks all setTimeout IDs for cleanup
92
+
93
+ // Flag to track if a reset is needed when component mounts
94
+
95
+ // Telemetry tracking - lifecycle events only
96
+
97
+ // Simple lifecycle logging without complex state tracking
98
+ function logLifecycleEvent(event, description, elapsedTime) {
99
+ try {
100
+ TelemetryHelper.logActionEvent(LogLevel.INFO, {
101
+ Event: event,
102
+ Description: description,
103
+ ...(elapsedTime && {
104
+ ElapsedTimeInMilliseconds: elapsedTime
105
+ })
106
+ });
107
+ } catch (error) {
108
+ // Silent fail - don't break functionality for telemetry issues
109
+ }
110
+ }
111
+
112
+ // Broadcast event subscription for marking reset needed when chat is closed/reopened
113
+ }, {
114
+ key: "setHasMoreHistoryAvailable",
115
+ value:
116
+ // Tracks if more history can be loaded
117
+
118
+ // Debug method to track what's changing hasMoreHistoryAvailable
119
+ function setHasMoreHistoryAvailable(value) {
120
+ LazyLoadHandler.hasMoreHistoryAvailable = value;
121
+ }
122
+
123
+ /**
124
+ * Direct reset method that can be called externally
125
+ * This bypasses the broadcast system for more reliable resets
126
+ */
127
+ }, {
128
+ key: "directReset",
129
+ value: function directReset() {
130
+ LazyLoadHandler.resetPending = true;
131
+ LazyLoadHandler.setHasMoreHistoryAvailable(true);
132
+ LazyLoadHandler.unmount();
133
+ }
134
+
135
+ /**
136
+ * Main initialization method for the lazy loading system
137
+ *
138
+ * This method sets up the IntersectionObserver that watches for when the trigger
139
+ * element becomes visible, indicating the user has scrolled near the top.
140
+ *
141
+ * Process:
142
+ * 1. Prevents duplicate initialization
143
+ * 2. Sets up intersection callback to handle visibility events
144
+ * 3. Finds appropriate scroll container with fallback strategies
145
+ * 4. Configures observer with optimal settings for chat history loading
146
+ * 5. Waits for target element availability with exponential backoff
147
+ *
148
+ * Observer Configuration:
149
+ * - root: The scrollable container (webchat or fallback)
150
+ * - rootMargin: 20px top margin to trigger slightly before element is visible
151
+ * - threshold: 0.05 (5%) visibility required to trigger
152
+ */
153
+ }, {
154
+ key: "useLazyLoadObserver",
155
+ value: function useLazyLoadObserver() {
156
+ // Auto-correct stale state: if hasMoreHistoryAvailable is false but we're trying to initialize, reset it
157
+ if (!LazyLoadHandler.hasMoreHistoryAvailable) {
158
+ LazyLoadHandler.setHasMoreHistoryAvailable(true);
159
+ }
160
+
161
+ // Prevent duplicate initialization
162
+ if (LazyLoadHandler.initialized) {
163
+ return;
164
+ }
165
+
166
+ // Start initialization timing
167
+ LazyLoadHandler.initTimer = createTimer();
168
+ LazyLoadHandler.logLifecycleEvent(TelemetryEvent.LCWLazyLoadInitializationStarted, "LazyLoad observer initialization started");
169
+
170
+ // Reset readiness during initialization to handle user interactions
171
+ LazyLoadHandler.isReady = false;
172
+
173
+ /**
174
+ * Intersection Observer Callback
175
+ *
176
+ * Triggered when the target element's visibility changes.
177
+ * Guards against triggering during paused states or pending operations.
178
+ *
179
+ * @param entries - Array of intersection observer entries
180
+ */
181
+ const callback = entries => {
182
+ // Guard clauses: Don't trigger if paused, already processing, or no more history available
183
+ if (LazyLoadHandler.paused || LazyLoadHandler.pendingScrollAction || !LazyLoadHandler.hasMoreHistoryAvailable) {
184
+ return;
185
+ }
186
+ entries.forEach(entry => {
187
+ // Check if element is intersecting with any visibility
188
+ if (entry.isIntersecting && entry.intersectionRatio > 0) {
189
+ // Double-check history availability at trigger time
190
+ if (!LazyLoadHandler.hasMoreHistoryAvailable) {
191
+ return;
192
+ }
193
+ LazyLoadHandler.handleLazyLoadTrigger();
194
+ }
195
+ });
196
+ };
197
+
198
+ /**
199
+ * Observer Setup Function
200
+ *
201
+ * Handles the complex process of finding the right container and
202
+ * setting up the intersection observer with proper configuration.
203
+ */
204
+ const setupObserver = () => {
205
+ // Find the scroll container using multiple strategies
206
+ const {
207
+ container: scrollContainer
208
+ } = LazyLoadHandler.findScrollContainer();
209
+ if (!scrollContainer) {
210
+ // Schedule retry with faster timeout for better responsiveness
211
+ const timeoutId = window.setTimeout(() => {
212
+ LazyLoadHandler.retryTimeouts.delete(timeoutId);
213
+ setupObserver();
214
+ }, 100); // Reduced from 200ms to 100ms for faster container detection
215
+ LazyLoadHandler.retryTimeouts.add(timeoutId);
216
+ return;
217
+ }
218
+
219
+ // Configure intersection observer options
220
+ const options = {
221
+ root: scrollContainer,
222
+ // Container to observe within
223
+ rootMargin: "20px 0px 0px 0px",
224
+ // Trigger 20px before element is visible
225
+ threshold: 0.05 // Trigger when 5% of element is visible
226
+ };
227
+
228
+ const observer = new IntersectionObserver(callback, options);
229
+
230
+ // Wait for target element and finalize setup
231
+ LazyLoadHandler.waitForTargetElement(() => {
232
+ const targetElement = document.getElementById(LazyLoadHandler.targetId);
233
+ if (targetElement) {
234
+ observer.observe(targetElement);
235
+ LazyLoadHandler.observer = observer;
236
+ LazyLoadHandler.initialized = true;
237
+ LazyLoadHandler.isReady = true; // Mark system as ready
238
+
239
+ // Log successful initialization
240
+ LazyLoadHandler.logLifecycleEvent(TelemetryEvent.LCWLazyLoadInitializationCompleted, "LazyLoad observer initialization completed", LazyLoadHandler.initTimer.milliSecondsElapsed);
241
+
242
+ // Process any actions that were queued during initialization
243
+ LazyLoadHandler.processInitializationQueue();
244
+ }
245
+ });
246
+ };
247
+ setupObserver();
248
+ }
249
+
250
+ /**
251
+ * Waits for the target element to be available in the DOM
252
+ *
253
+ * Uses exponential backoff to retry finding the target element.
254
+ * This is necessary because the target element may not be immediately
255
+ * available when the observer is being set up.
256
+ *
257
+ * @param callback - Function to call when target element is found
258
+ * @param maxAttempts - Maximum number of retry attempts (default: 10)
259
+ * @param attempt - Current attempt number (default: 1)
260
+ */
261
+ }, {
262
+ key: "waitForTargetElement",
263
+ value: function waitForTargetElement(callback) {
264
+ let maxAttempts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 10;
265
+ let attempt = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
266
+ const targetElement = document.getElementById(LazyLoadHandler.targetId);
267
+
268
+ // Target found, execute callback
269
+ if (targetElement) {
270
+ callback();
271
+ return;
272
+ }
273
+
274
+ // Max attempts reached, log error
275
+ if (attempt >= maxAttempts) {
276
+ LazyLoadHandler.logLifecycleEvent(TelemetryEvent.LCWLazyLoadTargetElementNotFound, "Target element not found after max attempts");
277
+ return;
278
+ }
279
+
280
+ // Schedule retry with exponential backoff (50ms * attempt number for faster initial attempts)
281
+ const timeoutId = window.setTimeout(() => {
282
+ LazyLoadHandler.retryTimeouts.delete(timeoutId);
283
+ LazyLoadHandler.waitForTargetElement(callback, maxAttempts, attempt + 1);
284
+ }, 50 * attempt); // Reduced from 100ms to 50ms for faster element detection
285
+ LazyLoadHandler.retryTimeouts.add(timeoutId);
286
+ }
287
+
288
+ /**
289
+ * Handles immediate scroll requests when system might not be ready
290
+ *
291
+ * This is crucial for handling minimize/maximize scenarios where users
292
+ * immediately scroll up before the observer is fully initialized.
293
+ *
294
+ * Two-Path Strategy:
295
+ * 1. If ready: Execute normal lazy load trigger
296
+ * 2. If not ready: Provide immediate feedback + queue action for later
297
+ *
298
+ * This ensures users always get visual feedback even during initialization delays.
299
+ */
300
+ }, {
301
+ key: "handleImmediateScrollRequest",
302
+ value: function handleImmediateScrollRequest() {
303
+ if (LazyLoadHandler.isReady) {
304
+ // System is ready, handle normally
305
+ LazyLoadHandler.handleLazyLoadTrigger();
306
+ } else {
307
+ // System not ready, provide immediate feedback and queue action
308
+ LazyLoadHandler.executeImmediateScrollFeedback();
309
+
310
+ // Queue the full lazy load action to execute when system is ready
311
+ LazyLoadHandler.initializationQueue.push(() => {
312
+ LazyLoadHandler.handleLazyLoadTrigger();
313
+ });
314
+
315
+ // Try to speed up initialization
316
+ LazyLoadHandler.expediteInitialization();
317
+ }
318
+ }
319
+
320
+ /**
321
+ * Provides immediate scroll feedback during initialization
322
+ *
323
+ * When the system isn't ready but user scrolls up, this provides
324
+ * immediate visual feedback (20px scroll) so the user knows their
325
+ * action was recognized.
326
+ */
327
+ }, {
328
+ key: "executeImmediateScrollFeedback",
329
+ value: function executeImmediateScrollFeedback() {
330
+ // Find container and validate scrollability
331
+ const {
332
+ container,
333
+ isScrollable
334
+ } = LazyLoadHandler.findScrollContainer();
335
+ if (container && isScrollable) {
336
+ const currentScrollTop = container.scrollTop;
337
+ const immediateScrollTarget = currentScrollTop + 20; // Small immediate scroll for feedback
338
+
339
+ // Use requestAnimationFrame for smooth scroll execution
340
+ requestAnimationFrame(() => {
341
+ if (container) {
342
+ container.scrollTop = immediateScrollTarget;
343
+ }
344
+ });
345
+ }
346
+ }
347
+
348
+ /**
349
+ * Forces faster initialization attempts
350
+ *
351
+ * When user interacts before system is ready, this tries to
352
+ * speed up the initialization process.
353
+ */
354
+ }, {
355
+ key: "expediteInitialization",
356
+ value: function expediteInitialization() {
357
+ // Force faster initialization attempts if not already initialized
358
+ if (!LazyLoadHandler.initialized) {
359
+ LazyLoadHandler.useLazyLoadObserver();
360
+ }
361
+ }
362
+
363
+ /**
364
+ * Processes all queued actions once system is ready
365
+ *
366
+ * During initialization, user actions are queued. Once the system
367
+ * is ready, this processes all queued actions with small delays
368
+ * between them to prevent overwhelming the system.
369
+ */
370
+ }, {
371
+ key: "processInitializationQueue",
372
+ value: function processInitializationQueue() {
373
+ // Process all queued actions sequentially
374
+ while (LazyLoadHandler.initializationQueue.length > 0) {
375
+ const action = LazyLoadHandler.initializationQueue.shift();
376
+ if (action) {
377
+ // Add small delay between queued actions to prevent conflicts
378
+ const timeoutId = window.setTimeout(() => {
379
+ LazyLoadHandler.retryTimeouts.delete(timeoutId);
380
+ action();
381
+ }, 25); // Reduced from 100ms to 25ms for faster processing
382
+ LazyLoadHandler.retryTimeouts.add(timeoutId);
383
+ }
384
+ }
385
+ }
386
+
387
+ /**
388
+ * Manually checks if target element is visible and triggers lazy load
389
+ *
390
+ * This is used when the intersection observer might not be ready yet
391
+ * but we need to check if the trigger element is already in view.
392
+ *
393
+ * Uses getBoundingClientRect() to calculate visibility manually.
394
+ */
395
+ }, {
396
+ key: "checkVisibilityAndTrigger",
397
+ value: function checkVisibilityAndTrigger() {
398
+ // Don't trigger if no more history is available
399
+ if (!LazyLoadHandler.hasMoreHistoryAvailable) {
400
+ return;
401
+ }
402
+ const targetElement = document.getElementById(LazyLoadHandler.targetId);
403
+ if (!targetElement) {
404
+ return;
405
+ }
406
+ const {
407
+ container
408
+ } = LazyLoadHandler.findScrollContainer();
409
+ if (!container) {
410
+ return;
411
+ }
412
+
413
+ // Get bounding rectangles for visibility calculation
414
+ const targetRect = targetElement.getBoundingClientRect();
415
+ const containerRect = container.getBoundingClientRect();
416
+
417
+ // Check if target element intersects with container viewport
418
+ const isVisible = targetRect.top < containerRect.bottom &&
419
+ // Target top is above container bottom
420
+ targetRect.bottom > containerRect.top &&
421
+ // Target bottom is below container top
422
+ targetRect.left < containerRect.right &&
423
+ // Target left is before container right
424
+ targetRect.right > containerRect.left // Target right is after container left
425
+ ;
426
+
427
+ if (isVisible) {
428
+ LazyLoadHandler.handleImmediateScrollRequest();
429
+ }
430
+ }
431
+
432
+ /**
433
+ * Main lazy load trigger handler
434
+ *
435
+ * This is the core method that executes when the trigger element becomes visible.
436
+ * It coordinates the entire lazy loading process:
437
+ *
438
+ * 1. Sets flags to prevent concurrent operations
439
+ * 2. Dispatches event to fetch more chat history
440
+ * 3. Waits for content to load
441
+ * 4. Executes scroll adjustment to maintain user position
442
+ *
443
+ * Timing: Uses 300ms delay to allow content loading before scroll adjustment
444
+ */
445
+ }, {
446
+ key: "handleLazyLoadTrigger",
447
+ value: function handleLazyLoadTrigger() {
448
+ // Final guard: Don't proceed if no more history is available
449
+ if (!LazyLoadHandler.hasMoreHistoryAvailable) {
450
+ return;
451
+ }
452
+
453
+ // Set flags to prevent overlapping operations
454
+ LazyLoadHandler.pendingScrollAction = true; // Block new scroll actions
455
+ LazyLoadHandler.paused = true; // Pause intersection observer
456
+
457
+ // Dispatch custom event to trigger chat history fetching
458
+ // This event is handled by other parts of the chat system
459
+ dispatchCustomEvent(ChatWidgetEvents.FETCH_PERSISTENT_CHAT_HISTORY);
460
+
461
+ // Wait for content to load before performing scroll adjustment
462
+ // 200ms provides good balance between responsiveness and content loading
463
+ const timeoutId = window.setTimeout(() => {
464
+ LazyLoadHandler.retryTimeouts.delete(timeoutId);
465
+ LazyLoadHandler.executeReliableScroll();
466
+ }, 200); // Reduced from 300ms to 200ms for faster response
467
+ LazyLoadHandler.retryTimeouts.add(timeoutId);
468
+ }
469
+
470
+ /**
471
+ * Executes reliable scroll with validation and retry logic
472
+ *
473
+ * This method implements a robust scroll system that:
474
+ * 1. Finds and validates the scroll container
475
+ * 2. Calculates target scroll position (current + 35px)
476
+ * 3. Sets up scroll state for tracking and retries
477
+ * 4. Initiates the scroll attempt process
478
+ *
479
+ * If no suitable container is found, schedules a retry.
480
+ */
481
+ }, {
482
+ key: "executeReliableScroll",
483
+ value: function executeReliableScroll() {
484
+ // Guard: Don't execute scroll if no more history is available
485
+ if (!LazyLoadHandler.hasMoreHistoryAvailable) {
486
+ LazyLoadHandler.finishScrollAction();
487
+ return;
488
+ }
489
+
490
+ // Find container using multiple fallback strategies
491
+ const {
492
+ container,
493
+ isScrollable
494
+ } = LazyLoadHandler.findScrollContainer();
495
+ if (!container || !isScrollable) {
496
+ LazyLoadHandler.scheduleScrollRetry();
497
+ return;
498
+ }
499
+
500
+ // Calculate scroll positions
501
+ const initialScrollTop = container.scrollTop;
502
+ const targetScrollTop = initialScrollTop + 35; // 35px down to maintain position
503
+
504
+ // Set up scroll state for tracking and retries
505
+ LazyLoadHandler.scrollState = {
506
+ container,
507
+ initialScrollTop,
508
+ targetScrollTop,
509
+ attemptCount: 0,
510
+ maxAttempts: 5 // Allow up to 5 retry attempts
511
+ };
512
+
513
+ // Begin the scroll attempt process
514
+ LazyLoadHandler.attemptScroll();
515
+ }
516
+
517
+ /**
518
+ * Attempts to perform scroll with verification and retry logic
519
+ *
520
+ * This method implements a sophisticated scroll system:
521
+ *
522
+ * 1. Uses requestAnimationFrame for smooth execution
523
+ * 2. Performs the scroll operation
524
+ * 3. Verifies scroll actually occurred (5px tolerance)
525
+ * 4. Retries with exponential backoff if failed
526
+ * 5. Continues operation after max attempts
527
+ *
528
+ * The two-frame approach ensures scroll is applied and then verified
529
+ * after the browser has had time to process the scroll change.
530
+ */
531
+ }, {
532
+ key: "attemptScroll",
533
+ value: function attemptScroll() {
534
+ // Guard: Don't attempt scroll if no more history is available
535
+ if (!LazyLoadHandler.hasMoreHistoryAvailable) {
536
+ LazyLoadHandler.finishScrollAction();
537
+ return;
538
+ }
539
+ if (!LazyLoadHandler.scrollState) {
540
+ LazyLoadHandler.finishScrollAction();
541
+ return;
542
+ }
543
+
544
+ // Extract current scroll state
545
+ const {
546
+ container,
547
+ targetScrollTop,
548
+ attemptCount,
549
+ maxAttempts
550
+ } = LazyLoadHandler.scrollState;
551
+ LazyLoadHandler.scrollState.attemptCount++;
552
+
553
+ // Perform scroll using requestAnimationFrame for smooth execution
554
+ // Frame 1: Apply the scroll
555
+ requestAnimationFrame(() => {
556
+ // Double-check history availability before applying scroll
557
+ if (!LazyLoadHandler.hasMoreHistoryAvailable) {
558
+ LazyLoadHandler.finishScrollAction();
559
+ return;
560
+ }
561
+ if (container) {
562
+ container.scrollTop = targetScrollTop;
563
+ }
564
+
565
+ // Frame 2: Verify scroll occurred after browser has processed the change
566
+ requestAnimationFrame(() => {
567
+ // Triple-check history availability before verification
568
+ if (!LazyLoadHandler.hasMoreHistoryAvailable) {
569
+ LazyLoadHandler.finishScrollAction();
570
+ return;
571
+ }
572
+ const actualScrollTop = container ? container.scrollTop : 0;
573
+ const scrollSucceeded = Math.abs(actualScrollTop - targetScrollTop) < 5; // 5px tolerance for success
574
+
575
+ if (scrollSucceeded) {
576
+ LazyLoadHandler.finishScrollAction();
577
+ } else if (attemptCount < maxAttempts) {
578
+ // Retry with exponential backoff (100ms * attempt number)
579
+ const timeoutId = window.setTimeout(() => {
580
+ LazyLoadHandler.retryTimeouts.delete(timeoutId);
581
+ LazyLoadHandler.attemptScroll();
582
+ }, 100 * attemptCount); // Exponential backoff
583
+ LazyLoadHandler.retryTimeouts.add(timeoutId);
584
+ } else {
585
+ LazyLoadHandler.finishScrollAction();
586
+ }
587
+ });
588
+ });
589
+ }
590
+
591
+ /**
592
+ * Finishes scroll action and prepares for next cycle
593
+ *
594
+ * Cleans up scroll state and schedules the reset cycle.
595
+ * Uses delays to allow content stabilization before re-enabling
596
+ * the intersection observer for the next lazy load cycle.
597
+ */
598
+ }, {
599
+ key: "finishScrollAction",
600
+ value: function finishScrollAction() {
601
+ // Clean up scroll tracking state
602
+ LazyLoadHandler.scrollState = null;
603
+ LazyLoadHandler.pendingScrollAction = false;
604
+
605
+ // Schedule unpause and reset with delay for content stabilization
606
+ const timeoutId = window.setTimeout(() => {
607
+ LazyLoadHandler.retryTimeouts.delete(timeoutId);
608
+ LazyLoadHandler.paused = false; // Re-enable intersection observer
609
+ LazyLoadHandler.scheduleReset(); // Schedule next cycle reset
610
+ }, 500);
611
+ LazyLoadHandler.retryTimeouts.add(timeoutId);
612
+ }
613
+
614
+ /**
615
+ * Schedules retry for failed scroll operations
616
+ *
617
+ * Used when scroll container is not available or scrollable.
618
+ * Provides a longer delay to allow container to become ready.
619
+ */
620
+ }, {
621
+ key: "scheduleScrollRetry",
622
+ value: function scheduleScrollRetry() {
623
+ // Don't schedule retry if no more history is available
624
+ if (!LazyLoadHandler.hasMoreHistoryAvailable) {
625
+ LazyLoadHandler.finishScrollAction();
626
+ return;
627
+ }
628
+ const timeoutId = window.setTimeout(() => {
629
+ LazyLoadHandler.retryTimeouts.delete(timeoutId);
630
+ // Only retry if we're still in a pending scroll action state and have more history
631
+ if (LazyLoadHandler.pendingScrollAction && LazyLoadHandler.hasMoreHistoryAvailable) {
632
+ LazyLoadHandler.executeReliableScroll();
633
+ }
634
+ }, 1000); // 1 second delay for container readiness
635
+ LazyLoadHandler.retryTimeouts.add(timeoutId);
636
+ }
637
+
638
+ /**
639
+ * Schedules observer reset for next lazy load cycle
640
+ *
641
+ * After a lazy load operation completes, the observer needs to be reset
642
+ * to detect the next time the user scrolls to the top.
643
+ */
644
+ }, {
645
+ key: "scheduleReset",
646
+ value: function scheduleReset() {
647
+ const timeoutId = window.setTimeout(() => {
648
+ LazyLoadHandler.retryTimeouts.delete(timeoutId);
649
+ LazyLoadHandler.reset();
650
+ }, 1000); // 1 second delay before resetting for next cycle
651
+ LazyLoadHandler.retryTimeouts.add(timeoutId);
652
+ }
653
+
654
+ /**
655
+ * Finds the appropriate scroll container using multiple strategies
656
+ *
657
+ * Container Detection Priority:
658
+ * 1. Primary: Specific webchat scroll container (.webchat__basic-transcript__scrollable)
659
+ * 2. Secondary: Widget root container (ms_lcw_webchat_root)
660
+ * 3. Tertiary: Any scrollable parent of the target element
661
+ * 4. Fallback: Best available container (even if not scrollable)
662
+ *
663
+ * Returns both the container and whether it's actually scrollable.
664
+ *
665
+ * @returns Object containing container element and scrollability status
666
+ */
667
+ }, {
668
+ key: "findScrollContainer",
669
+ value: function findScrollContainer() {
670
+ // Primary: Look for the specific webchat scroll container
671
+ const webchatContainer = document.querySelector(LazyLoadActivityConstants.SCROLL_ID);
672
+ if (webchatContainer && LazyLoadHandler.isElementScrollable(webchatContainer)) {
673
+ return {
674
+ container: webchatContainer,
675
+ isScrollable: true
676
+ };
677
+ }
678
+
679
+ // Secondary: Try the root container
680
+ const rootContainer = document.getElementById(LazyLoadHandler.rootId);
681
+ if (rootContainer && LazyLoadHandler.isElementScrollable(rootContainer)) {
682
+ return {
683
+ container: rootContainer,
684
+ isScrollable: true
685
+ };
686
+ }
687
+
688
+ // Tertiary: Find any scrollable parent of the target element
689
+ const targetElement = document.getElementById(LazyLoadHandler.targetId);
690
+ if (targetElement) {
691
+ const scrollableParent = LazyLoadHandler.findScrollableParent(targetElement);
692
+ if (scrollableParent) {
693
+ return {
694
+ container: scrollableParent,
695
+ isScrollable: true
696
+ };
697
+ }
698
+ }
699
+
700
+ // Fallback: Return the best available container even if not scrollable
701
+ // This allows the system to continue functioning in edge cases
702
+ return {
703
+ container: webchatContainer || rootContainer || document.body,
704
+ isScrollable: false
705
+ };
706
+ }
707
+
708
+ /**
709
+ * Determines if an element is scrollable
710
+ *
711
+ * Checks two conditions:
712
+ * 1. Content overflow: scrollHeight > clientHeight (content extends beyond visible area)
713
+ * 2. CSS overflow settings: overflow/overflowY set to 'auto' or 'scroll'
714
+ *
715
+ * @param element - The element to check for scrollability
716
+ * @returns true if element is scrollable, false otherwise
717
+ */
718
+ }, {
719
+ key: "isElementScrollable",
720
+ value: function isElementScrollable(element) {
721
+ const style = window.getComputedStyle(element);
722
+ const hasScrollableContent = element.scrollHeight > element.clientHeight;
723
+ const hasScrollableOverflow = style.overflow === "auto" || style.overflow === "scroll" || style.overflowY === "auto" || style.overflowY === "scroll";
724
+ return hasScrollableContent && hasScrollableOverflow;
725
+ }
726
+
727
+ /**
728
+ * Traverses up the DOM tree to find a scrollable parent element
729
+ *
730
+ * Starting from the given element, walks up the parent chain
731
+ * until it finds a scrollable element or reaches document.body.
732
+ *
733
+ * @param element - Starting element to search from
734
+ * @returns Scrollable parent element or null if none found
735
+ */
736
+ }, {
737
+ key: "findScrollableParent",
738
+ value: function findScrollableParent(element) {
739
+ let parent = element.parentElement;
740
+ while (parent && parent !== document.body) {
741
+ if (LazyLoadHandler.isElementScrollable(parent)) {
742
+ return parent;
743
+ }
744
+ parent = parent.parentElement;
745
+ }
746
+ return null;
747
+ }
748
+
749
+ /**
750
+ * Legacy method - redirects to new reliable scroll system
751
+ *
752
+ * Maintained for backwards compatibility with existing code
753
+ * that might call this method directly.
754
+ */
755
+ }, {
756
+ key: "moveScrollDown",
757
+ value: function moveScrollDown() {
758
+ LazyLoadHandler.executeReliableScroll();
759
+ }
760
+
761
+ /**
762
+ * Legacy scroll adjustment method with enhancements
763
+ *
764
+ * Enhanced version of the original adjustScroll method:
765
+ * - Validates container scrollability before attempting scroll
766
+ * - Uses requestAnimationFrame for smooth execution
767
+ * - Provides logging for debugging
768
+ *
769
+ * @param scrollContainer - The container element to scroll
770
+ */
771
+ }, {
772
+ key: "adjustScroll",
773
+ value: function adjustScroll(scrollContainer) {
774
+ // Validate container is scrollable before attempting scroll
775
+ if (!LazyLoadHandler.isElementScrollable(scrollContainer)) {
776
+ return;
777
+ }
778
+ const currentScrollTop = scrollContainer.scrollTop;
779
+ const moveDownBy = 35; // Standard scroll adjustment amount
780
+ const targetScrollTop = currentScrollTop + moveDownBy;
781
+
782
+ // Use requestAnimationFrame for smooth scroll execution
783
+ requestAnimationFrame(() => {
784
+ scrollContainer.scrollTop = targetScrollTop;
785
+ });
786
+ }
787
+
788
+ /**
789
+ * Handles the NO_MORE_HISTORY_AVAILABLE event
790
+ *
791
+ * Called when there's no more chat history to load.
792
+ * Disables further lazy loading attempts and cleans up the observer.
793
+ * Also removes the trigger element from the DOM to prevent further triggering.
794
+ */
795
+ }, {
796
+ key: "handleNoMoreHistoryAvailable",
797
+ value: function handleNoMoreHistoryAvailable() {
798
+ LazyLoadHandler.setHasMoreHistoryAvailable(false);
799
+ LazyLoadHandler.paused = true;
800
+ LazyLoadHandler.pendingScrollAction = false; // Reset this to prevent stuck states
801
+
802
+ LazyLoadHandler.logLifecycleEvent(TelemetryEvent.LCWLazyLoadNoMoreHistory, "No more history available");
803
+
804
+ // Clear all pending timeouts to stop any scheduled operations
805
+ LazyLoadHandler.retryTimeouts.forEach(timeoutId => {
806
+ clearTimeout(timeoutId);
807
+ });
808
+ LazyLoadHandler.retryTimeouts.clear();
809
+
810
+ // Disconnect observer to prevent further triggering
811
+ if (LazyLoadHandler.observer) {
812
+ LazyLoadHandler.observer.disconnect();
813
+ LazyLoadHandler.observer = null;
814
+ }
815
+
816
+ // Clear scroll state
817
+ LazyLoadHandler.scrollState = null;
818
+ }
819
+
820
+ /**
821
+ * Resets the lazy load system for the next cycle
822
+ *
823
+ * This method prepares the system for the next lazy load operation:
824
+ * 1. Cleans up current state
825
+ * 2. Resets all flags and queues
826
+ * 3. Reinitializes the observer with faster timing
827
+ *
828
+ * Called after each lazy load cycle completes.
829
+ */
830
+ }, {
831
+ key: "reset",
832
+ value: function reset() {
833
+ LazyLoadHandler.unmount(); // Clean up current state
834
+ LazyLoadHandler.initialized = false; // Reset initialization flag
835
+ LazyLoadHandler.isReady = false; // Reset readiness flag
836
+ LazyLoadHandler.setHasMoreHistoryAvailable(true); // Reset history availability flag
837
+ LazyLoadHandler.initializationQueue = []; // Clear action queue
838
+ LazyLoadHandler.resetPending = false; // Clear pending reset flag
839
+
840
+ // Reinitialize with faster timing for better responsiveness
841
+ const timeoutId = window.setTimeout(() => {
842
+ LazyLoadHandler.retryTimeouts.delete(timeoutId);
843
+ LazyLoadHandler.useLazyLoadObserver();
844
+ }, 25); // Reduced from 50ms to 25ms for faster reset
845
+ LazyLoadHandler.retryTimeouts.add(timeoutId);
846
+ }
847
+
848
+ /**
849
+ * Comprehensive cleanup method
850
+ *
851
+ * Performs complete cleanup of all lazy load resources:
852
+ * - Clears all pending timeouts to prevent memory leaks
853
+ * - Removes intersection observer and event listeners
854
+ * - Resets all state variables to initial values
855
+ * - Clears action queue
856
+ *
857
+ * Critical for preventing memory leaks and ensuring clean component unmounting.
858
+ */
859
+ }, {
860
+ key: "unmount",
861
+ value: function unmount() {
862
+ LazyLoadHandler.retryTimeouts.forEach(timeoutId => {
863
+ clearTimeout(timeoutId);
864
+ });
865
+ LazyLoadHandler.retryTimeouts.clear();
866
+
867
+ // Clean up intersection observer
868
+ const targetElement = document.getElementById(LazyLoadHandler.targetId);
869
+ if (targetElement && LazyLoadHandler.observer) {
870
+ LazyLoadHandler.observer.unobserve(targetElement);
871
+ }
872
+
873
+ // Reset all state variables to initial values
874
+ LazyLoadHandler.observer = null;
875
+ LazyLoadHandler.initialized = false;
876
+ LazyLoadHandler.paused = false;
877
+ LazyLoadHandler.pendingScrollAction = false;
878
+ LazyLoadHandler.scrollState = null;
879
+ LazyLoadHandler.isReady = false;
880
+ LazyLoadHandler.initializationQueue = [];
881
+ // Note: Don't reset resetPending here as it needs to persist across unmount/mount cycles
882
+ }
883
+
884
+ /**
885
+ * Complete cleanup including broadcast event listener
886
+ *
887
+ * This method is used for final cleanup when the LazyLoadActivity component is being destroyed completely.
888
+ * It includes unsubscribing from broadcast events and performing complete cleanup.
889
+ * This is different from reset() which prepares the system for a new chat session.
890
+ */
891
+ }, {
892
+ key: "destroy",
893
+ value: function destroy() {
894
+ LazyLoadHandler.logLifecycleEvent(TelemetryEvent.LCWLazyLoadDestroyed, "LazyLoad component destroyed");
895
+ LazyLoadHandler.unmount();
896
+
897
+ // Clean up broadcast event subscription
898
+ if (LazyLoadHandler.resetEventListener) {
899
+ LazyLoadHandler.resetEventListener.unsubscribe();
900
+ }
901
+ }
902
+ }]);
903
+ return LazyLoadHandler;
904
+ }();
905
+ /**
906
+ * LazyLoadActivity React Component
907
+ *
908
+ * This component serves as the React wrapper for the LazyLoadHandler system.
909
+ * It renders the trigger element and manages the component lifecycle.
910
+ *
911
+ * Component Lifecycle:
912
+ * 1. Mount: Initializes lazy load observer and sets up scroll listener
913
+ * 2. Active: Monitors user scroll behavior and responds to interactions
914
+ * 3. Unmount: Cleans up all resources and prevents memory leaks
915
+ *
916
+ * Key Features:
917
+ * - Automatic initialization on mount
918
+ * - Scroll event monitoring for immediate responsiveness
919
+ * - Proper cleanup on unmount
920
+ * - Handles minimize/maximize scenarios
921
+ * - Reactive rendering based on history availability
922
+ */
923
+ _defineProperty(LazyLoadHandler, "rootId", "ms_lcw_webchat_root");
924
+ _defineProperty(LazyLoadHandler, "targetId", "lazy-load-trigger-element");
925
+ _defineProperty(LazyLoadHandler, "initialized", false);
926
+ _defineProperty(LazyLoadHandler, "paused", false);
927
+ _defineProperty(LazyLoadHandler, "observer", null);
928
+ _defineProperty(LazyLoadHandler, "scrollState", null);
929
+ _defineProperty(LazyLoadHandler, "pendingScrollAction", false);
930
+ _defineProperty(LazyLoadHandler, "retryTimeouts", new Set());
931
+ _defineProperty(LazyLoadHandler, "resetPending", false);
932
+ _defineProperty(LazyLoadHandler, "initTimer", createTimer());
933
+ _defineProperty(LazyLoadHandler, "resetEventListener", BroadcastService.getMessageByEventName(BroadcastEvent.PersistentConversationReset).subscribe(() => {
934
+ LazyLoadHandler.logLifecycleEvent(TelemetryEvent.LCWLazyLoadReset, "LazyLoad reset triggered");
935
+ LazyLoadHandler.resetPending = true;
936
+ LazyLoadHandler.setHasMoreHistoryAvailable(true); // Reset this immediately so activityMiddleware doesn't block rendering
937
+ LazyLoadHandler.unmount(); // Clean up current state immediately
938
+ }));
939
+ // Readiness and queue system (handles minimize/maximize scenarios)
940
+ _defineProperty(LazyLoadHandler, "isReady", false);
941
+ // System readiness flag
942
+ _defineProperty(LazyLoadHandler, "initializationQueue", []);
943
+ // Queue for actions during initialization
944
+ // History availability state
945
+ _defineProperty(LazyLoadHandler, "hasMoreHistoryAvailable", true);
946
+ const LazyLoadActivity = props => {
947
+ const [hasMoreHistory, setHasMoreHistory] = useState(LazyLoadHandler.hasMoreHistoryAvailable);
948
+ useEffect(() => {
949
+ LazyLoadHandler.logLifecycleEvent(TelemetryEvent.LCWLazyLoadActivityMounted, "LazyLoadActivity component mounted");
950
+
951
+ // Set up event listeners FIRST - before any early returns
952
+ // Event listener for NO_MORE_HISTORY_AVAILABLE
953
+ const handleNoMoreHistory = () => {
954
+ LazyLoadHandler.handleNoMoreHistoryAvailable();
955
+ // Update React state to trigger re-render and hide component
956
+ setHasMoreHistory(false);
957
+ };
958
+
959
+ // Event listener for PersistentConversationReset to sync React state
960
+ // This fixes the issue where banner doesn't appear in start chat + close chat + start chat sequence
961
+ // by ensuring React state (hasMoreHistory) is synchronized with handler state when reset occurs
962
+ const handlePersistentConversationReset = () => {
963
+ // Update React state to trigger re-render and show component for new chat session
964
+ setHasMoreHistory(true);
965
+ };
966
+
967
+ // Add secure event listener for no more history signal
968
+ const eventBus = SecureEventBus.getInstance();
969
+ const unsubscribeNoMoreHistory = eventBus.subscribe(ChatWidgetEvents.NO_MORE_HISTORY_AVAILABLE, handleNoMoreHistory);
970
+
971
+ // Add event listener for persistent conversation reset
972
+ const resetSubscription = BroadcastService.getMessageByEventName(BroadcastEvent.PersistentConversationReset).subscribe(handlePersistentConversationReset);
973
+
974
+ // Sync React state with handler state on mount in case they're out of sync
975
+ if (hasMoreHistory !== LazyLoadHandler.hasMoreHistoryAvailable) {
976
+ setHasMoreHistory(LazyLoadHandler.hasMoreHistoryAvailable);
977
+ }
978
+
979
+ // Check if a reset was pending from a previous chat session
980
+ if (LazyLoadHandler.resetPending) {
981
+ LazyLoadHandler.resetPending = false;
982
+ LazyLoadHandler.reset();
983
+
984
+ // Still need to return cleanup function even after reset
985
+ return () => {
986
+ unsubscribeNoMoreHistory();
987
+ resetSubscription.unsubscribe();
988
+ };
989
+ }
990
+
991
+ // Initialize the lazy load observer system
992
+ LazyLoadHandler.useLazyLoadObserver();
993
+
994
+ // Pause initially to prevent unexpected triggers during initialization
995
+ LazyLoadHandler.paused = true;
996
+
997
+ // Set up initialization completion with visibility check
998
+ const initTimeoutId = window.setTimeout(() => {
999
+ LazyLoadHandler.paused = false; // Enable lazy loading
1000
+
1001
+ // Check if target is already visible after initialization
1002
+ // This handles cases where user scrolled during initialization
1003
+ LazyLoadHandler.checkVisibilityAndTrigger();
1004
+ }, 200); // Reduced from 500ms to 200ms for faster initial load
1005
+
1006
+ /**
1007
+ * Scroll Event Handler for Immediate Responsiveness
1008
+ *
1009
+ * This listener provides immediate feedback when users scroll
1010
+ * before the system is fully ready (e.g., after minimize/maximize).
1011
+ *
1012
+ * Uses passive event listener for better performance.
1013
+ */
1014
+ const handleScroll = () => {
1015
+ if (!LazyLoadHandler.isReady) {
1016
+ // System not ready, but user is scrolling - check if we should trigger
1017
+ window.setTimeout(() => {
1018
+ LazyLoadHandler.checkVisibilityAndTrigger();
1019
+ }, 50); // Reduced from 100ms to 50ms for faster response
1020
+ }
1021
+ };
1022
+
1023
+ // Find container and attach scroll listener
1024
+ const {
1025
+ container
1026
+ } = LazyLoadHandler.findScrollContainer();
1027
+ if (container) {
1028
+ container.addEventListener("scroll", handleScroll, {
1029
+ passive: true
1030
+ });
1031
+ }
1032
+
1033
+ // Cleanup function - critical for preventing memory leaks
1034
+ return () => {
1035
+ clearTimeout(initTimeoutId);
1036
+
1037
+ // Remove event listeners
1038
+ unsubscribeNoMoreHistory();
1039
+ resetSubscription.unsubscribe();
1040
+ if (container) {
1041
+ container.removeEventListener("scroll", handleScroll);
1042
+ }
1043
+
1044
+ // Perform complete system cleanup including broadcast event listener
1045
+ LazyLoadHandler.destroy();
1046
+ };
1047
+ }, []); // Empty dependency array - only run on mount/unmount
1048
+
1049
+ // Don't render if no more history is available
1050
+ if (!hasMoreHistory) {
1051
+ return null;
1052
+ }
1053
+
1054
+ // Render the trigger element that the intersection observer watches
1055
+ return /*#__PURE__*/React.createElement(LoadInlineBannerActivity, _extends({}, props || {}, {
1056
+ id: LazyLoadHandler.targetId
1057
+ }));
1058
+ };
1059
+ export default LazyLoadActivity;
1060
+ export { LazyLoadHandler };