How to stop listening for snapshot updates in cloud firestore in Vuejs? - firebase

I'm using firestore in a messaging app I wrote with Vuejs.
The scenario is as follows: Active conversations are listed on the left side of the screen, clicking on it connects to the relevant collection and messages are listed on the right. So far everything is great.
Problem: When I click on each chat on the left, it connects to the corresponding collection. If more than one connection is made, the problem arises. While messaging with person A and receive a message from person B, the data in my right message box will change to B (Because I just clicked on that chat and subscribed to the collection).
Here is the function I run to list chats on page load:
mounted() {
const me = this.getRandomNumber()
firestore.collection('chat-groups').doc('messages').collection(`${me}`).onSnapshot(snapshot => {
this.chatGroups = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data()
}))
})
},
Here is the function I run to list messages when I click on chat:
async getChatDetails(e) {
const me = this.getRandomNumber()
const pairId = e.id
this.activeChat.id = pairId
this.activeChat.userName = e.name
this.activeChat.profilePhoto = e.photo
firestore.collection('chat-groups').doc('messages').collection(`${me}`).doc(`${pairId}`).collection('messages').orderBy('createdAt', 'desc').limit(250).onSnapshot(snapshot => {
this.messages = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data()
})).reverse()
})
this.chatIsSelected = true
}
This is how I printed the messages stored in the this.messages variable to the page:
<ChatArea :messages="messages" />
I tried the following to close the connection but no results
firestore.collection('chat-groups').doc('messages').collection(`${me}`).onSnapshot()
firestore.collection().onSnapshot()
The solution I could think of was to unsubscribe from the previous subscription and start a new one when I clicked on a chat, but I was unable to do so.
As a result I want to be able to close the previous link both when I leave the page (beforeDestroy) and when I click on the other chat.

as you know that previous listener is not detached after you leave the previous chat and create a new listener for a new collection. so it still listening, i assume that you using options api, you can create a data "id" that will used for getting collection of message from props, and "listener" data for listening variable
attach listener data to snapshot in mounted function, after that create a watcher that watching data id changed or not, if changed, detach the listener variable and attach new listener with new id from props in watcher

Related

Is it possible to destroy firestore listeners soon if client is not connected? [duplicate]

Is there any way to pause firestore listener without removing it?
I have multiple firebase listeners, some are dependent on other, that changes or start other listeners on data change. Lets say my first listener starts a second listener its onSnapshot. First listener started on useEffect. For certain condition I may not want to change the second listener, so I need to discard data change update from first listener.
If condition met (button click), I discard data changes on first listener for a few moments. Currently I'm doing this using a boolean with useRef. My react app is working fine, with dependant listeners like this. I could remove the listener but I do not want to remove and recreate the listener.
I was wondering if there is a pausing mechanism or method available for any listener. I think it will save a tiny read cost if there was such a method because I'm not using that data sent onSnapshot.
Code example:
useEffect(() => {
let firstListener, secondListener;
//console.log("useEffect...");
function ListenerFunc(p) {
secondListener = await firestore
.collection("test")
.doc(p)
.onSnapshot((doc) => {
//console.log("Current data: ", doc.data());
//Need to discard unwanted change here.
//Changing it on button click for a 2 seconds then it changes back to : pauser.current = false.
if (pauser.current) {
console.log("paused for a moment.");
//pauser.current = false;
return;
}
else {
//update.
}
})
}
firstListener = firestore
.collection("test")
.doc("tab")
.onSnapshot((doc) => {
//console.log("Current data: ", doc.data());
var p = doc.data().p; //get variable p
ListenerFunc(p);
});
// cleanup.
}
Unfortunately this is not possible. If you need to stop listening for changes, even temporarily, you have to detach your listener and attach a new one when you want to start listening again, there is no pause mechanism for listeners.
You could open a Feature Request in Google's Issue Tracker if you'd like so that the product team can consider this, but given that this has already been proposed in this GitHub Feature Request for the IOS SDK and it was rejected I don't see this changing anytime soon.

a-frame networked - how to match an avatar with a broadcasting client?

I'm using aframe-networked, and I'm sending some custom data between the users:
// sender
NAF.connection.broadcastDataGuaranteed(dataType, data)
// all recipients listen
NAF.connection.subscribeToDataChannel(dataType, (senderId, type, data, target) => {})
but I'm having trouble determining which avatar entity is corresponding to the broadcasting client. How the receivers know which player is the sender from the callback?
One way would be by using aframe-networked events, and keeping all connected users in a dictionary. The creatorID, ownerId and networkId are all kept in the networked component - so all You need is to grab them once the avatar is created:
const usersMap = {};
// Fired when a networked entity is created
document.body.addEventListener("entityCreated", function(evt) {
const networkedComponent = evt.detail.el.getAttribute("networked");
usersMap[networkedComponent.creator] = {
networkId: networkedComponent.networkId,
el: evt.detail.el
};
});
// Fired when another client disconnects from you
document.body.addEventListener("clientDisconnected", function(evt) {
if (usersMap[evt.detail.clientId]) delete usersMap[evt.detail.clientId];
});
Now, each time You get a callback from a broadcasted message (via NAF.connection.subscribeToDataChannel(dataType, callback)) You can easily track the entity belonging to the sender:
// change the senders color to blue
NAF.connection.subscribeToDataChannel("data", (sender, type, data, target) => {
usersMap[sender].el.setAttribute("color", "blue");
})
You can see it working in this glitch. If any user presses "excuse me" the rest should see an exclamation mark above his head.
If You're broadcasting data in another way (like another socket.io connection), You can pass Your own clientID (player.getAttribute("networked").creator) in the message, so the above stays the same,
or you can pass the networkId, so the receiver can do a single query:
let senderEntity = document.getElementById("naf-" + networkId)

Using onWrite Trigger for Realtime database in Firebase function

I designed a flutter application using dart and firebase backend. I want to get triggered when a user creates, delete, or updates anything in the database. I understood onCreate,onRemoved, and onUpdate methods.
I wrote a javascript code in the firebase cloud function. I need to send a notification when a trigger happens. I don't want to use 3 different triggers. I want to use onWrite trigger only.
This is my database.
There are four departments called comp, civil, elect, and mech. I want to get triggered when database changes happen in a single onWrite trigger!
My First question is, I want to differentiate whether it is created or delete or update from onWrite trigger. I know it will trigger for all 3 events. But how can I differentiate it?
This is my code for onCreate...
exports.noticeAddedComp = functions.database.ref('main/notices/comp/{id}').onCreate( async evt => {
var token = ['dxnfr3dq6_Y:APA91bHavtoP62l296qzVoBhzQJbMFA']
const payload = {
notification:{
title : 'Message from Cloud',
body : 'This is your body',
sound : 'default'
},
data:{
message : 'Thi is new Push',
click_action : 'FLUTTER_NOTIFICATION_CLICK',
}
};
await admin.messaging().sendToDevice(token,payload);
});
Above code is working. But it is for onCreate.
My second question is, Do I need to write four onWrite trigger codes for four departments?
You can use the change.before and change.after properties to determine whether the node was created, deleted, or updated in a onWrite handler:
exports.noticeAddedComp = functions.database.ref('main/notices/comp/{id}')
.onWrite( async (change, context) => {
if (!change.before.exists()) console.log("New node: "+change.after.key);
if (!change.after.exists()) console.log("Deleted node: "+change.before.key);
if (change.before.exists() && change.after.exists()) console.log("Updated node: "+change.after.key)
...
});
You can attach a Cloud Function to all departments at once, by including another parameter into its path:
exports.noticeAddedComp = functions.database.ref('main/notices/{dept}/{id}')
.onWrite( async (change, context) => {
console.log("Triggered for department: "+context.params.dept);
...
})
You can check if
evt.after.data().property;
is either null or doesn't exist, meaning it is deleted.
EDIT:
Scenario when you create something (document or field) :
Creation of the field would mean that the field in "before" doesn't exist at all, and in "after" it exists, but can be null.
Scenario when you update something
Field in the "before" is not the same as the one in "after".

Jitsi-Meet SDK - Get alert of push for incoming call React Native

I have implemented jitsi-meet video call successfully in my React Native app using react-native-jitsi-meet SDK.
Both the user can join a call successfully with same URL. Now here, I want to get alert when user A start a call, user B should get alert to join that call. Right now I want to send alert to all users who has app installed, later I will manage to assign unique identifier, so only user for whom the call is generated can join that get alert to join.
Right now, I just want to get alert when call is initialized by first user. It can also be for specific user only so not all users can get notification. I don't know how exactly it works in jitsi.
My current implementation:
JitsiMeet.initialize();
JitsiMeetEvents.addListener('CONFERENCE_LEFT', (data) => {
console.log('CONFERENCE_LEFT');
});
JitsiMeetEvents.addListener('CONFERENCE_JOINED', (data) => {
console.log('CONFERENCE_JOINED');
});
JitsiMeetEvents.addListener('CONFERENCE_WILL_JOIN', (data) => {
console.log('CONFERENCE_WILL_JOIN');
});
setTimeout(() => {
JitsiMeet.call("https://mydomain.name/" + finalName, this.state.isAudioEnable);
}, 1000);
Any help will be appreciate.

Firebase React Native fetch data

I am trying to do an app on react-native with a feed. On my main screen, I go fetch the data :
fetchData() {
firebase.database().ref(`/posts/${group}`).on('value', async snapshot => {...}
}
when I want for example to like a comment of the post, I first push the data into firebase with different queries, for example :
export const likeComment = (...) => {
firebase.database().ref(`/posts/${group}/${post}`).update
({
updatedAt: firebase.database.ServerValue.TIMESTAMP
});
firebase.database().ref(`/posts/${group}/${post}/lastComments`).set(...);
But I realized that my first function fetchData was called 3 times.
then I grouped my queries like :
let updates = {}
updates[`/posts/${group}/${post}/lastComments`] = {...};
updates[`/posts/${group}/${post}`] = { ... };
firebase.database().ref().update(updates);
Then, fetchData was called still 2 times.
I was wondering if that was the best way to do it, and why does my function fetchData was still called twice.
Thanks for your help
It's hard to tell from the limited code, but my guess is that fetchData is not actually being called more than once, but instead the snapshot is firing when you make updates to your Firebase database.
The part of your code where you say .on('value', async snapshot => you're setting up a listener to send you updates any time that value changes.
So my guess is that your three invocations are:
The first time you actually call fetchData and set up the
listener
Your .on( snapshot listener firing when you call
update
Your .on( snapshot listener firing again when you
call set
This push-based database workflow is one of the main benefits of Firebase. If you only want to get the data once and not get live updates, you can call .once( instead of .on(
https://firebase.google.com/docs/database/web/read-and-write

Resources