Alternatives to user-status

I’m developing an events application and the companies are really concerned about which users are logged in.

Has anyone identified a performant way of doing that? We’ve tried meteor-user-status but there were inaccuracies.

Is it viable to offload that work to Google Analytics User-ID tracking or do you need to do it directly in your meteor app?

Can you elaborate? I am currently investigating to switch to this package (because it’s now part of the Meteor Community Packages) but I would really like to know, if there are tradeoffs in before :slight_smile:

After talking with my partner on it, we think it’s a configuration thing. I created the publication for Meteor.users, but didn’t specify all the fields.

Last time I checked, 5 years ago, Meteor.users only publishes part of the cursor.

Tldr, publish the extra fields.

We can’t really test until we get a bunch of people signed in :joy:.

Re: Google analytics, we have to know which users so we can boot unauthorized attendees.

Re: this package, it’s in active development so I still feel like this is the best solution :smile_cat:. It also has tons more functionality.

I didn’t know that package was there so I made a simple one. My users have already subscribed to a notifications channel so I just added a simple code to that publish function on the server.

Meteor.publish('notifications', function() {
// user is online

   this.onStop(() => {
      // this user is offline
   });
});

You might also look at socialize:user-presence

2 Likes

Does this approach also handle situations where a single user is logged into the app with multiple devices (i.e. laptop and a phone) or multiple tabs in the browser? So if a person closes their laptop, that publication is stopped and the user will be marked as offline, despite actually being still logged in on their phone. Though I might be mistaken here as well. And regardless, probably depends a lot on the app if such situatsions occur at all.

You’re right, it has problem when user open multiple tabs or devices. To make sure you have to create “ping” method to update the online status.
Update: (problem solved)

The publish function:

// update the user online status
  UsersHelper.updateOnlineStatus({ userId: this.userId, online: true });

  this.onStop(() => {
    // update the user online status
    UsersHelper.updateOnlineStatus({ userId: this.userId, online: false });
  });

The helper function:

updateOnlineStatus({ userId, online = true }) {
    const now = new Date();
    if (online === false) { // set user offline
      // first, update the offlineTimeoutAt value
      Meteor.users.update({
        _id: userId,
      }, {
        $set: {
          'onlineStatus.offlineTimeoutAt': now,
        },
      });

      // then delay update the online status
      Meteor.setTimeout(() => {
        const me = Meteor.users.findOne({ _id: userId }, {
          fields: { onlineStatus: 1 },
        });
        if (me && me.onlineStatus && me.onlineStatus.offlineTimeoutAt) {
          if (moment(me.onlineStatus.offlineTimeoutAt)
            .isBefore(moment().subtract(ONLINE_STATUS_DELAY_IN_SECONDS - 1, 'seconds'))) {
            // mark user is offline
            Meteor.users.update({
              _id: userId,
            }, {
              $set: {
                onlineStatus: {
                  online: false,
                  offlineTimeoutAt: null,
                  updatedAt: new Date(),
                },
              },
            });
          }
        }
      }, ONLINE_STATUS_DELAY_IN_SECONDS * 1000);
    } else { // set user online
      Meteor.users.update({
        _id: userId,
      }, {
        $set: {
          onlineStatus: {
            online: true,
            offlineTimeoutAt: null,
            updatedAt: now,
          },
        },
      });
    }
  },

and I created a method that allows user to update the online status:

'users.iAmStillHere'() {
    const user = Meteor.user();
    if (!user) {
      throw new Meteor.Error('users.iAmStillHere.2', 'Please login');
    }
    if (user && user.onlineStatus && user.onlineStatus.offlineTimeoutAt) {
      Meteor.users.update({ _id: user._id }, {
        $set: {
          'onlineStatus.offlineTimeoutAt': null,
          'onlineStatus.updatedAt': new Date(),
        },
      });
    }
  },

At front-end, I have a small component, watches the user data context and call that method to prevent false offline issue.

function OnlineStatus() {
  const { user } = useContext(AccountContext);

  const offlineTimeoutAt = (user && user.onlineStatus) ? user.onlineStatus.offlineTimeoutAt : null;

  useEffect(() => {
    if (offlineTimeoutAt) {
      Meteor.call('users.iAmStillHere');
    }
  }, [offlineTimeoutAt]);

  return null;
}