So, recently we’ve released the first Beta version of Planga, a Seamless Chat Integration Service that you can use to add live chat to your application. It will use your users and your defined channels, but other than that, it will get out of the way of your application, so that you do not have to worry about the messy details of building a scalable, feature-rich chat system at all.
Planga’s back-end is built using Elixir, and the front end is currently a very simple JavaScript class.
Needless to say, because this front-end interface is going to be built upon a lot in the future, we decided that it was time to re-build the initial JS version into an Elm application.
- Source code of current JavaScript version.
- Repo of Elm app that will replace it.
- Planga homepage with example chat to see it in action.
Long story short: The Elm app almost has reached feature parity with the JS version. The only thing that is still missing and non-trivial to add, is proper handling of the scrollbar.
This is the desired behaviour (this is very common, natural and expected behaviour for chat interfaces):
- When a person scrolls to the top, older messages should be loaded. Once they are added to the interface, the scrollbar should remain at the same distance from the bottom of the chat window. (in JS terms,
newScrollTop = newScrollHeight - (oldScrollHeight - oldScrollTop)
. See the lines in the JS code that does this: The user should be able to scroll further to the top now, but still see the same messages as before. - When the scrollbar is near or at the bottom, whenever a new message arrives, the scrollbar should move all the way to the bottom, so the message is visible without having to manually scroll.
However, I cannot figure out a way to do this with Elm, even with ports, because the code in a port has no idea when Elm has updated its (V)DOM (and then the real DOM), so it cannot properly add listeners and respond to changes in the view: We need to both listen to scroll events, as well as being aware of the current scroll position and inner height (the scrollHeight) of the element rendered by Elm.
The couple of libraries out there that let Elm immediately react to scroll events only work with the scrolling of the window not of an element that is rendered by Elm itself. (in this case: the list of chat messages).
IIRC Elm 0.19 has ways to set up scroll listeners to elements. However, because the chat application uses Phoenix and websockets, we’re stuck on 0.18 for now.
So, we’ve reached the point where it seems reasonable to dig into the internals and write some Native code to fix this problem (until we can migrate over to 0.19 and throw it away). Any solution that does not involve setTimeout/setInterval over ports (because that stuff is highly dependent on how fast a device is able to render and therefore not portable and prone to race conditions) is accepted.
We would be very grateful for your help.