0

I have on a website facebook style notification in top right corner. I show there up to 5 latest notifications. I do initial pulling with child_added and also after same firebaseRef child_added listening for new notifications.

Now I'd like to play a sound on new notification and a little number of new notifications.
The only thing I can't figure is how to distinguish when was a new notification and when was it already seen, a.k.a page reload? Is there any other approach than making some new property read?

I was looking around and found some old answers from 2012 with suggestions limitToLast(1) which doesn't help in my case.

EDIT:

https://stackoverflow.com/a/27693310/633154 This @Kato answers recommends to listen only to new notifications which time is more than current Firebase time Firebase.ServerValue.TIMESTAMP. This seems the way to go, but I am creating a new notification with REST API and myself setting timestamp as my server's UTC. So there may be some minor inconsistencies. Shouldn't be a big deal

EDIT 2:

With this query, I'm getting correctly up to 5 last notifications on page load and no new notifications are coming afterwards

notifRef.limitToLast(5).once("value", function(snapshot) {
    snapshot.forEach(function(data) {
        addNotifications(data.val());
    });
});

In the above linked other SO thread @Kato's answer doesn't work, notifRef.orderBy is not a function.
I have tried multiple other versions according to doc
https://www.firebase.com/docs/web/guide/retrieving-data.html#section-queries

My structure is same

{
  "messages": {
     "$messageid": { // firebase generated key 'JqcEWLFJrl1eaed5naN'
        "sender": "kato",
        "message": "hello world"
        "timestamp": 1433036536108  // Firebase.ServerValue.TIMESTAMP
     }
  }
}

Here is what i tried to do and errors I'm getting:

var queryRef = notifRef.orderByKey().startAt(Firebase.ServerValue.TIMESTAMP);
Error:Query: When ordering by key, the argument passed to startAt(), endAt(),or equalTo() must be a string.

var queryRef = notifRef.orderByChild('timestamp').startAt(Firebase.ServerValue.TIMESTAMP);
Error: Query: First argument passed to startAt(), endAt(), or equalTo() cannot be an object.

In the documentation I have not seen that to startAt anything but the element position is passed (integer) but not a firebase timestamp object, that's why such error.

Only below compiles, just having startAt without ordering, but it's not shooting any new notifications!

var queryRef = notifRef.startAt(Firebase.ServerValue.TIMESTAMP);
queryRef.on('child_added', function(snap) {
    console.log(snap.val());
    addNotifications(snap.val());
    // TODO clean up if more than 5 notifications
});

Any idea where could be the problem? What is the correct way to listen only to newer notifications than current timestamp?

EDIT 3

Here is my final solution

notifRef.limitToLast(5).once("value", function(snapshot) {
    var lastKey = null; // at least 1 key is always present
    var count = 0; // because startAt is inclusive, we have to ignore first child_added
    snapshot.forEach(function(data) {
        addNotifications(data.val());
        lastKey = data.key();
    });
    checkNotifications();
    notifRef.orderByKey().startAt(lastKey).on('child_added', function(snap) {
        if (count > 0) {
            console.log(snap.val());
            addNotifications(snap.val());
            // TODO clean up if more than 5 notifications
            checkNotifications();
        }
        count++;
    });
});

I don't trust browser time, so had to go first by querying last 5 existing keys, and after that passing to startAt the last key I received. notifRef.orderByKey().startAt(lastKey) can't be outside notifRef.limitToLast(5).once("value" because according to doc, once is queried last so the lastKey js variable passed to startAt would be always null.
Also need to have the count variable, because startAt is taking inclusive, but because it was already there, I need to ignore the first one.
Also with this solution when there are more than 5 notifications, I query my backend with checkNotifications only once at the end when notifications are received with once query. Otherwise on child_added it would do up to 5 times on every page load.

If there is anything that could be optimized, please tell

Community
  • 1
  • 1
Skyzer
  • 584
  • 3
  • 11
  • 27
  • possible duplicate of [How to retreive only new data?](http://stackoverflow.com/questions/18270995/how-to-retreive-only-new-data) – Frank van Puffelen May 27 '15 at 21:51
  • @FrankvanPuffelen thanks, saw that, but as some1 mentioned there `once('value'` is not efficient because I may have hundreds of notifications and will pull all them, but I need only up to 5 latest initially and then listen only for new ones – Skyzer May 29 '15 at 00:34
  • That last code snippet will not work, since you're not sorting on timestamp yet. if `ref` refers to the root of your data structure, you'll need `ref.child('messages').orderByChild('timestamp').startAt(Date.now()).once('child_added', function....`. Where you'll want to replace `Date.now()` with the timestamp you last checked. Note that all your edits and comments are not helping making your question any clearer. If you're still having the same problem, you'll often get a better response by creating a minimal fiddle that reproduces it and posting that. – Frank van Puffelen Jun 03 '15 at 04:20

1 Answers1

3

One solution would be to have your local client listen for the last 5 latest notifications via ref.limitToLast(5).on('child_added', ...) and then only render them to the user if some timestamp field on each of those notifications is newer than your local timestamp on the machine.

When writing those notifications from other clients, you could include a timestamp field as specified via Firebase.ServerValue.TIMESTAMP, which will use the server's notion of the Unix timestamp. Readers of that data could then compare that timestamp to their local clock to make the aforementioned determination.

Rob DiMarco
  • 13,226
  • 1
  • 43
  • 55
  • Thanks, this is what I currently have `notifRef.limitToLast(5).on("child_added", function(snapshot) { addNotifications(snapshot.val(), snapshot.key()); });` this is firing every page load, so i would have another `ref` where only listening to notifications `ref.orderBy('created').startAt(Firebase.ServerValue.TIMESTAMP);` like @Kato answer in another thread (link in my question edit) – Skyzer May 29 '15 at 00:01
  • hello, it's still not solved, please refer to my last comment in @Katos answer http://stackoverflow.com/a/27693310/633154 – Skyzer May 31 '15 at 04:53
  • Also I don't get your solution by getting user browser timestamp, how can i rely on user browser it, if it's not UTC and in firebase everything is stored UTC – Skyzer May 31 '15 at 07:22
  • @Skyzer `new Date().getTime()` returns the UNIX timestamp in the same format as Firebase.ServerValue.TIMESTAMP. – Rob DiMarco Jun 01 '15 at 18:22
  • 1
    Thank you bro, yours and Kato suggestions lead to me correct path! My last edit has my final approach. If there is anything that can be optimized, you could suggest – Skyzer Jun 08 '15 at 00:10