Reputation: 2024
I'm building a chat application and I'm trying to identify the state architecture for indicating unread messages.
My state currently contains a list of messages. On render, I group them by conversation so I can render a list of recipients and, for the selected recipient, the messages.
The simplest approach would be to also store a lastRead
hash of {recipient: lastReadTimestamp}
in the state as well. On render, count the number of messages in each conversation whose timestamp is greater than the stored lastRead
timestamp to get the number of unread messages. And when the recipient is selected, set the lastRead
timestamp for that recipient to that of the most recent message.
The problem with this is if, while you're away, 15+ messages come in - to the point that some are beyond the viewport and you'd have to scroll to see them. Once you select that recipient, it will mark the lastRead
timestamp as the most recent message, essentially marking the entire conversation as "read" even though there are messages above that haven't been seen.
Instead I was hoping to have functionality more like Slack's. in-view or an InteractionObserver could detect when a message has actually come into the viewport.
But what would I store in the state? The ids of each unread message? If so, when I refresh and my app receives all the messages, how does it know any of them are read? I could store the ids of each read message instead but that sounds unwieldy.
Has anyone seen a good design pattern for this?
Upvotes: 4
Views: 1575
Reputation: 1292
We did use IntersectionObserver for exactly the same use case in a chat application.
Pre-requests: The messages have 2 meta-fields:
time
(when it was sent)timeSeen
(when it was viewed)The flow is as follows:
time = now()
and timeSeen = null
to that messagetimeSeen
, e.g. it's null
IntersectionObserver
wrapped component, which triggers a hook when message is in viewport and passes the messageId
as a parammessageId
as seen by setting timeSeen = now()
messageId
(timeSeen updated)I know this sounds a bit of an overkill, but this way you can reliably get the following:
Upvotes: 1