Child_changed downloads everything on page load - javascript

var notifRef = new Firebase(webSocketBaseURL + "/notifications/custom:" + userId);
var $results = $("#notifications_nav"); //div in nav for notifcations
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) {
addNotifications(snap.val());
checkNotifications();
}
count++;
});
});
notifRef.on('child_changed', function(snapshot) {
var notification = snapshot.val();
var existingNotif = $results.find('.top-cart-item').eq(0);
if (notification.type == existingNotif.data("type") && notification.link == existingNotif.find("a").attr('href') && notification.message == existingNotif.find("a").text()) {
existingNotif.find(".top-cart-item-price.time").livestamp(moment(parseInt(notification.timestamp)).unix());
existingNotif.find(".top-cart-item-quantity").text("x " + notification.count);
checkNotifications(); // append +1 to new notifications
}
});
Hello, here is basically problem
When I have the child_changed event, on page load all the data from the firebase URL is loaded. Why is child_changed loading at all on page load? Should it be just listening if some of the data is changed and then only shooting notification?
I checked the frames under network tab in inspect element and indeed, when notifRef.on('child_changed') is commented out, only 5 last notifications are downloaded as it is in notifRef.limitToLast(5).once("value".
Let me know if you want to know more details or what am obvious thing am I missing here?
EDIT:
What is happening in code it is like facebook top right corner notification area for 5 latest notifications.
notifRef.limitToLast(5).once("value" this is pulling 5 latest notifications. I'm doing there a loop to get the last key. Now next part is how I realized your most common asked question: how to get children that were added after the page load?
Because in previous part in limitToLast(5) I got the latest key added, with notifRef.orderByKey().startAt(lastKey).on('child_added' I am listening to only new added children, since its ordered by key and started from the last one.
Now is the tricky part, since one notification type is new message from user and if user sends multiple times, then instead of adding every time new child, the last key just has a new incrementing property count which means how many new messages are received from user. But this is only in the case when the last notification was a message from that user. If last notification was not a message from that user and next notification is a new message from user, then it just adds new child.

Let's slice-and-dice your code a bit, to see what's going on:
var notifRef = new Firebase(webSocketBaseURL + "/notifications/custom:" + userId);
notifRef.on('child_changed', function(snapshot) {...
Note that you're not ordering or limiting the notifRef in any way here. So you're listening for child_changed events on all children at that location.
But the way you're mixing value and child_ events and the different queries are tricky for me to parse, so I might have misunderstood your use-case.

Related

In what circumstances a patch of event attendees can lead to the deletion of said event in Google Calendar?

I developed an application for the school I work for that create a bridge between our registration system and Google Calendar. In short, it populates multiple calendars (teachers, students, classrooms, and a calendar that contain all courses) based on data from the registration system, it allows teacher to register student attendance and homework for each class and it does a bunch of other stuff.
The application also allows a teacher to trigger an update of the student list when he notices a registered student is not entered as an attendant in the calendar events.
In the code:
I make a call to the registration system to get the updated list of students for the course.
I make a call to Google Calendar to get the recurrences of the events that represent the course in the calendar.
I make a call to Google Calendar to batch patch the "attendees" array of every recurrence
this.updateEvent = function(calendarId, eventId, resource)
{
return $window.gapi.client.calendar.events.patch({
calendarId: calendarId,
eventId: eventId,
resource: resource
});
};
this.batchUpdateEvents = function(calendarId, eventList)
{
var counter = 0;
var batchPromises = [];
for(var i=0; i < eventList.length; i++)
{
if(counter === 0)
{
var batch = $window.gapi.client.newBatch();
}
var event = eventList[i];
batch.add(this.updateEvent(calendarId, event.id, event.resource), {id: event.id});
counter++;
if(counter === 50 || i === eventList.length-1)
{
counter = 0;
batchPromises.push(batch.then());
}
}
return $q.all(batchPromises).then(
function(response){
var updateResults = {};
response.forEach(function(batchResponse){
updateResults = Object.assign(updateResults, batchResponse.result);
});
return updateResults;
});
};
It works fine in most cases but regularly all the calendar events associated with the course are deleted. I can't figure out how to reproduce the bug and since everything is coded in javascript I can't log the errors.
In this process, this is the only 2 times my application interacts with Google Calendar. I really don't see how a get request could delete anything so I assume the batch of patch requests is the problem. Although the only thing I include in the patch is the attendee array so its actually hard to mess it up to that point.
Has anybody got an idea of the direction I should look into? I am a bit at loss here.
It's possible that batchUpdateEvents could be receiving a valid calendarId, but an eventList with blank values. If that's the case checking for a valid value in eventList[0].resource before proceeding to the for loop would prevent blank event values from being saved (which might be causing Google to delete the event). If this script should only add events and never delete them it could also be beneficial to check event.resource before adding it to the batch.

Iterate through multiple web pages in JS with sleep in between

I was checking some simple solutions for showing multiple web pages for some dashboard and currently fighting with simple HTML page with javascript inside to achieve what I want to see there.
var urls = new Array();
urls[0] = "https://stackoverflow.com/"
urls[1] = "https://www.google.com"
var arrayLength = urls.length;
for (var i = 0; i < arrayLength; i++) {
window.location.assign(urls[i]);
sleep(3000);
}
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds) {
break;
}
}
}
Currently this page opens only first page (after some time) and looks like it doesn't do iteration trough other pages. Maybe you could help me to make it work? I want to rotate those pages forever on screen (will add some infinite while loop after making this part working).
Currently this page opens only first page (after some time) and looks
like it doesn't do iteration trough other pages.
Once you change your window.location, and go to the first url from the array, you are losing all of your JS code (as it is not present in just opened url any more).
You can do this by installing a chrome plugin (which will not lose your JS after window.location change).
The plugin will run the added JS at DOMContentLoaded (no need to attach any event listener).
I needed also to do this, check things on the page, store some information and move on to the next page. I know, this can be done with Python and other stuff but by doing this it can be done on the FE side also.
I used the localStorage to store my information.
I pasted this into the browser console to prepare all the stuff and clean the localStorage:
// clear the localStorage
localStorage.clear();
// set an array that will keep all our pages to iterate into the localStorage
localStorage.setItem(
"pages",
JSON.stringify([
"https://my-page-1.html",
"https://my-page-2.html",
"https://my-page-3.html",
"https://my-page-4.html",
])
);
// set an array that will keep our findings
localStorage.setItem("resultArray", JSON.stringify([]));
// move to the first page of the iteration
window.location.href = "https://my-page-1.html";
After doing this, I opened the plugin interface and added the following code:
(function check() {
// array saved into the localStorage that contains all the pages to iterate
const pagesArray = JSON.parse(localStorage.getItem("pages"));
// array to store your stuff
const resultArray = JSON.parse(localStorage.getItem("resultArray"));
// whatever you want to check on that page
const myFancyCondition = true;
if (myFancyCondition) {
// push any data to the array so that you can check it later
resultArray.push({
page: pagesArray[0],
message: "I found what I was looking for!",
});
}
//remove the current page from the array
pagesArray.shift();
//reset the array value after the first page was already checked
localStorage.setItem("pages", JSON.stringify(pagesArray));
//store the array data
localStorage.setItem("resultArray", JSON.stringify(resultArray));
// quit if the iteration is over and there are no more pages to check
if(!pagesArray.length) return;
//go to the next page
window.location.href = pagesArray[0];
})();
Then, to check the results you just need to read the data from the localStorage like:
JSON.parse(localStorage.getItem('resultArray'))
I hope this helps :)!

Firebase JavaScript value Change Events

I am wondering how i can solve the following issue. I have a Record in the Firebase DB which i am monitoring. The App is a Sports Score So far so good.
When user loads the initial page i check if Game is Running or Stopped and so a few things.
below is a snipped of what i do
if(Clock.Status=='Running'){
......
}
else if(Clock.Status == 'Stopped'){
.......
}
So far so good when user hits the page for first time. But now i want to monitor if the ClockStatus changes
clockStatusRef = firebase.database().ref("games/"+gameId+"/Score/");
clockStatusRef.on("child_changed", function(snapshot) {
var Clock = snapshot.val();
var status = Clock.Status;
// clock stopped - second scenario
if(status=='Stopped'){
stopTimer();
}
else if(status == 'Running'){
// clock status running- third scenario
firebase.database().ref('/.info/serverTimeOffset')
.once('value')
.then(function stv(data) {
console.log('hi');
serverTime = (data.val() + Date.now())/1000;
var timeElapsed = serverTime - Clock.ClockStart;
var totalCounts = document.getElementById("total_counts");
if(Clock.Timer > timeElapsed){
initTimer(Math.floor(Clock.Timer- timeElapsed),60);
}
else{
var Current_Clock = document.getElementById("count");
Current_Clock.innerHTML = '00:00';
}
}, function (err) {
return err;
});
}
console.log("Clock status changed: "+status);
});
for some strange reason on a change of status it starts with the main if
if(Clock.Status=='Running')
So i am wondering what am i missing and what is the best way to fix this so the first if is only run on the initial load and all subsequent will use the if's which handle status change of clock.
Here is the Json for games/B8120ACD-DF51-A64A-A83E-556007522E80/Score/Clock
{
"ClockStart" : 1510535632,
"Period" : 1,
"Status" : "Stopped",
"Timer" : 900
}
You're listening one level higher in your JSON than your code expects.
Either change the code that gets the clock from the snapshot to:
var Clock = snapshot.val().Clock;
Or (better, because it requires less data transfer) listen one level lower in the tree:
clockStatusRef = firebase.database().ref("games/"+gameId+"/Score/Clock");
As there seem to be some limitation as far as what is triggered when multiple listeners looking for changes and data overlaps i changed my code. As my App does not have any heavy traffic so changes are not that often, i use one listener for changes and to address my issue i just went ahead and added the run once for the initial setup then run different code on updates. Would have been nice to control what listener gets the notification of change, also the child_changed seems to have its limitations as i got it to fire but was not able to tell which child actually changed.

How to retrieve previous tokens in a MultiInput control when a tokenChange event is triggered?

In MultiInput control, when new tokens are added to the control, the old tokens are flushed off.
How do I get the previous tokens, when a new token(s) is added, as when the attachTokenChange event is triggered.
Here is my effort till now:
var oFilter = oSmartFilterBar.getControlByKey("Filter");
// Trying to get the current tokens when the control has focus
oFilter.attachBrowserEvent("onfocus", function(oEvent)
{
oFilter._tempTokens = oFilter.getTokens();
});
oFilter.attachTokenChange(function(oEvent)
{
var existingTokens = oFilter.getTokens();
var oAddedTokens = oEvent.getParameters("addedTokens").token;
}
});
The focus event is not triggered and the attachTokenChange event is triggered every time there is a token change and only the latest values are fetched. Hence the variables existingTokens and oAddedTokens have the same value.
My question is how do I get all the tokens in the control, before the change event is triggered.
You should be able to reconstruct the previous content of the tokens aggregation: old = new - added + removed.
The tokenChange event arguments tell you which tokens have been added and which have been removed. You have to take the type parameter into account.
Example on plunker:
onTokenChange: function(oEvent){
var newCollection = oEvent.getSource().getTokens();
var parameters = oEvent.getParameters();
if(parameters.type==="tokensChanged"){
//You can ignore the added, removed and removeAll type events
// as tokensChanged is always fired.
var oldCollection = newCollection.filter(function(t){return parameters.addedTokens.indexOf(t)<0});
Array.prototype.push.apply(oldCollection,parameters.removedTokens);
//oldCollection: here you are.
}
}

Get only new "child added" after page load

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
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.

Categories