Search code examples
angularfirebaseangularfire2angularfire

Only render recent changes Firestore + Angular Chat app


I have a created a chat app using firestore(using angularfire wrapper) and angular and it's working. The database structure is like this. I have unique ids for every user. When ever someone(sender) sends a message to other person(receiver) I add the message to sender:

collection("messages") => doc(sender's ID) => collection(receiver's ID) => doc(message ID)

Also, I add the same message to receiver:

collection("messages") => doc(receiver's ID) => collection(sender's ID) => doc(message ID)

Now, When user(sender) opens a chat with another user(receiver) I attach listener(valueChanges) to:

collection("messages") => doc(sender ID) => collection(receiver ID)

each message in the collection is structured like this:

message: 'string message'
profilePic: 'url for profile pic'
senderId: 'unique id of sender'
timestamp: some timestamp

Everything works as expected with this configurations but there are a few problems.

  1. Whenever a new message is added the the 'valueChanges' listens for the changes and assigns the value to array 'chats'. 'chats' array is used with *ngFor to display the content of messages. Problem occurs when ever I receive changes, the whole list of messages are re-rendered including the profile pics. Ideally, only the most recent changes should be rendered.
  2. There is no way (or that i can think of) to query for most recent message from every user.

Solution

  • Using valueChanges would be simple, but you will see the problems and it also costs of DB usage. So I recommend you to have a look at snapshotChanges.

    You could separate the logic as following way.

    • Load all messages at the entry time using valuechanges and first/or take(1) operator.
    this.messages = await this.afs.collection('messages')
      .valuechanges()
      .pipe(take(1))
      .toPromise();
    
    • And you have to listen the recent changes and modify the array in the Angular side.
    // manage recently added messages
    this.afs.collection('messages').snapshotChanges(['added'])
      .pipe(takeUntil(untilFn))
      .subscribe(added => {
        this.messages = [...added, this.messages];
    });
    
    // manage recently removed messages
    this.afs.collection('messages').snapshotChanges(['removed'])
      .pipe(takeUntil(untilFn))
      .subscribe(removed => {});
    
    // manage recently modified messages
    this.afs.collection('messages').snapshotChanges(['modified'])
      .pipe(takeUntil(untilFn))
      .subscribe(modified => {});
    

    Please note the return type of snapshotChanges is different than valueChanges.