Web messengers and the 'beforeunload' event: how to save a million messages when closing a page
In late summer, we addedto our cloud Voximplant messaging support. Now, using it and scattering SDK for different platforms, you can make your own mobile or web messengers: there are voice calls in any combination between telephone networks and SDK, there are video calls between SDK, there is messaging. And text messages have a key difference from voice and video calls: their content should remain. Voximplant can record a voice and video call on the cloud side and give the URL with the resulting file, but this is a “slow” story for CRM, order management systems and call centers. And messages are a quick story. The user is very upset when a click on the "old" chat in Skype causes a mobile or web application to freeze, which is trying to download at least some history from loaded servers via unstable 3G.
What, in fact, is the problem?
A new messenger user starts with a single Messenger object that gives access to the API and allows you to receive events. Communication between users begins when one of them creates a Conversation object (“conversation” or “chat” for two or more) and they begin to exchange messages using the sendMessage object method . Clients will learn about events through events. For example, if user “A” wants to send a message to user “B” for the first time, then he creates a conversation for two, after which the CreateConversation event comes to both of themby which user “B” finds out that they want to communicate with him. Also, events signal new messages joining conversations and users leaving them, changing admin status, or that the user is typing text.
All this idyll lasts exactly until one of the users closes the chat tab. And does not open it again every other day. Or in a month. Or in a year. What does the developer need to do so that the user sees everything new that happened during his absence? And preferably so as not to suspend the browser.
Serialization and Numbering - Two Whales of Message History
The main detail of the mechanism is the sequential numbering of all messages in the conversation. The SendMessage event has a seq field that contains a unique identifier for the message. The identifier is unique within the conversation and is constantly increasing. Accordingly, if we closed the browser page, opened it a year later and want to find out what new messages arrived during this time, all we need to do is to store somewhere the sequence id of the last message received, and after opening the page, request the missing messages from the cloud. Or, for example, the last few dozen, and load the rest only if the user decided to see the log.
An auxiliary detail is serialization. SDK is high level and works with objects. For example, if we want to get new messages for conversation, first we need to get an object for this conversation using getConversation , and then messages using the method of this object, retransmitEvents
But if we just loaded the page, where do we get the objects from? We have a bunch of id's that are prudently stored in localStorage. And the objects will have to be created, and each such creation of an object is a request to the cloud to obtain the necessary information.
The solution is a built-in mechanism for serializing objects using the toCache and create ... FromCache methodswhich create a JSON representation of the internals of the object and can restore the object from such JSON without accessing the server. And JSON can be stored in localStorage, instantly recovering a hundred channels and a million messages when a page loads.
With web pages, unlike desktop and mobile applications, everything is complicated. When the user commands “close the tab” or “close the browser”, the “beforeunload” event is triggered, which you can subscribe to. The message "you have unsaved data" in google docs is a string that the developer returned from the handler. Previously, it was possible to make even alerts in it, but the scam pages “your browser is locked, give money” gently hinted to browser developers that there wasn’t much to allow in the “beforeunload” handler.
However, modern browsers give our code a few seconds before they show such a message and the user starts to worry:
And in a few seconds it is quite possible to serialize several hundred conversations with a million messages in localstorage. But here it is important to remember that by default localStorage is limited to 5-10 megabytes, and even less for mobile browsers or if the user delved into the settings.
Best practices for nothing to burst
If you are making a new “Skype for Web” and planning a really large number of messages from your users, then it is better to use indexedDB, which all popular browsers support, to store serialized objects. The default quotas are much larger and you can explicitly ask the user even using the “Quota Management API” and specific browser pieces.
The second point - if in some conversation from the last visit a lot of messages accumulated, it would be reasonable to ask the server for the last few tens, and load the rest only if the user scrolled the log. It turns out a kind of “reverse infinite scroll” - new elements will not appear from below, as when scrolling a Facebook page, but from above.
This year we plan to significantly expand our messaging, adding control via HTTP and webhooks. This will allow developers to integrate with other instant messengers, program message management like “chat with operators” and other interesting things.