-
Notifications
You must be signed in to change notification settings - Fork 860
/
Copy pathUserActivityMonitor.ts
144 lines (123 loc) · 4.64 KB
/
UserActivityMonitor.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
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {app, powerMonitor} from 'electron';
import {EventEmitter} from 'events';
import {Logger} from 'common/log';
const log = new Logger('UserActivityMonitor');
/**
* Monitors system idle time, listens for system events and fires status updates as needed
*/
export class UserActivityMonitor extends EventEmitter {
isActive: boolean;
idleTime: number;
lastSetActive?: number;
systemIdleTimeIntervalID: number;
config: {
updateFrequencyMs: number;
inactiveThresholdMs: number;
statusUpdateThresholdMs: number;
};
constructor() {
super();
this.isActive = true;
this.idleTime = 0;
this.systemIdleTimeIntervalID = -1;
this.config = {
updateFrequencyMs: 1 * 1000, // eslint-disable-line no-magic-numbers
inactiveThresholdMs: 60 * 1000, // eslint-disable-line no-magic-numbers
statusUpdateThresholdMs: 60 * 1000, // eslint-disable-line no-magic-numbers
};
}
get userIsActive() {
return this.isActive;
}
get userIdleTime() {
return this.idleTime;
}
/**
* Begin monitoring system events and idle time at defined frequency
*
* @param {Object} config - overide internal configuration defaults
* @param {number} config.updateFrequencyMs - internal update clock frequency for monitoring idleTime
* @param {number} config.inactiveThresholdMs - the number of milliseconds that idleTime needs to reach to internally be considered inactive
* @param {number} config.statusUpdateThresholdMs - minimum amount of time before sending a new status update
* @emits {error} emitted when method is called before the app is ready
* @emits {error} emitted when this method has previously been called but not subsequently stopped
*/
startMonitoring(config = {}) {
if (!app.isReady()) {
this.emit('error', new Error('UserActivityMonitor.startMonitoring can only be called after app is ready'));
return;
}
if (this.systemIdleTimeIntervalID >= 0) {
this.emit('error', new Error('User activity monitoring is already in progress'));
return;
}
this.config = Object.assign({}, this.config, config);
// Node typings don't map Timeout to number, but then clearInterval requires a number?
this.systemIdleTimeIntervalID = setInterval(() => {
try {
this.updateIdleTime(powerMonitor.getSystemIdleTime());
} catch (err) {
log.error('Error getting system idle time:', err);
}
}, this.config.updateFrequencyMs) as unknown as number;
}
/**
* Stop monitoring system events and idle time
*/
stopMonitoring() {
clearInterval(this.systemIdleTimeIntervalID);
this.systemIdleTimeIntervalID = -1;
}
/**
* Updates internal idle time and sets internal user activity state
*
* @param {integer} idleTime
* @private
*/
updateIdleTime(idleTime: number) {
this.idleTime = idleTime;
if (idleTime * 1000 > this.config.inactiveThresholdMs) { // eslint-disable-line no-magic-numbers
this.setActivityState(false);
} else {
this.setActivityState(true);
}
}
/**
* Updates user active state and conditionally triggers a status update
*
* @param {boolean} isActive
* @param {boolean} isSystemEvent – indicates whether the update was triggered by a system event (log in/out, screesaver on/off etc)
* @private
*/
setActivityState(isActive = false, isSystemEvent = false) {
this.isActive = isActive;
if (isSystemEvent) {
this.sendStatusUpdate(true);
return;
}
const now = Date.now();
if (isActive && (this.lastSetActive == null || now - this.lastSetActive >= this.config.statusUpdateThresholdMs)) {
this.sendStatusUpdate(false);
this.lastSetActive = now;
} else if (!isActive) {
delete this.lastSetActive;
}
}
/**
* Sends an update with user activity status and current system idle time
*
* @emits {status} emitted at regular, definable intervals providing an update on user active status and idle time
* @private
*/
sendStatusUpdate(isSystemEvent = false) {
this.emit('status', {
userIsActive: this.isActive,
idleTime: this.idleTime,
isSystemEvent,
});
}
}
const userActivityMonitor = new UserActivityMonitor();
export default userActivityMonitor;