0

I am creating chat functionality in my web app. I am using mysql to store data in a table with a userId and fromId columns. The userId is the message recepient while fromId is the sender.

So, what comes from the GET /messages endpoint looks something like this:

[
  {
    "id": 10,
    "userId": 2,
    "fromId": 4,
    "message": "messsage",
    "read": 0,
    "createdAt": "2022-02-22T11:05:54.000Z",
    "updatedAt": "2022-02-22T11:05:54.000Z",
    "user.first": "Fourth",
    "user.last": "NNe",
    "user.email": "hello3@feathersjs.com",
    "user.phone": null,
    "user.location": null,
    "user.isVerified": 0,
    "user.roles": "user"
  },
  {
    "id": 9,
    "userId": 4,
    "fromId": 2,
    "message": "messsage",
    "read": 0,
    "createdAt": "2022-02-22T08:32:01.000Z",
    "updatedAt": "2022-02-22T08:32:01.000Z",
    "user.first": "Jane",
    "user.last": "Doe",
    "user.email": "hello1@feathersjs.com",
    "user.phone": null,
    "user.location": null,
    "user.isVerified": 0,
    "user.roles": "user"
  },
  {
    "id": 8,
    "userId": 2,
    "fromId": 3,
    "message": "messsage",
    "read": 1,
    "createdAt": "2022-02-22T08:31:52.000Z",
    "updatedAt": "2022-02-22T08:31:52.000Z",
    "user.first": "Third",
    "user.last": "Tatu",
    "user.email": "hello2@feathersjs.com",
    "user.phone": null,
    "user.location": null,
    "user.isVerified": 0,
    "user.roles": "user"
  },
  {
    "id": 7,
    "userId": 2,
    "fromId": 4,
    "message": "messsage",
    "read": 1,
    "createdAt": "2022-02-22T08:31:48.000Z",
    "updatedAt": "2022-02-22T08:31:48.000Z",
    "user.first": "Fourth",
    "user.last": "NNe",
    "user.email": "hello3@feathersjs.com",
    "user.phone": null,
    "user.location": null,
    "user.isVerified": 0,
    "user.roles": "user"
  },
  {
    "id": 6,
    "userId": 3,
    "fromId": 2,
    "message": "messsage",
    "read": 0,
    "createdAt": "2022-02-22T07:29:49.000Z",
    "updatedAt": "2022-02-22T07:29:49.000Z",
    "user.first": "Jane",
    "user.last": "Doe",
    "user.email": "hello1@feathersjs.com",
    "user.phone": null,
    "user.location": null,
    "user.isVerified": 0,
    "user.roles": "user"
  },
  {
    "id": 5,
    "userId": 2,
    "fromId": 1,
    "message": "messsage",
    "read": 0,
    "createdAt": "2022-02-22T07:29:48.000Z",
    "updatedAt": "2022-02-22T07:29:48.000Z",
    "user.first": "Joseph",
    "user.last": "Mungai",
    "user.email": "hello@feathersjs.com",
    "user.phone": null,
    "user.location": null,
    "user.isVerified": 0,
    "user.roles": "admin"
  },
  {
    "id": 4,
    "userId": 1,
    "fromId": 2,
    "message": "messsage",
    "read": 0,
    "createdAt": "2022-02-22T07:20:37.000Z",
    "updatedAt": "2022-02-22T07:20:37.000Z",
    "user.first": "Jane",
    "user.last": "Doe",
    "user.email": "hello1@feathersjs.com",
    "user.phone": null,
    "user.location": null,
    "user.isVerified": 0,
    "user.roles": "user"
  },
  {
    "id": 2,
    "userId": 2,
    "fromId": 1,
    "message": "If you have any questions or problems, you can ask them here.",
    "read": 0,
    "createdAt": "2022-02-22T07:16:04.000Z",
    "updatedAt": "2022-02-22T07:16:04.000Z",
    "user.first": "Joseph",
    "user.last": "Mungai",
    "user.email": "hello@feathersjs.com",
    "user.phone": null,
    "user.location": null,
    "user.isVerified": 0,
    "user.roles": "admin"
  }
]

Now what I can't figure out is how to manipulate the array response to get different arrays of each conversation. I think I should use array.reduce method but I can't wrap my head around how to do that.

As shown in the image below for the UI, I would like the column marked 'names' in red, to have the different arrays of each conversation. And the column marked 'chat' in blue to have the contents of each of these conversations.

enter image description here

I am pretty sure this is doable, please help.

Youzef
  • 616
  • 1
  • 6
  • 23
  • 5
    A picture of your response object is much less useful than a sample response. Additionally, it would be helpful if you could provide an example of your desired output, and what you have tried so far. The more context you can provide, the more enabled the community is to give you meaningful direction. Good luck, and happy coding! – Alexander Nied Feb 22 '22 at 15:50
  • The response is quite long, Thats why I posted the image. I have updated the question. – Youzef Feb 22 '22 at 16:04

2 Answers2

1

Instead of a user having to fetch ALL the messages from the database for opening only 1 chat, I suggest to change the API. The /messages Api should accept userId parameter (the active user which is using the app), and fromId (the userId of a user you want to show messages from.

Whenever a user now clicks on any chat, you can do a GET request to server which would then return one single array of messages which you can directly display. No complex data manipulation, more maintainable code!.

Using SQL on serverside:

-- Pseudocode..
Select * from messageTable where userid=${userId} and fromId=${fromId};

The db is designed for receiving/filtering large amounts of data very fast and allows you to easily build up the data structure you like and is probably faster than doing it in javscript/node

Scaling

Think of a user having 100+ chats, it would be large amounts of data to fetch for him before being able to even open 1 chat! You could for example load the 5 "last active" chats on app start and then lazy load the other chats in the background. (or only load them on click.)

Later on you can finetune your API and provide an endpoint to maybe fetch a few chats at once but for beginning don't bother to make 1 Request per chat!.

You may also want to fetch only the latest x messages, when the app would be used multiple years, the data amount could become quiet large.

Silvan Bregy
  • 2,544
  • 1
  • 8
  • 21
  • Thanks for the response. I thought of doing that. Problem is since I have to get messages both sent and received, i'm already doing `where userid=${userId} or fromId=${userId}` to get all conversation a user has participated in. I also have a notification badge that show any unread messages, so it seems like I still have to do it this way.. – Youzef Feb 22 '22 at 16:44
1

So you would need an array of messages which have both the sender Id and the recipient Id, or I think maybe a better approach could be to identify the objects with a conversationId which would make things a lot easier and you could group the results directly from sql. You could also group the results in your query by userId and fromId but that's out of the scope of this question. Anyway, this is what you are looking for

const mySegmentedArray = messageArray.reduce((finalArray, message)=>{
const addedConversation = finalArray.find(ma=>
  ((ma.user1 == message.userId && ma.user2 == message.fromId) ||
  (ma.user1 == message.fromId && ma.user2 == message.userId))

 if(addedConversation){
  addedConversation.messages = [...addedConversation.messages, message]
 }else{
  finalArray = [...finalArray, 
  {
    user1: message.userId, 
    user2: message.fromId, 
    messages: [message]}
  }]
}
return finalArray
}, [])
Manuel Duarte
  • 644
  • 4
  • 18
  • Thanks! I like this, but this works partially. I need all conversations between two individuals irrespective of who sent/received the message to be in the same array... Currently, only those that share `userId` and `fromId` are grouped, e.g all messages with userId of 1 and fromId of 2. But remember also messages with userId of 2 and fromId of 1 are in the same conversation.. I hope I am coming off clearly – Youzef Feb 22 '22 at 19:55
  • 1
    You are right, I just updated the function to meet the requirements – Manuel Duarte Feb 22 '22 at 22:55
  • I had done that, just wasn't very confident if I was right, I really have to master the reduce function.. I can see what the `addedConversation` variable is doing, but I still don't see how it's being added to `finalArray` since it's not being referenced anywhere in the `else` part – Youzef Feb 23 '22 at 04:28
  • 1
    Since addedConversation is a reference to an item from the "finalArray", we dont need to added to the finalArray explicitly. Modifying our addedConversation variable will affect the finalArray directly. Maybe this question can help you understand a little better this concept https://stackoverflow.com/questions/29050004/modifying-a-copy-of-a-javascript-object-is-causing-the-original-object-to-change – Manuel Duarte Feb 24 '22 at 17:20
  • Thanks man, didn't know that – Youzef Feb 26 '22 at 20:51