How to access the previous item in a LiveView stream?

Is there a way when rendering items in LiveView streams to access the previous item in the stream?

To make this concrete, I’m rendering a chat history as a stream and would like to show date dividers when the date changes.

For example, chat history might look like this:

me: a message from two days ago
-----Sept 24-----
me: a message from yesterday
them: a reply from yesterday
-----Sept 25-----
me: a message from today

so I need to be able to compare the date info of each message with the one right before it. Is this possible with streams?

You’d probably do that in the function that prepares the data before sending it to the stream. Something like this (untested code):

{songs, last_song_date} = Enum.map_reduce(songs, assigns[:last_song_date], fn

  %{date: this_date} = x, last_song_date when this_date==last_song_date -> 
    {Map.put(x, :date, nil), last_song_date} 

  x, _ -> 
    {x, x.date} 

  end)

socket
|> assign(:last_song_date, last_song_date)
|> stream(:songs, songs)
3 Likes

I’ve found it nice to keep streams conceptually simple and “pure”. So when the UI requires collections to be broken up into sections, I’d break a stream up into multiple streams. In your case, you could group the messages by date when you pull the messages from the database and then create a stream for each individual day’s messages. For reference, the TodoTrek repo shows how to stream todos grouped into lists.

While this encapsulation may add some complexity and overhead upfront, I’ve found it easier to reason about and extend down the line. For example, it would be fairly straightforward to implement a feature allowing users to delete all messages sent on a specific date by clicking a delete button in the date divider.

me: a message from two days ago
-----Sept 24----- [x] => delete all messages sent on 11/24
me: a message from yesterday
them: a reply from yesterday
-----Sept 25----- [x] => delete all messages on 11/25
me: a message from today
2 Likes