0

I develop in iOS and this is the first time I'm coding in Typescript. I made my first Cloud Function that triggers when a new chat message is sent in RTDB and notifies the members that receive the message.

When I make the call to Firestore to get tokens of the user devices (userTokens) I get no errors and have the correct path, but the data returned from those promises don't show anything. When I log the "tokenList" it just says ["0"]. I think the error is when I push the promise to a list or resolve them, but I haven't managed to fix it.

The code:

import * as functions from "firebase-functions"
import * as admin from "firebase-admin"
admin.initializeApp()

export const newLastMessageDetected = functions.database
.ref('/ChatMessages/{chatID}/{messageID}/')
.onCreate(async (snap, context) => {
    const values = snap.val()
    const chatID = context.params.chatID
    const messageID = context.params.messageID
    const message = values.message
    const fromID = values.fromID
    const fromName = values.fromName

    console.log( `LastMessage changed with chatID: ${chatID} and messageID ${messageID} `)
    console.log( `Last message: ${message} by fromID: ${fromID} and by name ${fromName}`)

    const payload = { 
        notification: {
            title: fromName,
            body: message
            
        }
    }

    let membersSnapshotRef =  admin.database().ref('/Members/' + chatID + '/')

    return membersSnapshotRef.once('value')
        .then(dataSnapshot => {
            const promises = []
            console.log('*** GOT SNAPSHOT ***')
            dataSnapshot.forEach((element) => {
                if (element.key != fromID && element.val() === true) {
                    const p = admin.firestore().collection('userTokens').doc(`${element.key}`).collection('devices').get()
                    console.log('*** GOT PROMISE ***')
                    console.log(`*** The recipientID: ${element.key} ***`)
                    console.log(`${p}`)                    
                    promises.push(p)
                }
                
            })
            return Promise.all(promises).then(snapshot => {
                console.log('*** GOT RETURNED PROMISES ***')
                const tokenList = []
                const data = snapshot.keys()
                for (const token in data) {
                    console.log(`${token}`)
                    tokenList.push(token)
                }
                console.log(`${tokenList}`)
                return admin.messaging().sendToDevice(tokenList, payload).then(result => {
                    console.log("Notification sent!");
                    return null;
            })

           
        })
        .catch(error => {
            console.log(`${error}`)
        })
       
    })
    
})
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Thel
  • 396
  • 2
  • 8

2 Answers2

2

When you use Promise.all(), the result of the promise it returns is always going to be an array.

Promise.all(promises).then(snapshot => {
    // snapshot is an array of results with one element for each of the promises
})

You need to iterate that array to find the results of all the promises you stored in the promises array. snapshot.keys() does not iterate that array - it is just giving you a list of numbers that are the indexes of those array. Try using snapshot.forEach() instead.

You might want to review some documentation for promise.all.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
  • Thank you! Your videos are really helpfull. I actually also had to do another forEach loop. – Thel Feb 26 '22 at 11:37
1

I actually really messed up because I tried to retrieve the data on the query; didn't realize that the first loop was on the retrieved queries, so I had to do another on the documents retrieved. The device tokens are each of the documentIDs with the timestamp stored as the data.

The working code:

import * as functions from "firebase-functions"
import * as admin from "firebase-admin"
admin.initializeApp()

export const newLastMessageDetected = functions.database
.ref('/ChatMessages/{chatID}/{messageID}/')
.onCreate(async (snap, context) => {
    const values = snap.val()
    const chatID = context.params.chatID
    const messageID = context.params.messageID
    const message = values.message
    const fromID = values.fromID
    const fromName = values.fromName

    console.log( `LastMessage changed with chatID: ${chatID} and messageID ${messageID} `)
    console.log( `Last message: ${message} by fromID: ${fromID} and by name ${fromName}`)

    const payload = { 
        notification: {
            title: fromName,
            body: message
            
        }
    }

    let membersSnapshotRef =  admin.database().ref('/Members/' + chatID + '/')

    return membersSnapshotRef.once('value')
        .then(dataSnapshot => {
            const promises = []
            // const docIDS = []
            console.log('*** GOT SNAPSHOT ***')

            dataSnapshot.forEach((element) => {
                if (element.key != fromID && element.val() === true) {
                    const doc = admin.firestore().collection('userTokens').doc(`${element.key}`).collection('devices')
                    const p = doc.get()
                    // const docID = doc.id
                    console.log('*** GOT PROMISE ***')
                    console.log(`*** The recipientID: ${element.key} ***`)
                    // console.log(`*** The docID: ${docID} ***`)
                    promises.push(p)
                    // docIDS.push(docID)
                }
                
            })
            return Promise.all(promises)
        })
        .then(async querySnapshot => {
            console.log('*** GOT RETURNED PROMISES ***')
            const tokenList = []
            
            querySnapshot.forEach(snap => { // first here
                console.log(`${snap.id}  *** `) 
                console.log(`${snap}  *** `) 
                
                snap.forEach(doc => { // then here
                    console.log(`${doc.id}`)
                    tokenList.push(doc.id)
                })
            
            })

            await admin.messaging().sendToDevice(tokenList, payload)
            console.log("Notification sent!")
            return null
           
          
        })
        .catch(error => {
            console.log(`${error}`)
        })
    
})

Thel
  • 396
  • 2
  • 8