How do I create a countdown timer that persists when I browse to other pages of my app? - meteor

I'm making a simple app where I start a countdown timer, and when the timer expires it shows a template. On that template, I answer a series of questions, then then submit the answers to the database.
At the moment, my countdown timer just written in JavaScript like this:
Template.start.events({
'click .startTimer': function() {
$("#timer").text();
(function() {
var wait = 2;
var countSpan = $('#timer span');
function countdown() {
wait -= 1;
if (wait > 0) {
countSpan.text(wait);
setTimeout(countdown, 1000);
$('.startTimer').prop("disabled", true);
Session.set('done', false);
} else {
Session.set('done', true);
$('.startTimer').prop("disabled", false);
countSpan.text("Complete checkup then keep working!");
}
}
setTimeout(countdown, 1000);
}());
}
});
The problem is that when I navigate away from the page that's printing out the countdown, it doesn't persist.
How do I make a countdown timer within my app that will keep going while I navigate to other pages within the app?

What about this pseudo idea:
Once you navigate away form the page (tab blur of some sorts), save
variable in the Session with current timestamp
Session.set("leavingAt", new Date())
Once you return to the page (equivalent tab focus), save new variable with current timestamp
Session.set("returningAt", new Date())
Add/substract difference between both (depending on how you implement) and update your countdown timer

You could start the timer on the server using a Meteor method.
// call method from template
Meteor.call('startTimer', function() {
// handle timer finished logic
});
//server
Meteor.methods({
startTimer: function() {
Meteor.setTimeout(function() {
//do something
return;
}, ms)
}
});
I think your callback will still be triggered even if you're on a different template, but I'm not 100% sure.

I ended up using meteorhacks:async to accomplish this. Here's the code:
function delayedMessge(callback) {
Meteor.setTimeout(function() {
callback(null, true)
}, 1000)
}
var wrappedDelayedMessage = Async.wrap(delayedMessge);
Meteor.methods({
'abc': function() {
var response = wrappedDelayedMessage();
return response;
}
});
Template.start.events({
'click .start': function() {
Meteor.call('abc', function(error, response){
var timeup = response
//I'm setting this session to true, then in a helper
//that isn't shown here, I'm getting the Session and
//which displays the template
Session.set('done', timeup)
});
}
})
azium's answer led me in the right direction but wasn't quite right. I did end up running a setTimeout within a method but there was a problem. The callback associated with the Meteor.call is executed right away, but the setTimeout within the mehod hasn't finished yet. With the meteorhacks:async package, the Meteor.call callback function waits until the setTimeout fires.

Related

How to use findOne in Template onCreated and access it in helper

Here is my template onCreated and helper function. I have provided inline comments to add some clarity. I can see that self.post is eventually set because it shows up on my console when I log Template.instance(). However, when I log Template.instance().post, it is always undefined.
Template.default.onCreated(function() {
var self = this;
self.autorun(function() {
var postId = FlowRouter.getParam('post_id');
self.subscribe('onePost', postId);
self.post = Posts.findOne(FlowRouter.getParam('post_id'));
});
});
Template.default.helpers({
poststage: function(stage) {
console.log(Template.instance()); // I can see post object here
console.log(Template.instance().post; //always undefined
if(Template.instance().post) {
// never true
return Template.instance().post.stage == stage;
}
}
});
Template.default.events({
'submit form':function(event, instance) {
Meteor.call('someFunc', instance.post.anotherField);
}
});
Edit: I should add, I'm trying to avoid writing the query twice because I'm also using it in the Template events. (see code).
This feels like a timing issue. You are subscribing to the onePost publication but you aren't waiting for that subscription to be .ready() before assigning self.post The autorun also seems superfluous. You shouldn't need to rerun that block of code in the onCreated block. I suggest:
Template.default.onCreated(function() {
this.postId = FlowRouter.getParam('post_id');
self.subscription = subscribe('onePost', this.postId);
});
Template.default.helpers({
poststage: function(stage) {
if ( this.subscription.ready() ){
return Posts.findOne(this.postId).stage;
}
}
});

Using of Session Issues in Meteor JS?

I need to know about the Sessions.Actually we using default sessions like session.set(key,value) and session.get(key). In this default session are cleared some cases like refresh and etc.
First i am using meteor add u2622:persistent-session Pkg.Use of this pkg gets one error i.e "Uncaught Error: Meteor does not currently support objects other than ObjectID as ids".
To overcome those problems to use amplify Sessions. But did one sample to using amplify Sessions as shown below code :
Js Code :
Messages = new Meteor.Collection("messages");
if (Meteor.isClient) {
var AmplifiedSession = _.extend({}, Session, {
keys: _.object(_.map(amplify.store(), function (value, key) {
return [key, JSON.stringify(value)];
})),
set: function (key, value) {
Session.set.apply(this, arguments);
amplify.store(key, value);
}
});
// counter starts at 0
Session.setDefault('counter', 0);
AmplifiedSession.set('no', 1);
Template.hello.helpers({
counter: function () {
return Session.get('counter');
}
});
Template.hello.helpers({
no: function () {
return AmplifiedSession.get('no');
}
});
Template.hello.events({
'click button': function () {
// increment the counter when button is clicked
console.log("Btn Clicked");
Session.set('counter', Session.get('counter') + 1);
AmplifiedSession.set('no',AmplifiedSession.get('no') + 1);
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}
Even not working.amplify Sessions also cleared at the time of Refresh.I didn't get any idea about this.So please suggest me what to do for this.
Thanks in Advance.
Try this package on atmosphere and let me know if it helped.
meteor add u2622:persistent-session
In this particular example, on every page load, you are running AmplifiedSession.set('no', 1);, therefore setting 'no' to be 1. This is why on page refreshes, 'no' is getting set to 1. Remove this line, and then change this line AmplifiedSession.set('no',AmplifiedSession.get('no') + 1); to set the value of 'no' if it does not exist.

How to properly replace this.stop() with pause() on Iron Router blaze integration

When I upgrade Iron Router to blaze integration branch, I began receiving this warning:
"You called this.stop() inside a hook or your action function but you should use pause() now instead"
Chrome console --> iron-router.js:2104 --> client/route_controller.js:193 from package
The code is on client:
Router.before(mustBeSignedIn, {except: ['userSignin', 'userSignup', 'home']});
var mustBeSignedIn = function () {
if (!Meteor.user()) {
// render the home template
this.redirect('home');
// stop the rest of the before hooks and the action function
this.stop();
return false;
}
return true;
}
I tried replacing this.stop() with: pause(), Router.pause() and this.pause() but still does not work. Also I haven't found pause function on iron-router package.
How do I properly replace this.stop() with pause()?
Thanks
From what I can tell the pause function is the first parameter your before hook is getting called with. Not in the docs anywhere, but that's what I gathered from the code and it seems to work.
Here's what I use:
var subscribeAllPlanItems = function (pause) {
var planId = this.params._id;
this.subscribe('revenues', planId).wait();
this.subscribe('expenses', planId).wait();
};
var waitForSubscriptions = function (pause) {
if (this.ready()) { //all the subs have come in
//NProgress.done();
setPlan(this.params._id);
} else { //all subscriptions aren't yet ready, keep waiting
//NProgress.start();
pause();
}
};
Router.map(function () {
this.route('calendar', {
path: '/calendar/:_id',
template: 'calendar',
before: [
subscribeAllPlanItems,
waitForSubscriptions
],
});
//Other routes omitted
});
var requireLogin = function (pause) {
if (Meteor.loggingIn()) { //still logging in
pause();
}
if (!Meteor.user()) { //not logged in
this.render('signIn');
pause();
} else { //logged in, life is good
console.log("requireLogin: logged in");
}
};
//This enforces login for all pages except the below ones.
Router.before(requireLogin, {
except: ['landing', 'signUp', 'signIn', 'forgotPassword', 'resetPassword']
});
I opened an issue on Github about this. Here's the response I got:
Oops I may have not changed the redirect method yet. Just use Router.go as it will work fine now. I will change over this.redirect sometime next week or a PR is welcome. Controllers are now automatically stopped if you change routes in a hook. You can pause the current run by calling the pause method which is passed as a parameter to your hooks and action functions.

Is there a simple way to simulate lag with Meteor?

is there a way to simulate lag with Meteor? Perhaps something that would delay all calls by say, 300ms?
You can do it in publish using:
Meteor._sleepForMs(5000); // sleeps for 5 seconds
I guess I'm a bit late for the party, but here's a better solution:
There are basically two parts to this question. One is, how to delay Meteor WebSocket (SockJS) writes and one is how to delay HTTP traffic (connect). You'll need to add both of the following snippets to your server-side code in order to delay all traffic sent from the Meteor server.
WebSocket
The hard part was overwriting the WebSocket write to delay it with a setTimeout:
(function () {
// Set the delay you want
var timeout = 3000
// stream_server abstracts sockJS the DDP-Server talks to it.
var streamServer = Meteor.server.stream_server
// The connect event listener
var standardConnect = streamServer.server._events.connection
// Overwrite the default event-handler
streamServer.server._events.connection = function (socket) {
// Overwrite the writes to the socket
var write = socket.write
socket.write = function () {
var self = this
var args = arguments
// Add a delay
setTimeout(function () {
// Call the normal write methods with the arguments passed to this call
write.apply(self, args)
}, timeout)
}
// Call the normal handler after overwritting the socket.write function
standardConnect.apply(this, arguments)
}
})()
HTTP
With connect it's pretty straight forward:
// Add a simple connect handler, wich calls the next handler after a delay
WebApp.rawConnectHandlers.use(function (req, res, next) {
return setTimeout(next, timeout)
})
Not sure about all calls, but you can use Futures to add a lag on the server, that way you can see latency compensation in action.
In a meteor method for example, you can
Meteor.methods({
post: function(post) {
post.title = post.title + (this.isSimulation ? '(client)' : '(server)');
// wait for 5 seconds
if (! this.isSimulation) {
var Future = Npm.require('fibers/future');
var future = new Future();
Meteor.setTimeout(function() {
future.ret();
}, 5 * 1000); // 5 seconds
future.wait();
}
var postId = Posts.insert(post);
return postId;
}
});
This will show the post being inserted with (client) appended to the end, and then 5 seconds later will get the update from the server and post's title will end with (server)
If you want simulate the lag in the subscriptions you can do the next:
Meteor.publish('collection', function(params) {
Meteor._sleepForMs(2000); // Sleep for 2 seconds
return CollectionX.find(params.query,params.projection);
});

execute javascript method after completing code behind method?

I want execute below callback() method after completion of document.getElementById('btnDownload').click(); method . Now callback()executing immediatly. I want wait "Click()" process done then Execute callback(); method.
function LoadPopup() {
// find the popup behavior
this._popup = $find('mdlPopup');
// show the popup
this._popup.show();
// synchronously run the server side validation ...
document.getElementById('btnDownload').click();
callback();
}
function callback() {
this._popup = $find('mdlPopup');
// hide the popup
this._popup.hide();
alert("hi");
}
Unless I've wildly misunderstood the question, this code will make a request to the page that the link leads to, and when the server has returned the response, executes the function:
$(document).ready(function() {
$("#btnDownload").click(function() {
$.ajax({
url: $(this).attr("href"),
complete: function(xhr, status) {
callback();
}
});
return false;
});
});
I may have wildly misunderstood the question...
Your question is not clear. But I will give you a example.
For example, you wan to load a page by clicking on a image then after the page load is complete you want to do something you can something like this
$('#buttonid').click(function() {
//this is the callback function of click event
//if you want some other callback functions then you need something like this
$("#somediv").load("page.php", function() {
//callback function code of .load event
alert('page loaded');
});
});
What you are asking is to have a callback function inside the default callback function of click event and a bit impossible from my point of view.

Resources