How to group chat messages per user?
I solved this problem for myself pretty recently. Here's a full example.
The core business logic for grouping messages, in the above example, can be found under src/store.js
in the addMessage
function.
Unless your server is also storing all the messages and you have some mechanism to ensure causal ordering across all clients, I would recommend that you run the logic on each client. That way, you ensure that clients don't see messages jumping around under any circumstance. At worst, messages would appear ungrouped.
The algorithm to determine this is run when a new message is received, therefore it runs on each client, but you can tweak it to work for your use-case as well! It's shown below (I may have used the wrong flowchart elements..apologies).
addMessage({ state }, { msg, selfHash }) {
let addAsNewMsg = true;
const lastMessage = state.messages.slice(-1)[0];
if (lastMessage && lastMessage.userHash === msg.userHash) {
// The last message was also sent by the same user
// Check if it arrived within the grouping threshold duration (60s)
const lastMessageTime = moment(lastMessage.time);
const msgTime = moment(msg.time);
const diffSeconds = msgTime.diff(lastMessageTime, "seconds");
if (diffSeconds <= 60) {
addAsNewMsg = false; // We're going to be appending.. so
lastMessage.message.push(msg.message);
// We're going to now update the timestamp and (any) other fields.
delete msg.message; // Since, we've already added this above
Object.assign(lastMessage, msg); // Update with any remaining properties
}
}
if (addAsNewMsg) {
state.messages.push(msg);
}
}
Seems like an ideal use case for computed properties. First sort the data by post_date
computed: {
sortedPosts() {
return this.posts.sort((a, b) => {
if (a.post_date < b.post_date) return -1;
else if (a.post_date > b.post_date) return +1;
return 0;
});
},
Then group by user.uid
groupedPosts() {
const posts = this.sortedPosts.reduce((post, grouped) => {
if (grouped.length === 0)
grouped.unshift([post]);
else if (grouped[0][0].user.uid === post.user.uid)
grouped[0].push(post);
else
grouped.unshift([post]);
return grouped;
}, []);
return posts.reverse();
}
Then used the computed property in a template
<div v-for="group in groupedPosts">
<div v-for="post in group">
(I haven't tested the code above, so watch for typos, etc.)