Best way to manage Chat channels in Firebase
Asked Answered
S

4

38

In my main page I have a list of users and i'd like to choose and open a channel to chat with one of them.

I am thinking if use the id is the best way and control an access of a channel like USERID1-USERID2.

But of course, user 2 can open the same channel too, so I'd like to find something more easy to control.

Please, if you want to help me, give me an example in javascript using a firebase url/array.

Thank you!

Sol answered 5/11, 2015 at 9:13 Comment(1)
dont combine the USERID1USERID2, what happends if google change the lengh of uids of new users? they didnt commit to a sizeUdometer
D
70

A common way to handle such 1:1 chat rooms is to generate the room URL based on the user ids. As you already mention, a problem with this is that either user can initiate the chat and in both cases they should end up in the same room.

You can solve this by ordering the user ids lexicographically in the compound key. For example with user names, instead of ids:

var user1 = "Frank"; // UID of user 1
var user2 = "Eusthace"; // UID of user 2

var roomName = 'chat_'+(user1<user2 ? user1+'_'+user2 : user2+'_'+user1);

console.log(user1+', '+user2+' => '+ roomName);
            
user1 = "Eusthace";
user2 = "Frank";

var roomName = 'chat_'+(user1<user2 ? user1+'_'+user2 : user2+'_'+user1);

console.log(user1+', '+user2+' => '+ roomName);
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>

A common follow-up questions seems to be how to show a list of chat rooms for the current user. The above code does not address that. As is common in NoSQL databases, you need to augment your data model to allow this use-case. If you want to show a list of chat rooms for the current user, you should model your data to allow that. The easiest way to do this is to add a list of chat rooms for each user to the data model:

"userChatrooms" : {
  "Frank" : {
    "Eusthace_Frank": true
  },
  "Eusthace" : {
    "Eusthace_Frank": true
  }
}

If you're worried about the length of the keys, you can consider using a hash codes of the combined UIDs instead of the full UIDs.


This last JSON structure above then also helps to secure access to the room, as you can write your security rules to only allow users access for whom the room is listed under their userChatrooms node:

{
  "rules": {
    "chatrooms": {
      "$chatroomid": {
        ".read": "
           root.child('userChatrooms').child(auth.uid).child(chatroomid).exists()
        "
      }
    }
  }
}
Darnelldarner answered 5/11, 2015 at 14:28 Comment(7)
I know this is old but, was just looking for some ideas. You can make this flexible by having the users in an array and sort it. Then, just for ease of use and to prevent others from easily guessing room names, just make an MD5 out of it, or hash it in some way. This way you can scale up the number of users in a room. I've been using this for a while and it's quite nice.Okelley
Thank you for you points. Hashing with js-sha256 module worked for me. 'let chatHash = SHA256('agent:' + agentId + '_user:' + userId)'Eduction
This is an excellent answer thank you. But im wondering how to make sure this chat room name is unique. Especially for more common names. Even if I store the chat in both users node, I could have a chat with 2 Franks. Right now I'm thinking of just using FB key for the chat, storing a ref to it in each user's /conversations which has withUser: recipientUid and then when one of the users initiates a chat just checking to make sure one doesn't already exist with the other user. Seems like this might not be the best design though. Any thoughts? Thx!Averi
If you wanted to also keep an unread count for each userChatRoom (in the context of each user), would you keep a lastRead and lastUpdated timestamp in each chatroom object?Fourflush
@Frank van Puffelen What if someone leaves the group? room-name changes? if so others will lose the link to the previous conversations. What is the harm in generating the room-id by pushing room to the room's collection and adding the generated id to all the members?Wizard
@Frank van Puffelen Is it really a good idea to use user names instead of UIDs? I'm asking because user names can change but UIDs can't. So how do we handle user name changes in this case? Do we change room name too?Erasme
I just names here to make the explanation easier to follow. If you check the comments, you can see that they represent UIDs: var user1 = "Frank"; // UID of user 1.Darnelldarner
D
3

In a typical database schema each Channel / ChatGroup has its own node with unique $key (created by Firebase). It shouldn't matter which user opened the channel first but once the node (& corresponding $key) is created, you can just use that as channel id.

Hashing / MD5 strategy of course is other way to do it but then you also have to store that "route" info as well as $key on the same node - which is duplication IMO (unless Im missing something).

Darciedarcy answered 16/4, 2017 at 22:19 Comment(2)
I think they are doing it to preserve the information which user started the chat.Handstand
Thats true though to create the hash you sort user-names so in the end you really dont know who started that channel. If you really need this information you can just create property under your chat-channel-node. Typically if the channel has multiple users you should make someone "admin" of that channel & give few special "powers" but yeah depends on your application's needs.Darciedarcy
U
2

We decided on hashing users uid's, which means you can look up any existing conversation,if you know the other persons uid.

Each conversation also stores a list of the uids for their security rules, so even if you can guess the hash, you are protected.

Udometer answered 22/10, 2020 at 19:3 Comment(0)
E
1

Hashing with js-sha256 module worked for me with directions of Frank van Puffelen and Eduard.

import SHA256 from 'crypto-js/sha256'
let agentId = 312
let userId = 567
let chatHash = SHA256('agent:' + agentId + '_user:' + userId)
Eduction answered 27/2, 2017 at 13:30 Comment(4)
Could you provide some more information on how you are doing this? I have been trying different things and can't wrap my head around it.Courtney
Main point here is to create a sender and receiver string with ids. So in my example we create a string as: "agent:142_user:5346" which means agent id is 142 and user id is 5346. Also you can use two users in string like "user:123_user:312". Then to make it hard to read we use hash algorithm. And it converts something like "e769925f0d2068d6280e3a61b6". I use SHA256 here but you can use different algorithm like MD5, etc.Eduction
this is generating string with length 64. wont it be nice that all chat rooms maintain an array of participants so that we can easily query if there exists a chat where any number of users have done chat ? also it ll be in general case so that it can fit any amount of users. in this hashing use case you have to remember the pattern in which you put the strings and it also wont be nice in case you have a group chat with 100+ users. then you ll be looping and would certainly make code more complex ??Vladamir
@harkal array contains query did not exist at time of previous answersCohn

© 2022 - 2024 — McMap. All rights reserved.