Chat in Ionic with Cloud Firestore - firebase

I'm trying to implement a chat page in my app.
In the constructor I ask for all the messages in the database:
this._DB
.getDocument("chats", this.favour.chatId)
.then(documentSnapshot => {
var chat = documentSnapshot.data();
for (var key in chat) {
chat.key = chat[key];
}
delete chat.key;
this.chat = chat;
})
.catch(error => {
console.log(error);
});
And the app load ok the messages with this html:
<ion-item *ngFor="let messages of chat?.messages" class="chat" text-wrap [ngClass]="{'chat-partner' : messages?.askedName == localStorage?.name}">
{{messages.message}}
</ion-item>
To implemet a real time chat I see in the docs I have to use the onSnapshot method:
https://firebase.google.com/docs/firestore/query-data/listen
Then I use this function:
ionViewDidEnter() {
this._DB._DB
.collection("chats")
.doc(this.favour.chatId)
.onSnapshot(function(doc) {
this.chat = doc.data();
console.log(this.chat);
});
this.content.scrollToBottom();
}
But the problem is that this.chat is showed ok in the console by the console.log, but the html dont refresh it :-(
I'm doing something wrong?
Thanks in advance
PS: I see maybe is confuse in the onSnapshot function I use this._DB._DB, this is because in the provider (the _DB) I don't have that functiĆ³n and I make it public to can use in other place and can do tests.

Finally I find a solution.
Looks like the problem is the object this... is diferent in the component like in the function called by onSnapshot.
I solved creating a different function:
observingChat(objectThis){
if(objectThis.favour.chatId){
this._DB._DB
.collection("chats")
.doc(objectThis.favour.chatId)
.onSnapshot(function(doc) {
objectThis.chat = doc.data();
objectThis.content.scrollToBottom();
});
}
}
That recibe the object this, and call it from the ionViewDidEnter()
ionViewDidEnter() {
this.content.scrollToBottom();
this.observingChat(this);
}
I don't know why or where the object this change.... if anybody can clarify and say me if the solution is correct I will be vefry gratefull :-)

Related

newbie question - firebase svelte-infinite-loading - unable to read new data from firestore

I'm trying to load db content on scroll down, but i'm getting errors because i don't know how to read correctly the data. Error shows that there is no data.hits, is undefined. What that means?
I used this sample: svelte-infinite-loading sample and tried to apply to work with firebase, but I don't know how to read the console.log(data), hits is undefined, but should it be?
Could someone help me?
ContentBlocks.svelte
<script>
import InfiniteLoading from "svelte-infinite-loading";
let page = 1;
function fetchData({ detail: { loaded, complete } }) {
const response = db
.collection("todos")
.orderBy("created", "desc")
.startAfter(page*5)
.limit(5);
response.get().then((data) => {
console.log(data)
if (data.hits.length) {
page += 1;
list = [...list, ...data.hits];
loaded();
} else {
complete();
}
});
}
</script>
<Box>
<InfiniteLoading on:infinite={fetchData} />
</Box>
is use async / await but that will still help you (also, your list variable is not declared anywhere we can see):
const results = await queryObj.get(); // queryObj == your response constant
if (results.docs) {
list.concat(results.docs.map((doc) => doc.data()));
}
doc is here, with another solution to that problem : https://firebase.google.com/docs/firestore/query-data/get-data#get_multiple_documents_from_a_collection

Angular Firestore document update causing infinite loop in subscription

I understand the issue but can't figure out the workaround. I am querying a specific document to extract an array of token strings. I need to append a new token to the end of this string and then update the current document with this new token array.
To do this, I have subscribed to a query and within, I update that document. But of course, when you update the same object, the subscription runs again thus creating an infinite loop. I tried incorporating a take(1) pipe rxjs operator but that did not change anything. Any suggestions?
Here's my code:
this.afs.collection('users').doc(user.userUID).valueChanges().pipe(take(1)).subscribe((user: userModel) => {
const currentTokens: string[] = user.notifTokens ? user.notifTokens : [];
//token variable is provided outside this query
currentTokens.push(token);
//this next lines causes the subscription to trigger again
userRef.doc(user.userUID).update({notifTokens: currentTokens})
})
I would recommend you avoid using a subscription in this situation, for exactly this reason. I realize the Angularfire2 docs don't list this method, but the base Firebase package includes a .get() method... and while the AF2 docs don't mention the .get() method... the source code shows that it is supported.
Try something like:
this.afs.collection('users').doc(user.userUID).get().then( (user: userModel) => {
if (user.exists) {
console.log("Document data:", user.data());
// Do stuff with the info you get back here
const currentTokens: string[] = user.data().notifTokens ? user.data().notifTokens : [];
currentTokens.push(token);
userRef.doc(user.data().userUID).update({notifTokens: currentTokens})
} else {
// user.data() will be undefined in this case
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});

Vuejs & Firestore - How to Update when Data Changes in Firestore

I've gone through a bunch of tutorials and docs but cannot seem to be able to update on page when data changes in Firestore (NOTE: not Firebase)
Heres what I have currently which is working fine except if data changes in the DB it is not reflected on the page itself unless I refresh. Code below is within script tags:
import { recipeRef } from '../../firebase';
export default {
data() {
return {
recipes: []
}
},
firestore: {
recipes: recipeRef
},
created() {
db.collection('recipes').get().then((onSnapshot) => {
this.loading = false
onSnapshot.forEach((doc) => {
let data = {
'id': doc.id,
'name': doc.data().name
}
this.recipes.push(data)
})
})
}
I'm not using Vuex. Adding data, editing and reading works fine. Just not reflecting changes once data has changed. Maybe there is a life cycle hook Im supposed to be using? For "onSnapshot" - Ive tried "snap", "querySnapshot" etc. No luck.
Thanks in advance.
Remove the get() and just replace with snapshot - like so
created() {
db.collection('recipes').onSnapshot(snap => {
let foo = [];
snap.forEach(doc => {
foo.push({id: doc.id, name: doc.data().name})
});
}
});
I am not familiar with the firestore API, but glancing through the docs, it looks like calling get() is how you query a single time. Where you have onSnapshot should really be querySnapshot -- that is, the results of a one query. See:
https://firebase.google.com/docs/firestore/query-data/get-data
versus:
https://firebase.google.com/docs/firestore/query-data/listen
So to get live updates, it looks like you need to create a listener, like so:
db.collection('recipes')
.onSnapshot(function(snapshot) {
snapshot.forEach(function(doc) {
// Find existing recipe in this.recipes
// and swap in the new data
});
}, function(error) {
// handle errors
});
I think you will need to add that listener in addition to the get() query you are currently doing. Hope this helps!

Convert multiple FirebaseObjectObservable into a FirebaseListObservable

The snippet is part of a bigger code. Generally I have an object on firebase database called users (it's not a list). I need to get some of them and then convert into Array or FirebaseListObservable.
Observable.merge(...[
this.db.object('users/user1'),
this.db.object('users/user2'),
this.db.object('users/user3'),
this.db.object('users/user4'),
this.db.object('users/user5')
]).subscribe(user => {
console.log(user);
});
This return me user by user, however I need to get all users together. I need to do it in sync. Any ideas?
I have a similar problem and this is how I'm solving it for the moment:
getUsers(): Observable<any> {
let observables = [];
for (let user of users) {
observables.push(this.db.object(user))
}
return Observable.combineLatest(...observables, (...results) => { return results });
}
What I did not manage to do is to return it as FirebaseListObservable.

Using pipe in *ngFor, the page sometimes updates, sometimes not

I am using angular2-meteor, I already use pure: false. But the pipe sometimes run, sometimes not. See my comments in the code for details of the problem.
Thanks
<div *ngFor="#user of (users|orderByStatus)">
{{user.status.online}}
</div>
users:Mongo.Cursor<Meteor.User>;
ngOnInit()
{
this.subscribe('users', () => {
this.autorun(() => {
this.users = Meteor.users.find();
});
}, true);
}
import {Pipe} from 'angular2/core';
#Pipe({
name: 'orderByStatus',
pure: false
})
export class OrderByStatusPipe {
transform(usersCursor:Mongo.Cursor<Meteor.User>):Array<Meteor.User> {
console.log("OrderByStatusPipe runs");
// (1) If I only do these two lines, the change of other users' status can show on the screen immediately.
// let users = usersCursor.fetch();
// return users;
// (2) If sort users by status, the page sometimes updates, sometimes not when user status change.
// If not update automatically, I click that part of screen, it will update then.
let users:Array<Meteor.User> = usersCursor.fetch();
users.sort((a, b) => {
return (a.status.online === b.status.online) ? 0 : (a.status.online ? -1 : 1);
});
return users;
}
}
UPDATE: The bug seems fixed.
I think the problem is related with angular2-meteor.
At last I found a working way using sort in when you try to get data from Mongo. So not using sort pipe any more.
But you cannot use users:Mongo.Cursor<Meteor.User> with *ngFor, need fetch() first and use Array<Meteor.User>, otherwise it will show this error when the order of list changes:
Cannot read property 'status' of undefined
But then the list won't update automatically in UI. So you need use NgZone.
So the final working code is like this:
<div *ngFor="#user of users)">
{{user.status.online}}
</div>
users:Array<Meteor.User>; // here cannot use users:Mongo.Cursor<Meteor.User>
constructor(private _ngZone:NgZone) {}
ngOnInit()
{
this.subscribe('users', () => {
this.autorun(() => {
this._ngZone.run(() => {
this.users = Meteor.users.find().fetch();
});
});
}, true);
}
I don't know exactly what is behind the calls Meteor.users.find() and usersCursor.fetch() but I think the retrieval of your users should be done outside the filter itself. I guess that one part is done in the filter (with usersCursor.fetch()?) and this could be the problem...

Resources