Check if user is online in Firebase - javascript

I found an example of what I need at Google Firebase documentation here.
Still, I want to modify it a bit to make it check the presence of user every second/10 seconds or at least every minute depending on how this will affect the load on the server so I came up with this:
TestApp.prototype.initFirebase = function() {
this.database = firebase.database();
this.database.ref(".info/connected").on("value", this.isOnline.bind(this));
};
TestApp.prototype.isOnline = function(snap) {
var i=0;
console.log(snap.val());
setInterval(function() {
if (snap.val() === true) {
console.log("connected"+(i+=10));
} else {
console.log("not connected");
}
}, 10000);
}
But here is what happens in the console if I run it:
main.js:34 false
main.js:91 User signed out
main.js:34 true
main.js:39 not connected
main.js:37 connected10
main.js:39 not connected
main.js:37 connected20
main.js:39 not connected
main.js:37 connected30
main.js:39 not connected
main.js:37 connected40
It triggers the function every 10 seconds but it shows me both results which are connected and disconnected at the same time. (actually, there is delay around 1 second) Moreover, it completely ignores if user is logged in or not and shows me the same logs every time.
I want it to run isOnline method only if the user is logged in(with email and password). And if that is true isOnline should send request to the server if user is online every N seconds. Is there anything I can do?
It would be much better if I could check if user is connected from the server side because I need to make some actions as long as user remains online. But I am not sure if that's possible so I think the best way is to check on frontend and trigger actions with HTTP triggers.

The current behavior is due to the values of snap being captured in the timer closures.
When you are setting up isOnline to trigger on value event, Firebase invokes the method every time the value of the key changes. In this case, Firebase invoked isOnline first time when it determined that the value is false, and then second time after login was established and the value became true.
Now inside isOnline, you are starting off timeouts. As the function was called twice with different snap objects, two timeouts were created. But both of them have their own snap objects in their closures, which are fixed to the values when isOnline was invoked.
As the end-result, you have two perpetual timers running which keep printing the historic snap values :).
As you mentioned that you only want to do something periodically if and only if the user is online, you should try this instead:
isOnline : function(snap){
let test = snap.val();
// If the user is not online,
// check if we had the timer set,
// as we should clear it now.
// It essentially means user went offline.
if (!test && this.whenOnline) {
clearTimeout(this.whenOnline);
this.whenOnline = null;
return;
}
// If the user is not online, return.
if (!test){
return;
}
// User is online. Install the timer if we haven't.
if (!this.whenOnline){
this.whenOnline = setTimeout(this.doSomethingWhenOnline, 10000);
}
}
doSomethingWhenOnline : function(){
// whatever;
// Cue the next timer again, notice we only install if
// the previous instance has not been cleared by the
// isOnline handler.
if (this.whenOnline){
this.whenOnline = setTimeout(this.doSomethingWhenOnline, 10000);
}
}

Related

How to handle inactive users and log them out in react JavaScript?

I'm working on a private site with important data, and I want to log out any user who hasn't done anything for 10min (forgot the tab open in the background as an example). How can I do that, is just running an event listener to the mouse clicks with a timer is fine, or are there other better solutions for this?
This can be achieved by JavaScript only.
Since our web app can be open in multiple tab, so its better to store last activity of user in localStorage
First lets declare events which we consider as user activity and store time of user activity in localStorage
document.addEventListener("mousemove", () =>{
localStorage.setItem('lastActvity', new Date())
});
document.addEventListener("click", () =>{
localStorage.setItem('lastActvity', new Date())
});
Next lets create interval which will be checked on every given interval.
let timeInterval = setInterval(() => {
let lastAcivity = localStorage.getItem('lastActvity')
var diffMs = Math.abs(new Date(lastAcivity) - new Date()); // milliseconds between now & last activity
var seconds = Math.floor((diffMs/1000));
var minute = Math.floor((seconds/60));
console.log(seconds +' sec and '+minute+' min since last activity')
if(minute == 10){
console.log('No activity from last 10 minutes... Logging Out')
clearInterval(timeInterval)
//code for logout or anything...
}
},1000)
event listeners for mouse movement (not clicks), and event listeners for keyboard clicks as well should do the job.
Let's say you want to log out the user after 10mins of inactivity. Simply start a timer (for 10mins) the moment user logs in, every time the user makes any mouse movement or any keyboard clicks, use the event listener to reset the timer.
If there is no activity, after 10mins, the timer should execute log out functionality.
Update :
Like the case suggested by Vishnu Bhadoriya, in that case, the idle solution should be to implement a heartbeat b/w the client and the server. Now, in this case, your mouse and keyboard listeners would send a lifeline event to the server, and the server will keep its session alive. When the lifeline event is not received by the server for more than 10mins (threshold for activity), the server can emit an event to the client which can log him out or can simple invalidate the client's auth token

Performance issues with using localStorage / sessionStorage?

I am trying to detect user inactivity for a certain amount of time and based on it need to perform certain actions.
I am using localStorage for this so that i set the idle time start across all tabs open.
below is the relevant part of my code
const detect = () => {
//console.log(`time is ${idleStartTime}`);
if(Date.now() - getIdleStartTime() > ONE_HOUR){
console.log('idle time more than one hour....');
console.log(`${getIdleStartTime()} ended`);
alert('idle time more than one hour....');
} else {
idleAnimationFrameId = requestAnimationFrame(function() {
detect();
});
}
};
const startTimer = () => {
idleAnimationFrameId = requestAnimationFrame(function() {
detect();
});
};
function setIdleStartTime() {
return new Promise((resolve, reject) => {
if (storageAvailable('localStorage')) {
// Yippee!
localStorage.setItem('idleStartTime', Date.now());
console.log('idle time has been set...');
resolve(true);
}
else {
// Too bad, no localStorage for us
console.log('no local storage support...');
reject('no local storage support.')
}
});
}
I mark the user as active if by listing to the following events. ['mousedown', 'mousemove', 'keydown', 'scroll', 'touchstart'];
function setEventListners() {
activityEvents.forEach(function(eventName) {
document.addEventListener(eventName, activity, true);
});
}
function activity() {
console.log('user activity detected...');
localStorage.setItem('idleStartTime', Date.now());
}
I see that in some cases there are 1.5K request sent in a very short time to localstorage to set the value. I see user activity detected... printed on the console around 1.5k times in matter of seconds.
My couple of questions are
will there be any performance issues as i am setting the value idleStartTime in localStorage and local storage set will be called thousands of time in a few seconds.
Are there any better alertnatives than using localStorage for my scenario.
Thanks.
Answer to Question-1
There will be no memory limit issue since every time you call localStorage.setItem, you are overwriting on the previous value. But you mentioned that your activity change is firing 1.5K times in a very short amount of time. That will increase disk usage(I/O) time.
Answer to Question-2
You can use setTimeout instead of event listeners. Many website use setTimeout 1 or 2 minutes to detect idle time. The logic is to check whether all your input fields are same as 2 minutes ago. If they are same then it is considered that the page is in idle mode for that amount of time. I know this approach seems ancient, but it will significantly reduce I/O time.
You can skip cursor move event. That alone will reduce the checking in a huge amount.

Firebase Web SDK - Transaction causing on('child_added', func.. to be called

See jsbin.com/ceyiqi/edit?html,console,output for a verifiable example.
I have a reference listening to a database point
jobs/<key>/list
where <key> is the teams unique number
Under this entry point is a list of jobs
I have a listener on this point with
this.jobsRef.orderByChild('archived')
.equalTo(false)
.on('child_added', function(data) {
I also have a method that does the following transaction:
ref.transaction(function(post) {
// correct the counter
if(post)
{
// console.log(post);
if(active)
{
// if toggeling on
}
else
{
// if toggeling off
}
}
return post;
})
When invoking the transaction the child_added is also invoked again, giving me duplicate jobs.
Is this expected behavior?
Should I simply check to see if the item has been got before and add to the array accordingly?
Or am I doing something wrong?
Thanks in advance for your time
You're hitting an interesting edge case in how the Firebase client handles transactions. Your transaction function is running twice, first on null and then on the actual data. It's apparent to see if you listen for value events on /jobs, since then you'll see:
initial value
null (when transaction starts and runs on null)
initial value again (when transaction runs again on real data)
The null value in step 2 is the client's initial guess for the current value. Since the client doesn't have cached data for /jobs (it ignores the cached data for /jobs/list), it guesses null. It is clearly wrong. But unfortunately has been like this for a long time, so it's unlikely to change in the current major version of the SDK.
And because of the null in step 2, you'll get child_removed events (which you're not handling right now) and then in step 3 you'll get child_added events to re-add them.
If you handled the child_removed events, you're items wouldn't end up duplicated, but they would still disappear / reappear, which probably isn't desirable. Another workaround in the current setup is to explicitly tell the transaction to not run with the local estimate, which you can do by passing in false as the third parameter:
function transact() {
var path = 'jobs/';
var ref = firebase.database().ref(path);
ref.transaction(function(post) {
return post;
}, function(error, committed, snapshot) {
if (error) {
console.log('Transaction failed abnormally!', error);
} else if (!committed) {
console.log('Transaction aborted.');
} else {
console.log('Transaction completed.');
}
}, false);
}
I'm sorry I don't have a better solution. But as I said: it's very unlikely that we'll be able to change this behavior in the current generation of SDKs.

Jquery keep Alive / Any client side interaction?

I have used a few of the jquery keepalive session plugins with out problem.
I have been asked for something a bit different.
We have some forms (built before I started here) that are fairly large and users work in them for a while. The page is never refreshed, so they click save and the session is expired and redirect to the login page.
I suggested one of these plugin, that just prompt the user a few minutes before the session expires which would make an ajax call to keep the session alive.
However, they said, will what if they dont see the prompt and miss it all together and logs them out.
They would like me to check
Has the user had any interaction with the page in the last 5 minutes.
If Yes=Ajax call to keep alive the session, and reset timer.
if No, continue to wait until we get within 2 minutes of session time out and prompt user.
They are trying to avoid the prompt.
Is there anyway with JS/Jquery to know if the page has had any client side interaction?
Rather than using a timer to check if they've had any interaction in the last 5 minutes, couldn't you just send your keepalive any time the form has changed? It would eliminate a need for a timer loop and a small payload ajax call just to keep the session alive shouldn't hurt performance at all.
If you still want to keep the timer loop, I would still recommend using the change event on your form elements. Changing the form implies they're interacting with it, and I think that satisfies their requirement.
Edit: Update to use timer
var idle = true;
function finalIdleCheck(prompt){
if(idle){
if(prompt){
alert("last warning!");
}
//Tighten the idle check time loop; may even want to go < 30s
setTimeout(finalIdleCheck, 30*1000);
} else {
//Ajax stuff to keep session alive
idle = true; //Reset idle flag
}
}
function checkIdle(){
if(idle){
//Warn them
alert("You've been idle");
setTimeout(function(){
finalIdleCheck(true);
}, 60*2*1000);
} else {
//Ajax stuff to keep session alive
idle = true; //Reset idle flag
}
}
$(document).ready(function(){
$("form input").on("change", function(){
idle = false;
}
setTimeout(idleCheck, 60*5*1000);
}

How can I make my javascript chat polling script be more efficient?

For some reason this check for new chat messages causes a larger amount of browser (and to some extent server) load than I would expect. Anyone see any ways that I can make it more efficient, to lessen the load?
// Begin the cycle of refreshing the mini chat after the standard delay.
function startRefreshingMinichat(){
var secs = 30; // Chat checking frequency.
setTimeout(function (){
checkForNewChats();
startRefreshingMinichat(); // Loop the check for refresh.
}, secs*1000);
}
// Check for the latest chat and update if it's different.
function checkForNewChats(){
// Check whether the latest chat doesn't match the latest displayed chat.
// NOTE THAT THIS CALLBACK DOES NOT TRIGGER IMMEDIATELY.
$.getJSON('api.php?type=latest_chat_id&jsoncallback=?', function(data){
var newChats = false;
// Update global data stores if an update is needed.
if(updateDataStore(data.latest_chat_id, 'chat_id', 'latestChatId', 'chat_id')){
newChats = true;
}
if(newChats){ // there are new chats to show.
refreshMinichat(null, 50); // loads new chat content.
}
// Since this callback isn't immediate, any feedback has to occur whenever the callback finishes.
}); // End of getJSON function call.
}
Check out CometD. It's a js long-polling system I've used with some success for simple chat systems integrated with jQuery. (Last time I looked, there were a few jQuery specific implemetations, but I never found one that was robust enough for me.)
you can checkout this push engine so that you have not to poll for new data anymore.
check it out, its really cool.

Categories