Firebase data structure for chat - firebase

I am trying to build an app for employers to chat with employees.
So I have employers, employees and messages.
I did it with $firebaseArray and childs:
recipient > sender > messages
I want to add sender data such as profile images and last message, I'm not how to do it.
- employer_1
- employee_1
- SKLDJLKDksdklJS
- content: "Hello"
- timestamp: 129081021
Is this the right way to do it or is there a better way? Thanks.

You might have issues of showing only latest messages or newer messages since any get on employer_1 -> employee_1 will load all messages.
Another alternate might be to have a structure like this:
{
"users":{
"employer_1":{
"profile-image":"<url>",
"last-message":"SKLDJLKDksdklJS",
...
},
"employee_1"{
"profile-image":"<url>",
"user-chat-list":{
"employer_1":{
"last-message":"SKLDJLKDksdklJS",
"message-list":{
"SKLDJLKDksdklJS" : 129081021,
"ASDCJLKDksdklJS" : 129081021
}
}
}
}
},
"messages":{
"SKLDJLKDksdklJS":{
"content": "Hello",
"sender":"employer_1",
"timestamp": 129081021
}
}
}
you won't need to fetch all the messages with content for a chat list.

Related

Firebase database structure for one-on-one messaging?

I'm relatively new to Firebase and I'm trying to figure out the best way to structure my DB for 1:1 chats.
It basically has to function like Whatsapp - you have your active conversations on the left when a person sends a message that conversation is then put on top of the list and a basic one-on-one chat on the right.
Right now the best that I have got is to create a firestore "chats" collection
chats : {
chat : { users : [user_id, user_id], messages : {user_id, message, created_at} created_at }
}
Would this solution allow me to :
Get all the chats based on the logged-in user?
Then Sort the returned chats by date?
Get the latest message from the messages collection for each returned chat?
On new message change the order of the chat and update the latest message?
And if all of that is doable would this be effective or is a there a better way?
Any help would be appreciated - thanks!
How would a logged in user be associated with any given chat they participated into?
Right now your structure doesn't seem to allow for an easy handling of this, given that "user_id" are nested within the chat document.
Personally, here's what I would do.
First I would create 2 collections, one called chats one called users.
users would have the following structure:
{"users": {
"userID_1": {
"name": "John",
"surname": "Smith",
"chats": [
"chatID_1",
"chatID_2",
"chatID_3"
]
},
"userID_2": {
"name": "Betty",
"surname": "Sue",
"chats": [
"chatID_1",
"chatID_4"
]
}
}}
Chats would instead be stored like this:
{"chats": {
"chatID_1": {
"chatName": "foo",
"otherInfo": "..",
"messages": {
"messageID_1": {"senderID": "..", "message": "..", "timestamp": 999},
"messageID_2": {"senderID": "..", "message": "..", "timestamp": 999}
}
},
"chatID_2": {
"chatName": "bar",
"otherInfo": "..",
"messages": {
...
}
}
}}
This way, when a user is logged in, you can easily fetch all his chats by querying users.userID.chats, and retrieve the content of any selected chat by querying chats.chatID.messages.

Remove notification after a certain time in React Native (#react-native-firebase/messaging)

I have push notifications working in React Native using #react-native-firebase/messaging. I am using FCM on the backend, and it is currently showing the OS lock screen notifications on iOS and Android.
I want to clear a given notification after a certain time, or after an amount of time has passed. Is there a way to do this? Right now when I send a notification it will stick around for days if I don't click on it. I would like to take a notification down after say an hour, or at 4pm, or whatever. Front-end and/or back-end solutions welcome.
I had assumed that the "ttl" (time to live) parameter did this, but this is not the case.
you could use a background handler like react-native-background-fetch
In your onMessage or backgroundMessage schedule a backgroundTask for your desired time with scheduleTask().
You could use react-native-push-notification to display the notification, which has an method cancelLocalNotifications() to cancel notifications
In the task you could clear the notification depending on the id
PushNotification.configure({
onNotification: (notification) => {
var {id} = remoteMessage.data
BackgroundFetch.configure({
minimumFetchInterval: 15
}, async (taskId) => {
PushNotification().cancelLocalNotifications(id)
BackgroundFetch.finish(taskId);
})
BackgroundFetch.scheduleTask({
taskId: id,
forceAlarmManager: true,
delay: 5000
});
}
})
TTL parameter only specifies the delivery of the notification to the user device. E.g. Still deliver the message after the phone was offline for 2 hours or not.
I'm not sure if there is a way with the default firebase package, but the more advanced version of it seems to be able to handle that use case:
https://notifee.app/react-native/reference/canceldisplayednotification
I think you should be able to call that method in a background task (e.g. after receiving another (silent) notification).
Unfortunately I couldn’t test it myself yet.
On iOS you you can use the badge setting. If you set it to 0, it will remove all notifications. For your case you could schedule a "cleanup" task that triggers the below request after a certain amount of time.
{
message: {
notification: {
body: "" // has to be empty
},
android: {
notification: {
channel_id: 'some_channel'
}
},
apns: {
payload: {
aps: {
category: 'some_category',
badge: 0
}
}
},
token: device_token
}
}
Unfortunately, I have not found a similar solution for android yet.

How to prevent malicious scripts writing to Firebase database?

I been reading firebase 3+ documentation for a while and I'm still wondering how to manage the following scenario regarding safety:
Let say I have a website for publishing local business like yellow pages in where everyone with an account can add new entries and edit the info of the existing ones with the following schema:
{
"businesses"": {
"62061635": {
"id": "62061635",
"name": "Cellphone store"
},
"66856728": {
"id": "66856728",
"name": "El Bambino restaurant"
}
}
}
If a user with a successful login write the following snipped in the developers console:
firebase.database().ref('/businesses/').once('value').then(function(snapshot) {
console.log(snapshot.val());
});
Practically all users could retrieve all the businesses info, that's not so drastic, but if instead of the above code the users use the following code:
var i=0;
while(i++ < 10) {
var id = generateRandomString();
firebase.database().ref('businesses/' + id).set({
id: id,
name: generateRandomString()
});
}
That's something I worry about, I know there are rules for database, but in this case where all users can add and edit the info, how can I prevent the users to run malicious scripts like the ones above?

Storing User Data Flattened Security

I'd like to create an app that has a lot of user data. Let's say that each user tracks their own time per task. If I were to store it flattened it would look like this:
{
users: {
USER_ID_1: {
name: 'Mat',
tasks: {
TASK_ID_1: true,
TASK_ID_2: true,
...
}
},
},
tasks: {
TASK_ID_1: {
start: 0,
end: 1
},
TASK_ID_2: {
start: 1,
end: 2
}
}
}
Now I'd like to query and get all the task information for the user. Right now the data is small. From their guides: https://www.firebase.com/docs/web/guide/structuring-data.html it says (near the end) "... Until we get into tens of thousands of records..." and then doesn't explain how to handle that.
So my question is as follows. I know we can't do filtering via security, but can I use security to limit what people have access to and then when searching base it off the user id? My structure would then turn to this:
{
users: {
USER_ID_1: {
name: 'Mat'
}
},
tasks: {
TASK_ID_1: {
user: USER_ID_1,
start: 0,
end: 1
},
TASK_ID_2: {
user: USER_ID_1,
start: 1,
end: 2
},
...
}
}
Then I would set up my security rules to only allow each task to be accessed by the user who created it, and my ref query would look like this:
var ref = new Firebase("https://MY_FIREBASE.firebaseio.com/");
$scope.tasks = $firebaseArray(ref.child('tasks/')
.orderByChild('user')
.startAt('USER_ID_1')
.endAt('USER_ID_1'));
Is that how I should structure it? My query works but I'm unsure if it'll work once I introduce security where one user can't see another users tasks.
You've already read that security rules can not be used to filter data. Not even creative data modeling can change that. :-)
To properly secure access to your tasks you'll need something like:
"tasks": {
"$taskid": {
".read": "auth.uid === data.child(user).val()"
}
}
With this each user can only read their own tasks.
But with these rules, your query won't work. At it's most core your query is reading from tasks here:
ref.child('tasks/')...some-filtering...on(...
And since your user does not have read permission on tasks this read operation fails.
If you'd give the user read permission on tasks the read and query would work, but the user could then also read all tasks that you don't want to give them access to.

Datastructure for multi-cast type of message broadcasting

We are thinking about migrating from Pusher to Firebase. We are having troubling thinking about how Pusher channels would be represented in Firebase.
In Pusher we have a channel per user. So a user might be in a user-1 channel, another might be in a user-2 channel.
Then our backend/server would send a message to both these users via Pusher.trigger(message, ['user-1', 'user-2']).
I think this would usually be done like this:
{
web_page_1: {
user_1: {
messages: [{}, {}, ..],
},
user_2: {
messages: [{}, {}, ..],
},
...
},
web_page_2: {
user_2: {
messages: [{}, {}, ..],
},
user_3: {
messages: [{}, {}, ..],
}
},
....
}
Here the problem is: User 1 and User 2 for the same page might have a lot of messages in common. Is there a way to reduce this duplication, since these messages can get rather large, sending and storing them per user can get expensive. Also User 1 should not be able to read the messages of User 2.
It would be nice to do something like this:
{
web_page_1: {
message_1: {
user_ids: [1,2,3]
content: {},
},
message_2: {
recipient_ids: [3,4,5]
content: {},
}
...
},
web_page_2: {
message_1: {
user_ids: [1,2,3]
content: {},
},
message_2: {
user_ids: [3,4,5]
content: {},
}
},
....
}
But then, how would the security policy be applied such that a message can only be read by the user_ids specified in it.
Any pointers would be really appreciated.
If multi-cast is your use-case and the messages get large, I would indeed split the messages from the users and add message-references to the users like you show.
Root
Users
provider:344923
Name: Akshay Rawat
Messages
1: true
2: true
3: true
provider:209103
Name: Frank van Puffelen
Messages
1: true
Messages
1: It's a beautiful day
2: The sun is shining
3: I feel good, I feel good
4: And nothing's gonna stop me now
In the above data you can see that you and I are users. The provider:... is our uid, but can be anything that allows you to identify the current user. You've received messages 1, 2 and 3, while I have only received message 3. Neither of us has received message 4.
I took the Web_page level out to simplify things a bit. If you really need that level, you can add it back. The basic approach will remain the same.
You security rules can then use these message-references to see if the use can read a specific message:
{
"rules": {
"Messages": {
"$message_id": {
".read": "root.child('Users/'+auth.uid+'/Messages').hasChild($message_id)"
}
}
}
This rule defines the security for any child under messages (identified by $message_id). We grant read access if the $message_id is references as a message for the current user (auth.uid).

Resources