Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scroll to top of a bubble in botframework webchat

Some answers of our chatbot are very long. The webchat scrolls automatically to the bottom so users have to scroll up to get to the top of the bubble and start reading.

I've implemented a custom renderer (react) to wrap the answers into a custom component which simply wraps the answer into a div-tag. I also implemented a simple piece of code to scroll to the top of the bubble.

const MyCustomActivityContainer = ({ children }) => {
    const triggerScrollTo = () => {
        if (scrollRef && scrollRef.current) {
            (scrollRef.current as any).scrollIntoView({
                behavior: 'smooth',
                block: 'start',
            })
        }
    }

    const scrollRef: React.RefObject<HTMLDivElement> = React.createRef()

    return (
        <div ref={ scrollRef } onClick={ triggerScrollTo }>
            { children }
        </div>
    )
}

export const activityMiddleware = () => next => card => {
    if (/* some conditions */) {
        return (
            <MyCustomActivityContainer>
                { next(card) }
            </MyCustomActivityContainer>
        );
    } else {
        return (
            { next(card) }
        )
    }
};

But this only works if the scrollbar slider is not at its lowest position (there is at least 1 pixel left to scroll down, see here). The problem is the useScrollToBottom hook which always scrolls to bottom automatically if the scrollbar is completely scrolled down.

Is there any way to overwrite the scroll behavior or to temporarily disable the scrollToBottom feature?

like image 366
MrToast Avatar asked Apr 03 '20 12:04

MrToast


2 Answers

As there is no reproducible example I can only guess.
And I'll have to make some guesses on the question too.
Because it's not clear what exactly in not working:

  1. Do you mean that click on the <div> of MyCustomActivityContainer and subsequent call to triggerScrollTo doesn't result into a scroll? That would be strange, but who knows. In this case I doubt anyone will help you without reproducible example.
  2. Or do you mean that you can scroll the message into view, but if it is already in the view then new messages can result into a scroll while user is still reading a message.
    That's so, but it contradicts with you statement that your messages are very long, because that would be the problem with short messages, not with the long ones.

    But anyway, you should be able to fix that.
    If it works fine with 1 pixel off the lowest position, then just scroll that 1 pixel. You'll need to find the scrollable element. And do scrollable_element.scrollTop -= 1. I tested this approach here. And it worked (there the scrollable element is the grandparent of <p>'s)

  3. Or do you try to scroll automatically at the moment the message arrives? Аnd that is the real issue, but you forgot to mention it, and didn't posted the code that tries to auto-scroll?

    In that case you can try to use setTimeout() and defer the scroll by, let's say, 200ms. This number in based on what I gathered from the source:

    1. BotFramework-WebChat uses react-scroll-to-bottom
    2. In react-scroll-to-bottom there are some timeouts 100ms and 34ms
    3. BotFramework-WebChat doesn't redefine them
    4. There are some heuristics in react-scroll-to-bottom that probably coursing the trouble https://github.com/compulim/react-scroll-to-bottom/blob/3eb21bc469ee5f5095a431ac584be29a0d2da950/packages/component/src/ScrollToBottom/Composer.js

      Currently, there are no reliable way to check if the "scroll" event is trigger due to user gesture, programmatic scrolling, or Chrome-synthesized "scroll" event to compensate size change. Thus, we use our best-effort to guess if it is triggered by user gesture, and disable sticky if it is heading towards the start direction.

      And
      https://github.com/compulim/react-scroll-to-bottom/blob/f19b14d6db63dcb07ffa45b4433e72284a9d53b6/packages/component/src/ScrollToBottom/Composer.js#L91

      For what we observed, #1 is fired about 20ms before #2. There is a chance that this stickyCheckTimeout is being scheduled between 1 and 2. That means, if we just look at #1 to decide if we should scroll, we will always scroll, in oppose to the user's intention.

      That's why I think you should use setTimeout()

like image 100
x00 Avatar answered Oct 19 '22 01:10

x00


Since there isn't a reproducible code for me tweak and show you. My suggestion is tweak your code slightly. Chatbot requires constant streaming of data when a new message arrives calculate the height of the div element created for the message. If the div element is greater than the widget height scroll to the top else you can choose to leave it as it is.

like image 2
Prashanth M Avatar answered Oct 19 '22 02:10

Prashanth M