Usually in a chat app, if you post a message/comment/etc. then the next message/comment/etc. is from the same user, and posted in under X minutes, it’s displayed is a “continued” message/comment/etc. (doesn’t re-display the username/avatar/etc.).
I have figured out how to do this via a view helper, but it makes the markup clunky as I always need to carry around the previous_message instead of just having the current message. What I’d like to do is have this stored as an attribute on the message, or as a calculated field when loading a message/list of messages from the DB.
One way I thought to do it is via something in the changeset like this:
But this seems like it’s likely to cause race conditions. Another way it to make it virtual field, and populate it when getting the rows out of the database, but this seems clunky too. The other thought is to do it via a Postgresql Trigger, and have the DB calculate it on insert (no race conditions, faster, and no need to calculate when retrieving rows).
Is this a pattern you’ve dealt with before? What’s the best approach?
Here’s the current implementation. I’d like to move to using LiveComponents for each message, and slimming down the assigns needed will simplify the logic. I’m thinking a DB trigger is the fastest/cleanest way to do it.
<%= for {message, index} <- Enum.with_index(@messages) do %>
<%= if index == 0 do %>
<%= render "_message.html", Map.put(assigns, :message, message) %>
<% else %>
<%= if continue_message?(Enum.at(@messages, index - 1), message) do %>
<%= render "_message_cont.html", Map.put(assigns, :message, message) %>
<% else %>
<%= render "_message.html", Map.put(assigns, :message, message) %>
<% end %>
<% end %>
<% end %>
Give your messages something unique by sender, which you can target via css, and with selector + selector target the ones following up after each other to alternate the styling. I‘d expect this styling only version to be better for accessibility as well, as e.g. blind/bad-sighted people likely need the full markup per message.