Recently we launched a chatbot along with a new chat UI in N26 for Web.
The chatting behaviour of bot and human differs a lot. This difference is exposed not only in natural language understanding, but in other aspects as well.
We know that there should be a pause between messages to make it easier for the user to read them. This pause is created naturally when another person is typing.
Unfortunately, the chatbot doesn’t need time for typing and send all messages in a row almost at the same time.
For better UX the solution would be to fake that the bot is typing.
Before we implemented this feature, when a new message was received it was stored in the state of the Chat component. As a new message got added to the state, the component got updated and a new message element was rendered in the view. All messages received in a row were displayed almost simultaneously. In the fake typing scenario we simulate a delay before adding a new message to the state. During this delay, we display a typing animation to show to the user that a new message will come soon.
We fiddled with a few solutions, and here is the one we came up with: use
setTimeout method to create a delay before adding a message to the state. However, in case several messages are received in a row, we need to wait until the previous message is added to the state and only after that run timeout for the next one. Therefore, we should store a queue of incoming messages (outside of the state to avoid unnecessary re-renders).
When a new message is received, we initiate a timeout to add it to the state, during which we show a typing animation. The duration of the timeout is based on the message length and typing speed. Aiming to bring more humanity to the chatbot we assumed it has fast typing speed with average of 100 words per minute. However, taking into account reading speed, the typing should not take longer than 3 seconds.
Instead of running message queue in a loop, for each received message we increase timeout duration by previous message timeout duration. In the callback of
setTimeout we remove a message from the queue and add it to the state. If there is no more messages in the queue, we reset duration to 0 and stop typing animation.
The implementation took just 30 lines of code, however positive UX impact is huge. The chat feels smooth, more natural and easier to read.