Fire event without user interaction

Hi, I’m currently working on a browser based private message system. Sending messages works fine so far but now I would like to add a ding-sound when a user receives a new message. In my thinking process I believe I need an event for that that fires without user interaction but I couldn’t find anything about it.

Right now I’m using a button with the class “.test” to fire the event to test it.

Here is how I did it so far:

Here I get the current count of unread messages. If you get on the page it waits a second, then looks for your PNs and then sets a session with the current count.

Template.nav.onRendered(function () {
    Meteor.setTimeout(function(){ 
        const pnCount = PN.find({userGet: Meteor.user().username, isSeen: false, isTrash: false, isFav: false}, {sort: {createdAt: -1}}).count();
        Session.set('pnCount', pnCount);
      }, 1000);
  });

Then, the event:

I get the current Count and check if the new pnCount is greater than what the Session saved. If it’s true it makes that little beep sound.

Template.nav.events({
'click .test'() {
  var pnCount = Session.get('pnCount');
  const pnCountNew = PN.find({userGet: Meteor.user().username, isSeen: false, isTrash: false, isFav: false}, {sort: {createdAt: -1}}).count();
  if (pnCountNew > pnCount) {
    var audio = new Audio('sounds/blep.wav');
    audio.play();
    Session.set('pnCount', pnCountNew);
  }
},
});

I checked the blaze.js API but I didn’t find any help there.

Is there a way to change the trigger: 'click .test' to something that fires if that
(pnCountNew > pnCount) condition is true?

I have found a little solution for myself although I don’t know if it’s very smart to do it that way.

I just put the check inside an interval and let it check every 3 seconds for new messages, like so:

Template.nav.onRendered(function (template) {
    Meteor.setTimeout(function(){ 
        const pnCount = PN.find({userGet: Meteor.user().username, isSeen: false, isTrash: false, isFav: false}, {sort: {createdAt: -1}}).count();
        Session.set('pnCount', pnCount);
      }, 1000);

      Meteor.setInterval(function(){

        var pnCount = Session.get('pnCount');
        const pnCountNew = PN.find({userGet: Meteor.user().username, isSeen: false, isTrash: false, isFav: false}, {sort: {createdAt: -1}}).count();
      
        if (pnCountNew > pnCount) {
          var audio = new Audio('sounds/blep.wav');
          audio.play();
          Session.set('pnCount', pnCountNew);
          console.log("Testlog: There is a new pn");
      
        } else {
          console.log("Testlog: There is none");
        }
      }, 3000);
  });

My question here would be: How “encumbering” would it be for an app to have this function run every 3 seconds all the time?

I suggest you to use the Collection.observeChanges. Check about it, probably you will prefer that.

Im sorry I cant share some code, im not around my pc right now.

1 Like

Yeah, observeChanges would work great for this:

PN.find({
        userGet: Meteor.user().username,
        isSeen: false,
        isTrash: false,
        isFav: false,
    },
    { sort: { createdAt: -1 } }
).observeChanges({
    added() {
        var audio = new Audio('sounds/blep.wav');
        audio.play();
        console.log('Testlog: There is a new pn');
    },
});

Alternatively you could use an autorun to re-run a function anytime your query changes, but that kinda ends up being more code.

Not that an interval is a bad idea, traditional apps often use this kind of polling with HTTP requests to check for new notifications. The resource use is even lower in a Meteor app, where the data is pushed over the web-socket instead of polled from the client. So your solution is perfectly fine, just a little more complicated than using observeChanges

2 Likes

Thank you! That really would be more comfortable. Although there’s a problem that occurse if I use observeChanges or the method with interval.

On some pages it works fine, on others there’s this error-message in the console:

Uncaught (in promise) DOMException

It appears somewhat random to me when it comes to the pages that work or those who don’t. The nav is inside a template and always on the top of the page. My sound-folder is inside the nav-component-directory on that path: /imports/ui/components/nav/sounds/blep.wav This is also where my nav-template is. Later I put this nav-template inside body.html in /imports/ui/layout/body/body.html.

My guess is that DOMException is coming from the call to audio.play(). If you try to play sounds or videos with js before the user has ‘interacted’ with the page, it will throw. Try loading the page and leaving it alone and see if it errors, then try clicking some things and see if that stops the error from happening

If that’s the case, you can just wrap it in a try/catch and ignore

1 Like