Skip to content

Commit c4c1d7f

Browse files
committed
Fix initial scroll position
1 parent f0e719d commit c4c1d7f

4 files changed

Lines changed: 61 additions & 14 deletions

File tree

‎lhc_web/design/defaulttheme/js/widgetv2/react.app.js‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎lhc_web/design/defaulttheme/js/widgetv2/react.app.js.map‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎lhc_web/design/defaulttheme/widget/react-app/src/components/StartChat.js‎

Lines changed: 58 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class StartChat extends Component {
4848
this.apiLoaded = false;
4949
this.customHTMLPriority = false;
5050

51-
this.state = {showBBCode : null, Question:'', changeLanguage: false, hasBotData : false, textAreaHidden: false, voiceMode: false};
51+
this.state = {showBBCode : null, Question:'', changeLanguage: false, hasBotData : false, textAreaHidden: false, voiceMode: false, messagesReady: false};
5252
this.botPayload = null;
5353
this.handleSubmit = this.handleSubmit.bind(this);
5454
this.enterKeyDown = this.enterKeyDown.bind(this);
@@ -329,6 +329,7 @@ class StartChat extends Component {
329329
componentDidMount() {
330330
helperFunctions.prefillFields(this);
331331
this.updateOnlineFields();
332+
this.applyInitialScroll();
332333

333334
if (this.props.botPayload !== null) {
334335
// This is required because prefill fields can be one of the required
@@ -423,15 +424,20 @@ class StartChat extends Component {
423424
}
424425

425426
// Auto focus if it's show operation
426-
if ((needFocus === true || (this.props.chatwidget.get('isMobile') == false && prevProps.chatwidget.get('shown') === false && this.props.chatwidget.get('shown') === true)) && this.props.chatwidget.get('mode') == 'widget' && this.textMessageRef.current) {
427-
this.textMessageRef.current.focus();
428-
this.scrollBottom();
427+
if ((needFocus === true || (/*this.props.chatwidget.get('isMobile') == false &&*/ prevProps.chatwidget.get('shown') === false && this.props.chatwidget.get('shown') === true)) && this.props.chatwidget.get('mode') == 'widget' && this.textMessageRef.current) {
428+
if (this.props.chatwidget.get('isMobile') == false) {
429+
this.textMessageRef.current.focus();
430+
}
431+
this.scrollToPreviousOrFirst();
429432
}
430433

431434
// Rest API data was fetched we can scroll to bottomnow
432435
if (this.props.chatwidget.getIn(['onlineData','fetched']) === true && prevProps.chatwidget.getIn(['onlineData','fetched']) === false) {
433436
this.props.chatwidget.hasIn(['chat_ui','uprev']) && helperFunctions.emitEvent('play_sound', [{'type' : 'new_message','sound_on' : (this.props.chatwidget.getIn(['usersettings','soundOn']) === true), 'widget_open' : ((this.props.chatwidget.get('shown') && this.props.chatwidget.get('mode') == 'widget') || document.hasFocus())}]);
434-
this.scrollBottom();
437+
if (this.props.chatwidget.get('shown') === true){
438+
this.scrollToPreviousOrFirst();
439+
}
440+
435441
}
436442

437443
// If parent pages changes default department we have to reload
@@ -443,16 +449,55 @@ class StartChat extends Component {
443449
}
444450
this.updateOnlineFieldsInit();
445451
}
452+
453+
this.applyInitialScroll();
454+
}
455+
456+
// The widget lives inside an iframe which the parent page makes visible before the
457+
// `widgetStatus` event reaches the chat. Our scroll adjustment therefore used to run
458+
// a frame later and the visitor briefly saw the messages at the top before they were
459+
// scrolled to the correct position. Keep them hidden until we positioned them once.
460+
applyInitialScroll() {
461+
if (this.state.messagesReady === true || !this.messagesAreaRef.current) {
462+
return;
463+
}
464+
465+
// In widget mode the iframe can already be visible before the open event reaches us.
466+
if (this.props.chatwidget.get('mode') == 'widget' && this.props.chatwidget.get('shown') !== true) {
467+
return;
468+
}
469+
470+
this.scrollToPreviousOrFirst();
471+
this.setState({messagesReady: true});
446472
}
447473

448474
scrollBottom() {
449475
if (this.messagesAreaRef.current) {
450476
this.messagesAreaRef.current.scrollTop = this.messagesAreaRef.current.scrollHeight + 1000;
451-
setTimeout(() => {
452-
if (this.messagesAreaRef.current) {
453-
this.messagesAreaRef.current.scrollTop = this.messagesAreaRef.current.scrollHeight + 1000;
454-
}
455-
},450);
477+
}
478+
}
479+
480+
scrollToPreviousOrFirst() {
481+
const container = this.messagesAreaRef.current;
482+
483+
if (!container) {
484+
return;
485+
}
486+
487+
const previousHolder = container.querySelector('.previous-holder');
488+
const targetElement = previousHolder || container.querySelector('[id="msg-"]');
489+
490+
if (targetElement) {
491+
// Adjust the scroll position of our own messages container only.
492+
// scrollIntoView() would also scroll every ancestor scroll box (including the
493+
// parent page when the widget is embedded) and respects `scroll-behavior: smooth`,
494+
// which caused a visible flick while the widget was opened for the first time.
495+
// Direct `scrollTop` assignment is always instant and never bubbles to ancestors.
496+
const containerRect = container.getBoundingClientRect();
497+
const targetRect = targetElement.getBoundingClientRect();
498+
container.scrollTop += (targetRect.top - containerRect.top);
499+
} else {
500+
this.scrollBottom();
456501
}
457502
}
458503

@@ -604,7 +649,9 @@ class StartChat extends Component {
604649
var classMessageInput = (!this.props.chatwidget.hasIn(['chat_ui','bbc_btnh']) || this.props.chatwidget.hasIn(['chat_ui','lng_btnh']) ? 'ps-0' : 'ps-2')+" no-outline form-control rounded-0 form-control rounded-start-0 rounded-end-0 border-0";
605650

606651
var msg_expand = "flex-grow-1 overflow-scroll position-relative";
607-
var bottom_messages = "bottom-message px-1";
652+
// Bootstrap `invisible` utility is used to hide the messages until their
653+
// initial scroll position is applied - see applyInitialScroll().
654+
var bottom_messages = "bottom-message px-1" + (this.state.messagesReady === true ? '' : ' invisible');
608655

609656
if (this.props.chatwidget.hasIn(['chat_ui','msg_expand']) && this.props.chatwidget.get('mode') == 'embed') {
610657
msg_expand = "overflow-scroll position-relative";

‎lhc_web/modules/lhwidgetrestapi/settings.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@
581581
$outputResponse['wv'] = 279;
582582

583583
// React APP versions
584-
$outputResponse['v'] = 427;
584+
$outputResponse['v'] = 428;
585585

586586
$cfg = erConfigClassLhConfig::getInstance();
587587

0 commit comments

Comments
 (0)