async subscribeToChatMessages(id, userId, callback) {
    return chatsCollection
      .doc(id)
      .collection('messages')
      .orderBy('dateTime', 'desc')
      .limit(CHAT_MESSAGES_BATCH_SIZE)
      .onSnapshot((snapshot) => {
        snapshot.docChanges().forEach((change) => {
          // note that change type `removed`, `added`, `modified` is called for newly sent messages, while change
          // type `added` is only called for loading the first batch of messages
          const data = change.doc.data();
          if (!data.dateTime && snapshot.metadata.hasPendingWrites) {
            return;
          }

          // change type `added` is for loading first batch of messages
          // change type `modified` is for newly sent messages
          // link: https://medium.com/firebase-developers/the-secrets-of-firestore-fieldvalue-servertimestamp-revealed-29dd7a38a82b
          if (change.type === 'modified' || change.type === 'added') {
            callback(change.doc);
          }
        });
      });
  }
/**
   * Subscribe to messages belonging a chat
   * @param {string} id The id of the chat
   * @param {string} userId The id of the user
   * @param {function(object): void} callback The function to call to handle the new chat message
   * It will also return a CHAT_MESSAGES_BATCH_SIZE of chat messages belonging to the chat on the initial subscription
   * It is recommended to use this function to fetch the first batch of chat messages and use the getChatMessages to get older chat messages
   *  The first argument is doc. The firebase document that is changed
   * @throws {FirebaseError}
   * @return {function} The subscriber function. Needed to unsubscribe from the listener
   */
Subscribe to messages belonging a chat
@param {string} id The id of the chat
@param {string} userId The id of the user
@param {function(object): void} callback The function to call to handle the new chat message
It will also return a CHAT_MESSAGES_BATCH_SIZE of chat messages belonging to the chat on the initial subscription
It is recommended to use this function to fetch the first batch of chat messages and use the getChatMessages to get older chat messages
The first argument is doc. The firebase document that is changed
@throws {FirebaseError}
@return {function} The subscriber function. Needed to unsubscribe from the listener
Subscribe to messages belonging a chat @param { string } id The id of the chat @param { string } userId The id of the user @param { function ( object ) : void } callback The function to call to handle the new chat message It will also return a CHAT_MESSAGES_BATCH_SIZE of chat messages belonging to the chat on the initial subscription It is recommended to use this function to fetch the first batch of chat messages and use the getChatMessages to get older chat messages The first argument is doc . The firebase document that is changed @throws { FirebaseError } @return { function } The subscriber function . Needed to unsubscribe from the listener
async subscribeToChatMessages(id, userId, callback) {
    return chatsCollection
      .doc(id)
      .collection('messages')
      .orderBy('dateTime', 'desc')
      .limit(CHAT_MESSAGES_BATCH_SIZE)
      .onSnapshot((snapshot) => {
        snapshot.docChanges().forEach((change) => {
          const data = change.doc.data();
          if (!data.dateTime && snapshot.metadata.hasPendingWrites) {
            return;
          }
          if (change.type === 'modified' || change.type === 'added') {
            callback(change.doc);
          }
        });
      });
  }
async subscribeToChatMessages ( id , userId , callback ) { return chatsCollection . doc ( id ) . collection ( 'messages' ) . orderBy ( 'dateTime' , 'desc' ) . limit ( CHAT_MESSAGES_BATCH_SIZE ) . onSnapshot ( ( snapshot ) => { snapshot . docChanges ( ) . forEach ( ( change ) => { const data = change . doc . data ( ) ; if ( ! data . dateTime && snapshot . metadata . hasPendingWrites ) { return ; } if ( change . type === 'modified' || change . type === 'added' ) { callback ( change . doc ) ; } } ) ; } ) ; }
Subscribe to messages belonging a chat
 @param {string} id The id of the chat
 @param {string} userId The id of the user
 @param {function(object): void} callback The function to call to handle the new chat message
 It will also return a CHAT_MESSAGES_BATCH_SIZE of chat messages belonging to the chat on the initial subscription
 It is recommended to use this function to fetch the first batch of chat messages and use the getChatMessages to get older chat messages
  The first argument is doc.
Subscribe to messages belonging a chat @param { string } id The id of the chat @param { string } userId The id of the user @param { function ( object ) : void } callback The function to call to handle the new chat message It will also return a CHAT_MESSAGES_BATCH_SIZE of chat messages belonging to the chat on the initial subscription It is recommended to use this function to fetch the first batch of chat messages and use the getChatMessages to get older chat messages The first argument is doc .
// note that change type `removed`, `added`, `modified` is called for newly sent messages, while change // type `added` is only called for loading the first batch of messages // change type `added` is for loading first batch of messages // change type `modified` is for newly sent messages // link: https://medium.com/firebase-developers/the-secrets-of-firestore-fieldvalue-servertimestamp-revealed-29dd7a38a82b