MooseMan55
MooseMan55

Reputation: 544

Firebase Cloud Function sends multiple of the same notification to the same devices

I have a firebase cloud function that is supposed to send a notification to the 5 closest phones when a new "emergency" is added to the database. The code I wrote does send a notification to the 5 closest phones, but It sends that same notification over and over again. It gets even worse when my users log on or off because then it sends even more. I'm confused why my cloud function doesn't just operate once and then terminate.

Here is the code for my cloud function:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

exports.sendPushNotificationAdded = functions.database.ref('/emergencies/{id}').onCreate(event => {
    return admin.database().ref('/tokens').on('value', function(snapshot) {
        var efarArray = snapshotToArray(snapshot, event.data.child('latitude').val(), event.data.child('longitude').val());
        efarArray.sort(function(a, b) {
            return a.distance - b.distance;
        });
        var payload = {
            notification: {
                title: "NEW EMERGANCY!",
                body: "Message from Patient: " + event.data.child('other_info').val(),
                //badge: '1',
                sound: 'default',
            }
        };
        var options = {
          priority: "high",
          timeToLive: 0
        };
        tokens_to_send_to = [];
        if(efarArray.length >= 5){
            //only send to the 5 closest efars
            for (var i = 4; i >= 0; i--) {
                tokens_to_send_to.push(efarArray[i].token);
            }
        }else{
            for (var i = efarArray.length - 1; i >= 0; i--) {
                tokens_to_send_to.push(efarArray[i].token);
            }
        }
        //TODO: send a messaged back to patient if no efars respond or are found?
        return admin.messaging().sendToDevice(tokens_to_send_to, payload, options).then(response => {

        });
    });
});

//code for function below from https://ilikekillnerds.com/2017/05/convert-firebase-database-snapshotcollection-array-javascript/
function snapshotToArray(snapshot, incoming_latitude, incoming_longitude) {
    var returnArr = [];

    snapshot.forEach(function(childSnapshot) {
        var distance_to_efar = distance(childSnapshot.child('latitude').val(), childSnapshot.child('longitude').val(), incoming_latitude, incoming_longitude);
        var item = {
            latitude: childSnapshot.child('latitude').val(), 
            longitude: childSnapshot.child('longitude').val(),
            token: childSnapshot.key,
            distance: distance_to_efar
        };

        returnArr.push(item);
    });

    return returnArr;
};

If more clarification or code is needed just let me know. I've been stuck on this forever...

Upvotes: 1

Views: 652

Answers (1)

Doug Stevenson
Doug Stevenson

Reputation: 317828

Don't use on() with Cloud Functions. That's almost never the right thing to use, since it adds a listener that could be invoked any number of times as the database changes. Use once() to get a single snapshot and act on that.

Also, you must return a promise from the function that resolves when all the asynchronous work in that function is complete. on() doesn't return a promise, so your function isn't doing that as well.

You might want to study some of the official sample code and follow the patterns established there.

Upvotes: 3

Related Questions