
* Initial setup and migrated src/common * WIP * WIP * WIP * Main module basically finished * Renderer process migrated * Added CI step and some fixes * Fixed remainder of issues and added proper ESLint config * Fixed a couple issues * Progress! * Some more fixes * Fixed a test * Fix build step * PR feedback
141 lines
4.5 KiB
TypeScript
141 lines
4.5 KiB
TypeScript
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||
// See LICENSE.txt for license information.
|
||
|
||
import {EventEmitter} from 'events';
|
||
|
||
import electron from 'electron';
|
||
import log from 'electron-log';
|
||
|
||
const {app} = electron;
|
||
|
||
/**
|
||
* Monitors system idle time, listens for system events and fires status updates as needed
|
||
*/
|
||
export default 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);
|
||
|
||
// TODO: Node typings don't map Timeout to number, but then clearInterval requires a number?
|
||
this.systemIdleTimeIntervalID = setInterval(() => {
|
||
try {
|
||
this.updateIdleTime(electron.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);
|
||
}
|
||
|
||
/**
|
||
* 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,
|
||
});
|
||
}
|
||
}
|