Skip to content

Commit a686d32

Browse files
boehlerlukasclaude
andcommitted
perf: survey sheet keyboard handler runs at most once per frame and does nothing unless a card survey sheet is lifted
visualViewport scroll/resize fire many times a second while any page with the SDK scrolls; every event touched the frame container's styles even with no survey on screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent ec28226 commit a686d32

1 file changed

Lines changed: 20 additions & 2 deletions

File tree

‎src/GleapFrameManager.js‎

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ export default class GleapFrameManager {
4141
// The end user's expand/collapse choice (undefined = not read from the cache yet).
4242
widgetExpanded = undefined;
4343
surveyKeyboard = false;
44+
// The card survey sheet is lifted above the on-screen keyboard (updateSurveyKeyboardInset).
45+
surveySheetLifted = false;
4446
appliedWidgetExpanded = false;
4547
lastWidgetSizeUpdate = null;
4648
// Hidden while the customer answers a capture request (see setCaptureHidden).
@@ -98,7 +100,16 @@ export default class GleapFrameManager {
98100
// Phones: keep the card survey sheet above the on-screen keyboard (iOS Safari
99101
// draws the keyboard over fixed elements; visualViewport shrinks instead).
100102
if (window.visualViewport) {
101-
const onViewport = () => this.updateSurveyKeyboardInset();
103+
// visualViewport fires scroll/resize many times a second while any page scrolls:
104+
// at most one update per frame (and it returns early unless a card survey sheet is up).
105+
let viewportFrame = null;
106+
const onViewport = () => {
107+
if (viewportFrame !== null) return;
108+
viewportFrame = window.requestAnimationFrame(() => {
109+
viewportFrame = null;
110+
this.updateSurveyKeyboardInset();
111+
});
112+
};
102113
window.visualViewport.addEventListener('resize', onViewport);
103114
window.visualViewport.addEventListener('scroll', onViewport);
104115
}
@@ -282,11 +293,16 @@ export default class GleapFrameManager {
282293
if (!container) {
283294
return;
284295
}
285-
const inner = container.querySelector('.gleap-frame-container-inner');
286296
const vv = window.visualViewport;
287297
const isSheet = this.appMode === 'survey' && !this.surveyLegacy && window.innerWidth <= 450;
288298
const keyboard = vv ? Math.max(0, window.innerHeight - (vv.height + vv.offsetTop)) : 0;
289299
if (!isSheet || !vv || keyboard < 80) {
300+
// Nothing lifted: nothing to undo (the common case, on every scroll of every page).
301+
if (!this.surveySheetLifted) {
302+
return;
303+
}
304+
this.surveySheetLifted = false;
305+
const inner = container.querySelector('.gleap-frame-container-inner');
290306
container.style.removeProperty('bottom');
291307
container.style.removeProperty('max-height');
292308
if (inner) {
@@ -295,7 +311,9 @@ export default class GleapFrameManager {
295311
this.sendSheetViewport(0, false);
296312
return;
297313
}
314+
const inner = container.querySelector('.gleap-frame-container-inner');
298315
const visible = Math.max(160, vv.height - 24);
316+
this.surveySheetLifted = true;
299317
container.style.setProperty('bottom', keyboard + 8 + 'px', 'important');
300318
container.style.setProperty('max-height', visible + 'px', 'important');
301319
if (inner) {

0 commit comments

Comments
 (0)