npm package: @houseninja/ember-user-activity
Description: Ember Addon for tracking user activity & idling
Author: Jordan Hawker
Latest version: 7.0.0
Deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.

![Ember User Activity](docs/hero-logo.png)
==========================================

[![Netlify Status](https://api.netlify.com/api/v1/badges/e30a3237-450a-4582-8d15-5d568c784d94/deploy-status)](https://app.netlify.com/sites/hopeful-carson-42f584/deploys)
[![Build Status](https://github.com/elwayman02/ember-user-activity/workflows/CI/badge.svg)](https://github.com/elwayman02/ember-user-activity/actions?query=workflow%3ACI)
[![Ember Observer Score](https://emberobserver.com/badges/ember-user-activity.svg)](https://emberobserver.com/addons/ember-user-activity)
[![Code Climate](https://codeclimate.com/github/elwayman02/ember-user-activity/badges/gpa.svg)](https://codeclimate.com/github/elwayman02/ember-user-activity)

This addon provides services for detecting user activity & idling across the entire application.

Check out the [Demo](https://ember-user-activity.jhawk.co/)!

We adhere to the [Ember Community Guidelines](https://emberjs.com/guidelines/) for our Code of Conduct.


Compatibility
------------------------------------------------------------------------------

* Ember.js v3.20 or above
* Ember CLI v3.20 or above
* Node.js v10 or above


Installation
------------------------------------------------------------------------------

```bash
ember install @houseninja/ember-user-activity
```


Usage
------------------------------------------------------------------------------

### User Activity Service

This service fires events from global window listeners.
These listeners trigger on capture, meaning they are not affected by event cancellation.

These `window` events are enabled by default:

* `keydown` - Fires when a key is pressed
* `mousedown` - Fires when a mouse is clicked
* `scroll` - Fires when the user scrolls
* `touchstart` - Fires when a touch point is placed on the touch surface [Mobile-Friendly]
* `storage` - Fires when any activity event is triggered on another tab of the same website. This avoids problems when a user opens up serveral tabs to the same application.
* ~~`mousemove` - Fires when the user moves the mouse~~ [Removed as of v0.2.0](https://github.com/elwayman02/ember-user-activity/issues/16)

A custom event, `userActive` is fired for ALL enabled events.

To catch these events, simply inject the service and subscribe to the events you care about:

```javascript
import { inject as injectService } from '@ember/service';

// any file where services can be injected
// Classic
userActivity: injectService('ember-user-activity@user-activity'),

setupListeners() {
  this.userActivity.on('userActive', this, this.activeHandler);
},
activeHandler(event) {
  // do stuff
}


// Octane
@injectService('ember-user-activity@user-activity')
userActivity

setupListeners() {
  this.userActivity.on('userActive', this, this.activeHandler);
},
activeHandler(event) {
  // do stuff
}
```

Each event handler will receive the standard DOM `event` object
(ex: [mousemove](https://developer.mozilla.org/en-US/docs/Web/Events/mousemove)).

Unsubscribe from any event by calling `off`:

```javascript
this.userActivity.off('userActive', this, this.activeHandler);
```

Note: While our event dispatch system mirrors Ember.Evented, it does not include the `one` method. 
Only `on`, `off`, and `trigger` have been implemented. If you feel `one` is necessary for you, 
we're happy to accept PRs!

#### Event Configuration

If you would like to listen to a different set of events, extend the service in your app:

```javascript
// app/services/user-activity.js
import UserActivityService from 'ember-user-activity/services/user-activity';

// Classic
export default UserActivityService.extend({
  defaultEvents: ['keypress', 'mouseenter', 'mousemove']
});


// Octane
export default class UserActivity extends UserActivityService {
  defaultEvents = ['keypress', 'mouseenter', 'mousemove']
}
```

Additionally, you can enable/disable events after the service has been initialized.

```javascript
this.userActivity.enableEvent('keyup');
this.userActivity.disableEvent('mousedown');
```

Event names must be from the [DOM Event](https://developer.mozilla.org/en-US/docs/Web/Events) list.
Custom events are not currently supported. If you enable an event name
that was not set up by default, a new listener will be created automatically.

You can find out if an event is currently enabled:

```javascript
this.userActivity.isEnabled('foo'); // false
this.userActivity.isEnabled('keydown'); // true
```

#### Performance Configuration

Each individual event is throttled by 100ms for performance reasons,
to avoid clogging apps with a firehose of activity events. The length of
the throttling can be configured by setting `EVENT_THROTTLE` on the activity service.

```javascript
// app/services/user-activity.js
import UserActivityService from 'ember-user-activity/services/user-activity';

// Classic
export default UserActivityService.extend({
  EVENT_THROTTLE: 200 // 200 ms
});

// Octane
export default class UserActivity extends UserActivityService {
  EVENT_THROTTLE = 200 // 200 ms
}

```

Setting `EVENT_THROTTLE` to 0 will enable the full firehose of events.
This may cause performance issues in your application if non-trivial
amounts of code are being executed for each event being fired.

### User Idle Service

This service tracks user activity to decide when a user has gone idle by
not interacting with the page for a set amount of time.

```javascript
import { inject as injectService } from '@ember/service';
import { readOnly } from '@ember/object/computed'

// Classic
userIdle: injectService('ember-user-activity@user-idle')
isIdle: readOnly('userIdle.isIdle')


// Octane
@injectService('ember-user-activity@user-idle')
userIdle

@readOnly('userIdle.isIdle')
isIdle
```

The default timeout is set for 10 minutes but can be overridden by extending the service:

```javascript
// app/services/user-idle.js
import UserIdleService from 'ember-user-activity/services/user-idle';

// Classic
export default UserIdleService.extend({
  IDLE_TIMEOUT: 300000 // 5 minutes
});

// Octane
export default class UserIdle extends UserIdleService {
  IDLE_TIMEOUT = 300000 // 5 minutes
});
```

By default, the idle service listens to the `userActive` event, but it can be
configured to listen to a custom set of events from the `user-activity` service:

```javascript
// app/services/user-idle.js
import UserIdleService from 'ember-user-activity/services/user-idle';

// Classic
export default UserIdleService.extend({
  activeEvents: ['mousedown', 'keydown']
});

// Octane
export default class UserIdle extends UserIdleService {
  activeEvents = ['mousedown', 'keydown']
};
```

Note that the `userActive` event is a superset of all events fired from `user-activity`,
so in most cases you won't need to change this.

The idle service has a `idleChanged` event when `isIdle` gets changed.

```javascript
import { inject as injectService } from '@ember/service';

// Classic
userIdle: injectService('ember-user-activity@user-idle'),

init() {
  this.userIdle.on('idleChanged', (isIdle) => {
    // isIdle is true if idle. False otherwise.
  })
}

// Octane
@injectService('ember-user-activity@user-idle')
userIdle

init() {
  this.userIdle.on('idleChanged', (isIdle) => {
    // isIdle is true if idle. False otherwise.
  })
}
```

### Scroll Activity Service

This service tracks scrolling within the application by periodically checking
(via [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame))
for changes in scroll position for the various scrollable elements in the page. By default, it only
checks `document`, but the [Scroll Activity Mixin](#scroll-activity-mixin) provides an easy
way to register your components as well. The [User Activity Service](#user-activity-service) subscribes to these scrolling events by default, so you do not need to do anything to use this service for global scroll events if you are already injecting the user-activity service.

Any elements can be subscribed to this service:

```javascript
this.scrollActivity.subscribe(this, element);
```

`subscribe` requires at least two parameters:

* `target` - Usually `this`, target just needs to be a unique identifier/object
that can be used to unsubscribe from the service
* `element` - The scrollable element (can be a DOM or jQuery element - jQuery not required!)

Two optional parameters may follow:

* `callback` - A callback to execute when scrolling has been detected in the element
* `highPriority` - A boolean (default `true`) specifying this subscriber should eagerly check
  scroll positions on each animation frame. When `false`, it will instead
  use an approximation of idle checking on the UI thread to avoid performing
  measurements at sensitive times for other work (like rendering).

Conversely, elements can also be unsubscribed:

```javascript
this.scrollActivity.unsubscribe(this);
```

`unsubscribe` only requires the `target` parameter that was initially used to `subscribe`.

### Cleanup

Please remember that subscribing to events can cause memory leaks if they are not properly cleaned up.
Make sure to remove any listeners before destroying their parent objects.

```javascript
// app/components/foo-bar.js
willDestroyElement() {
  this.userActivity.off('keydown', this, this.keydownHandler);
}
```

### Using in an Addon

Building your own addon to extend Ember User Activity? No problem!

```javascript
// my-addon/addon/services/user-idle.js
import UserIdleService from 'ember-user-activity/services/user-idle';

// Classic
export default UserIdleService.extend({
  IDLE_TIMEOUT: 3000 // 3 minutes
});

// Octane
export default class UserIdle extends UserIdleService {
  IDLE_TIMEOUT = 3000 // 3 minutes
};
```


Contributing
------------------------------------------------------------------------------

See the [Contributing](CONTRIBUTING.md) guide for details.


License
------------------------------------------------------------------------------

This project is licensed under the [MIT License](LICENSE.md).