0

I want to display users which are currently online and have a focus on a special page. Somewhere on this page I like to display the list of users and there status (on/offline focus/no focus)

I found this package: benjaminrh/event-hooks

The setup is clear, this is in the docs. But I do not understand how to use the Hooks. E.g.:

Hooks.onLoseFocus = function ([server: userId]) { ... } 

( anywhere ) - Provide a callback to run when the window loses focus.

So the function takes a userId.

Having the above layout in mind, I will have somewhere an extra template and an each loop like:

  {{#each userstatus}}
     {{>users_stats}
  {{/each}}

How would I create userstatus when the hook takes individual userIds?

Michael Hoeller
  • 22,018
  • 10
  • 39
  • 66

1 Answers1

1

Ok, here is a fully working solution which tracks users currently viewing a particular route path and displays this list of users via a helper. This works under the assumption you are using iron:router:

Server:

Hooks.onCloseSession = function() {
    Meteor.call('clearUserFocus', function(error, result) {
        if(error)
            console.log(error);
    });
}

Meteor.methods({
    'setUserFocus': function(_routePath) {
        Meteor.users.update({_id: Meteor.userId()}, {$set: {focus: _routePath}});
    },
    'clearUserFocus': function() {
        var userId = Meteor.userId();
        Meteor.users.update({_id: userId}, {$unset: {focus: ''}});
    }
});

onCloseSession seems to only work reliably in the server, as you would expect.

Client:

Meteor.startup(function() {
    // Start hooks, for user focus tracking
    Hooks.init({
        updateFocus: 500
    });
});

Hooks.onGainFocus = function() {
    // Router.current().route.getName(); // route name
    var routePath = Iron.Location.get().pathname;  // route path
    Meteor.call('setUserFocus', routePath, function(error, result) {
        if(error)
            console.log(error);
    });
}

Hooks.onLoseFocus = function() {
    Meteor.call('clearUserFocus', function(error, result) {
        if(error)
            console.log(error);
    });
}

Client Global Router:

Router.onBeforeAction(function() {
    // Record user focus
    var routePath = Iron.Location.get().pathname;
    Meteor.call('setUserFocus', routePath, function(error, result) {
        if(error)
            console.log(error);
    });

    this.next();
});

Client Template Helper:

Template.yourTemplateName.helpers({
    'focusedUsers': function() {
            var routePath = Iron.Location.get().pathname;
            return Meteor.users.find({focus: routePath});
     }
});

Client Template:

<ul>
    {{#each focusedUsers}}
        <li>{{_id}}</li>
    {{/each}}
</ul>

This is working pretty nicely for me, I need to test it thoroughly though as I've discovered at least one caveat. With multiple windows running it seems it can get a bit confused with focus.

Also note you'll need all your routes to have access to the users publication (whatever yours is called) so that the helper has access to the collection and can get focus. Which you can do like this:

// Global router settings
Router.configure({
    layoutTemplate: 'defaultLayout',
    loadingTemplate: 'loading',
    waitOn: function() {
        return Meteor.subscribe('users');
    }
});

As requested: Note that this includes an optional example of default layout and loading templates. The loading template replaces the contents of the layoutTemplate {{> yield}} until the waitOn (both globally and route specific) subscriptions are ready.

I'm also getting a browser console error, which doesn't seem to hinder anything as far as I can tell, but I don't like it being there: 'Error invoking Method 'eventsOnHooksInit': Method not found [404]'

Ian Jones
  • 1,369
  • 1
  • 10
  • 15
  • In the doc, I have copied it in my question, is also `Hooks.onLoseFocus = function ([server: userId]) { ... } ` **( anywhere )** - Provide a callback to run when the window loses focus. --- _Where would that code go to on the client site? --- How can I create a list of users which are currently focussed on ONE special page?_ – Michael Hoeller Mar 26 '15 at 07:47
  • @MBushveld I'm going to actually integrate this functionality into the app I'm working on, I'll let you know exactly how I achieve this. Are you using iron:router? – Ian Jones Mar 26 '15 at 10:39
  • @MBushveld Here is a full solution, please note of caveats – Ian Jones Mar 26 '15 at 11:25
  • Thanks! I am just giving it a try. concerning the client part: do I need to put it just on the client site or do I need to get a relation to a specific site? - I assume the first. Concerning the router: I assume that I build up in the router the relation to the specific site, so that I need to put the "onBeforeAction" into a specific route? e.g this.route('profile'), { .... Thanks again ! – Michael Hoeller Mar 26 '15 at 22:17
  • I think the part concerning the router I could figure out. I assume that the onBeforAction is a global config. I found in an example, `Router.onBeforeAction('loading');` How can I integrate this in the above mentioned code? The loading provides in the example a waiting circle which I like to keep. – Michael Hoeller Mar 26 '15 at 22:29
  • @MBushveld Client part just needs to go within an Meteor.isClient or /client folder as you figured. The onBeforeAction as you have figured out is global to the router, because you want this action to happen for every route. If your application is for members only, it's also somewhere you might check if a user is logged in for example and redirect them to a login form if not. I've added my additional global router config (slightly different to the onBeforeAction, but still global) to demonstrate my loading setup. – Ian Jones Mar 27 '15 at 00:02
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/73902/discussion-between-mbushveld-and-ian-jones). – Michael Hoeller Mar 27 '15 at 07:12