Meteor-Community-Packages / meteor-user-status

Track user connection state and inactivity in Meteor.
MIT License
558 stars 85 forks source link
hacktoberfest meteor

user-status

What's this do?

Keeps track of user connection data, such as IP addresses, user agents, and client-side activity, and tracks this information in Meteor.users as well as some other objects. This allows you to easily see users that are online, for applications such as rendering the users box below showing online users in green and idle users in orange.

User online states

For a complete example of what can be tracked, including inactivity, IP addresses, and user agents, check out a demo app at http://user-status.meteor.com, or its source.

Install

Install using Meteor:

$ meteor add mizzao:user-status

Additionally, note that to read client IP addresses properly, you must set the HTTP_FORWARDED_COUNT environment variable for your app, and make sure that IP address headers are forwarded for any reverse proxy installed in front of the app. See the Meteor docs on this for more details.

Basic Usage - Online State

This package maintains two types of status: a general user online flag in Meteor.users, and some additional data for each session. It uses timesync to maintain the server's time across all clients, regardless of whether they have the correct time.

Meteor.users receives a status field will be updated automatically if the user logs in or logs out, closes their browser, or otherwise disconnects. A user is online if at least one connection with that userId is logged in. It contains the following fields:

To make this available on the client, use a reactive cursor, such as by creating a publication on the server:

Meteor.publish("userStatus", function() {
  return Meteor.users.find({ "status.online": true }, { fields: { ... } });
});

or you can use this to do certain actions when users go online and offline.

Meteor.users.find({ "status.online": true }).observe({
  added: function(id) {
    // id just came online
  },
  removed: function(id) {
    // id just went offline
  }
});

You can use a reactive cursor to select online users in a client-side template helper:

Template.foo.usersOnline = function() {
  return Meteor.users.find({ "status.online": true })
};

Making this directly available on the client allows for useful template renderings of user state. For example, with something like the following you get the picture above (using bootstrap classes).

<template name="userPill">
    <span class="label {{labelClass}}">{{username}}</span>
</template>
Template.userPill.labelClass = function() {
  if (this.status.idle)
    return "label-warning"
  else if (this.status.online)
    return "label-success"
  else
    return "label-default"
};

Advanced Usage and Idle Tracking

Client API

On the client, the UserStatus object provides for seamless automatic monitoring of a client's idle state. By default, it will listen for all clicks and keypresses in window as signals of a user's action. It has the following functions:

For an example of how the above functions are used, see the demo.

Server API

The UserStatus.connections (in-memory) collection contains information for all connections on the server, in the following fields:

startupQuerySelector (Optional)

On startup meteor-user-status automatically resets all users to offline and then marks each online as connections are reestablished.

To customize this functionality you can use the startupQuerySelector Meteor package option like this:

  "packages": {
    "mizzao:user-status": {
      "startupQuerySelector": { "$or": [{ "status.online": true }, { "status.idle": { "$exists": true } }, { "status.lastActivity": { "$exists": true } }] }
    }
  }

The above example reduces server/database load during startup if you have a large number of users.

Usage with collection2

If your project is using aldeed:collection2 with a schema attached to Meteor.users, you need to add the following items to the schema to allow modifications of the status:

import SimpleSchema from 'simpl-schema';

const userSchema = new SimpleSchema({
    status: {
        type: Object,
        optional: true,
    },
    'status.lastLogin': {
        type: Object,
        optional: true,
    },
    'status.lastLogin.date': {
        type: Date,
        optional: true,
    },
    'status.lastLogin.ipAddr': {
        type: String,
        optional: true,
    },
    'status.lastLogin.userAgent': {
        type: String,
        optional: true,
    },
    'status.idle': {
        type: Boolean,
        optional: true,
    },
    'status.lastActivity': {
        type: Date,
        optional: true,
    },
    'status.online': {
        type: Boolean,
        optional: true,
    },
});

// attaching the Schema to Meteor.users will extend it
Meteor.users.attachSchema(userSchema);

Events

The UserStatus.events object is an EventEmitter on which you can listen for connections logging in and out. Logging out includes closing the browser; reopening the browser will trigger a new login event. The following events are supported:

UserStatus.events.on("connectionLogin", function(fields) { ... })

fields contains userId, connectionId, ipAddr, userAgent, and loginTime.

UserStatus.events.on("connectionLogout", function(fields) { ... })

fields contains userId, connectionId, lastActivity, and logoutTime.

UserStatus.events.on("connectionIdle", function(fields) { ... })

fields contains userId, connectionId, and lastActivity.

UserStatus.events.on("connectionActive", function(fields) { ... })

fields contains userId, connectionId, and lastActivity.

Check out https://github.com/mizzao/meteor-accounts-testing for a simple accounts drop-in that you can use to test your app - this is also used in the demo.

Startup selector

By default, the startup selector for resetting user status is {}. If you want to change that you can set the default selector in your settings.json file:

{
  "packages": {
    "mizzao:user-status": {
      "startupQuerySelector": {
        // your selector here, for example:
        "profile.name": "admin"
      }
    }
  }
}

Testing

There are some Tinytest unit tests that are used to test the logic in this package, but general testing with many users and connections is hard. Hence, we have set up a demo app (http://user-status.meteor.com) for testing that is also hosted as a proof of concept. If you think you've found a bug in the package, try to replicate it on the demo app and post an issue with steps to reproduce.

Contributing

See Contributing.md.