Reputation: 1143
I am using Meteor 1.6.0.1 The use case is that client calls meteor method every 5s to tell server it is online and server stores last-event-time, online in collection.
Meteor.methods({
'ping.online'() {
MyColl.update({_id:this.userId}, {$set:{last_online:Date.now(), online:true}})
}
});
I also want to mark it offline if there is no event till 1 minutes. To do so I thought about following way.
Meteor.methods({
'ping.online'() {
MyColl.update({_id:this.userId}, {$set:{last_online:Date.now(), online:true}});
let timer = Meteor.setTimeout(()=>{
MyColl.update({_id:this.userId}, {$set:{online:false}});
}, 60*1000)
}
});
For this to work I have to call Meteor.clearTimeout(timer)
when next ping comes after 5s. I am confused how should i store timer value and use this in next call from same client/user.
I could have just stored last_online time and use the same logic to show it offline, but when I publish this to client, client receives too many updates since there would be frequent changes on this field from every user. This leads to ui getting updated multiple times in a sec due to data change and shows flicker.
Upvotes: 0
Views: 147
Reputation: 8423
A general note: There are packages for Meteor out there, that solve user online status automatically and very efficiently. The following code is still posted for supporting a general understanding of the approach to get track of a certain field by using a second "supporting" collection.
Edit: after I found out, that Meteor.setTimeout returns a handle instead of an id I had to rewrite my solution. The updated version is in the bottom.
A possible and way would be to create a second collection called Timers
with key/value schema:
{
timerId:String,
userId:String,
}
By doing so, you can store the current timer in a document of this collection. If a timerDoc is present on the method run, you can clear the timer by the stored timer id:
Meteor.methods({
'ping.online'() {
// get timer, if present
// and clear timeout
const timerDoc = Timers.findOne({userId:this.userId})
if (timerDoc && timerDoc.timerId) {
Meteor.clearTimeout(timerDoc.timerId);
}
// update collection
MyColl.update({_id:this.userId}, {$set:{last_online:Date.now(), online:true}});
// create new timer
let timer = Meteor.setTimeout(()=>{
MyColl.update({_id:this.userId}, {$set:{online:false}});
Timers.remove({_id: timerDoc._id});
}, 60*1000);
// update Timers collection
if (timerDoc) {
Timers.update({_id: timerDoc._id}, {$set: {timerId: timer} });
}else{
Timers.insert({
timerId: timer,
userId: this.userId,
});
}
}
});
The code above will not work with Meteor.setTimeout
as it does not return a timer id but a handle, as described in the docs.
You can cache the timer objects using a plain Object, which acts as a dictionary.
However, this does not prevent you from caching the timer handle and use it in a similar fashion as in the code above.
// your timers dictionary
const Timers = {};
Meteor.methods({
'ping.online'() {
// get timer, if present
// and clear timeout
const existingTimer = Timers[this.userId];
if (existingTimer) {
Meteor.clearTimeout(existingTimer.timerId);
delete Timers[this.userId];
}
// update collection
MyColl.update({_id:this.userId}, {$set:{last_online:Date.now(), online:true}});
// create new timer
let timerHandle = Meteor.setTimeout(()=>{
MyColl.update({_id:this.userId}, {$set:{online:false}});
delete Timers[this.userId];
}, 60*1000);
// store timerHandle in dictionary
Timers[this.userId] = timerHandle;
}
});
This approach is not persistent but it doesn't has to be, because the information is needles after server startup and all timers have been reset. Despite this fact, keep the other example (using the collection) in mind as it is a pattern that often occurs, when you need to make supporting information persistent.
Upvotes: 1