-
Notifications
You must be signed in to change notification settings - Fork 88
/
idleManager.ts
144 lines (129 loc) · 3.97 KB
/
idleManager.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/** @module IdleManager */
type IdleCB = () => unknown;
export type IdleManagerOptions = {
/**
* Callback after the user has gone idle
*/
onIdle?: IdleCB;
/**
* timeout in ms
* @default 30 minutes [600_000]
*/
idleTimeout?: number;
/**
* capture scroll events
* @default false
*/
captureScroll?: boolean;
/**
* scroll debounce time in ms
* @default 100
*/
scrollDebounce?: number;
};
const events = ['mousedown', 'mousemove', 'keydown', 'touchstart', 'wheel'];
/**
* Detects if the user has been idle for a duration of `idleTimeout` ms, and calls `onIdle` and registered callbacks.
* By default, the IdleManager will log a user out after 10 minutes of inactivity.
* To override these defaults, you can pass an `onIdle` callback, or configure a custom `idleTimeout` in milliseconds
*/
export class IdleManager {
callbacks: IdleCB[] = [];
idleTimeout: IdleManagerOptions['idleTimeout'] = 10 * 60 * 1000;
timeoutID?: number = undefined;
/**
* Creates an {@link IdleManager}
* @param {IdleManagerOptions} options Optional configuration
* @see {@link IdleManagerOptions}
* @param options.onIdle Callback once user has been idle. Use to prompt for fresh login, and use `Actor.agentOf(your_actor).invalidateIdentity()` to protect the user
* @param options.idleTimeout timeout in ms
* @param options.captureScroll capture scroll events
* @param options.scrollDebounce scroll debounce time in ms
*/
public static create(
options: {
/**
* Callback after the user has gone idle
* @see {@link IdleCB}
*/
onIdle?: () => unknown;
/**
* timeout in ms
* @default 10 minutes [600_000]
*/
idleTimeout?: number;
/**
* capture scroll events
* @default false
*/
captureScroll?: boolean;
/**
* scroll debounce time in ms
* @default 100
*/
scrollDebounce?: number;
} = {},
): IdleManager {
return new this(options);
}
/**
* @protected
* @param options {@link IdleManagerOptions}
*/
protected constructor(options: IdleManagerOptions = {}) {
const { onIdle, idleTimeout = 10 * 60 * 1000 } = options || {};
this.callbacks = onIdle ? [onIdle] : [];
this.idleTimeout = idleTimeout;
const _resetTimer = this._resetTimer.bind(this);
window.addEventListener('load', _resetTimer, true);
events.forEach(function (name) {
document.addEventListener(name, _resetTimer, true);
});
// eslint-disable-next-line @typescript-eslint/ban-types
const debounce = (func: Function, wait: number) => {
let timeout: number | undefined;
return (...args: unknown[]) => {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const context = this;
const later = function () {
timeout = undefined;
func.apply(context, args);
};
clearTimeout(timeout);
timeout = window.setTimeout(later, wait);
};
};
if (options?.captureScroll) {
// debounce scroll events
const scroll = debounce(_resetTimer, options?.scrollDebounce ?? 100);
window.addEventListener('scroll', scroll, true);
}
_resetTimer();
}
/**
* @param {IdleCB} callback function to be called when user goes idle
*/
public registerCallback(callback: IdleCB): void {
this.callbacks.push(callback);
}
/**
* Cleans up the idle manager and its listeners
*/
public exit(): void {
clearTimeout(this.timeoutID);
window.removeEventListener('load', this._resetTimer, true);
const _resetTimer = this._resetTimer.bind(this);
events.forEach(function (name) {
document.removeEventListener(name, _resetTimer, true);
});
this.callbacks.forEach(cb => cb());
}
/**
* Resets the timeouts during cleanup
*/
_resetTimer(): void {
const exit = this.exit.bind(this);
window.clearTimeout(this.timeoutID);
this.timeoutID = window.setTimeout(exit, this.idleTimeout);
}
}