Strategy for creating a group messaging interface


#1

I’m working on a project that requires a group messaging interface. There’s an interesting challenge here, in terms of grouping the messages by the days, and grouping them by the sender. For example:

I’m trying to figure out how this is typically done, and in particular, how it could be done with Blaze and minimongo.

I guess that one could write a script to process all the data and display it in this format, but that would have to sit on the client due to how pub/sub works (?), and it might have poor performance due to how DDP, etc, works.


#2

I think so, a script on the client side will process data then display that format. I think in React way and it’s not too complicated.
About performance, I don’t think it will be poor. You can subscribe messages by group, it’s normal or I’m wrong?


#3

I think the hard part is around deciding when and how to run the script, especially because Tracker tracks every field by default, and because DDP messages are sent in small chunks.

I did some research, and it looks like it could be done on the Meteor.publish() level, which might be more efficient in some ways. Hmm…


#4

I think the script is a part of render method, it will run every time the component (react) renders.


#5

Can you be more specific about what you mean by this?


#6

Notice how the Slack screenshot above displays:

  • grouping by time (i.e. “Yesterday”, “August 14th”, etc)
  • grouping by sender (i.e. moriacat sent two messages but the username and avatar is only displayed once)

#7

Oh, yes… I’ve actually thought about this a bit as a feature for socialize:messaging.

I think that grouping by days could be accomplished quite trivially in the UI as the messages are being rendered. When it comes to grouping by sender you might accomplish this by actually updating the previous message record if that message is by the same user and not older than a certain amount of time. This could be either a direct append to the current body of the message with a newline between, or you could actually store the body as an array and then just iterate over, and display them.


#8

Depends a lot on how you built your UI, but, considering you iterate over every message anyway, how about something like that ? (pseudo pseudo code)

messages.forEach((message, index) => {
 const previousMessage = messages[index - 1];
 if (previousMessage) {
  if (previousMessage.author !== message.author) {
    // render user avatar and name
  }
  if (isDifferentDay(message.date, previousMessage.date)) {
    // render day 
  }
 }
 // render message
})

Of course, this lacks any corner case coverage, and I am not sure how it could be done in Blaze (haven’t used it in a couple years), but I think this is the simplest direction you can take. Doing aggregation in the publish would work, but I fear for the performance implications, considering chats can get quite hectic and is one of the domain where reactivity is expected to be really good.


#9

Thanks for the help @copleykj @pdecrat

I thought about doing it on the data level, but I thought it might be too resources intensive, especially with how aggressive DDP and Tracker are when it comes to reactivity.

I ended up having each message component check the item that rendered before it, to determine if it should omit any data, such as name/avatar, or to potentially display some extra data, such as a title or something.

Here’s how that ended up working: