overcentric
Version:
Overcentric watches your website, product, and users - and tells you what matters and what to do about it.
68 lines (67 loc) • 1.94 kB
JavaScript
import { log } from "./config";
// What counts as "real" user activity. These are the signals that should keep a
// session (and an in-progress recording) alive. A tab that is merely open and
// focused but receives none of these is considered idle.
const ACTIVITY_EVENTS = [
"mousedown",
"mousemove",
"keydown",
"scroll",
"touchstart",
"click",
];
// Coalesce bursts of activity (e.g. mousemove) so we don't run callbacks on
// every single event.
const THROTTLE_MS = 1000;
let initialized = false;
let lastActivity = Date.now();
let throttledUntil = 0;
const listeners = new Set();
function onActivity() {
const now = Date.now();
lastActivity = now;
if (now < throttledUntil)
return;
throttledUntil = now + THROTTLE_MS;
listeners.forEach((fn) => {
try {
fn();
}
catch (e) {
log.debug(`Activity listener threw: ${e}`);
}
});
}
/**
* Start listening for real user-input events. Safe to call multiple times;
* listeners are only attached once.
*/
export function initActivityTracking() {
if (initialized || typeof window === "undefined")
return;
initialized = true;
lastActivity = Date.now();
ACTIVITY_EVENTS.forEach((evt) => {
document.addEventListener(evt, onActivity, {
capture: true,
passive: true,
});
});
log.debug("Activity tracking initialized");
}
/**
* Register a callback that fires (throttled) whenever genuine user activity
* occurs. Returns an unsubscribe function.
*/
export function onUserActivity(fn) {
listeners.add(fn);
return () => listeners.delete(fn);
}
/** Milliseconds since the last detected user activity. */
export function timeSinceLastActivity() {
return Date.now() - lastActivity;
}
/** Timestamp (ms) of the last detected user activity. */
export function getLastActivity() {
return lastActivity;
}