Skip to content

Commit

Permalink
Add idle detection - WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
raducristianpopa committed Oct 5, 2024
1 parent 795f604 commit 8a4f076
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 1 deletion.
7 changes: 7 additions & 0 deletions src/content/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
type ContentToBackgroundMessage,
MessageManager,
} from '@/shared/messages';
import { IdleDetection } from './services/idleDetection';

export interface Cradle {
logger: Logger;
Expand All @@ -18,6 +19,7 @@ export interface Cradle {
message: MessageManager<ContentToBackgroundMessage>;
monetizationLinkManager: MonetizationLinkManager;
frameManager: FrameManager;
idleDetection: IdleDetection;
contentScript: ContentScript;
}

Expand All @@ -44,6 +46,11 @@ export const configureContainer = () => {
.inject(() => ({
logger: logger.getLogger('content-script:tagManager'),
})),
idleDetection: asClass(IdleDetection)
.singleton()
.inject(() => ({
logger: logger.getLogger('content-script:idleDetection'),
})),
contentScript: asClass(ContentScript)
.singleton()
.inject(() => ({
Expand Down
68 changes: 68 additions & 0 deletions src/content/services/idleDetection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Cradle } from '@/content/container';

export class IdleDetection {
private document: Cradle['document'];
private logger: Cradle['logger'];
// Pass this to the class to make e2e testing easier instead of declaring
// it here.
private isIdle: boolean = false;
private readonly idleTimeout: number = 10000;

constructor({ document, logger }: Cradle) {
Object.assign(this, {
document,
logger,
});
}

detectUserInactivity() {
let lastActive = Date.now();
let timeoutId: ReturnType<typeof setTimeout> | undefined = undefined;

const onInactivityTimeoutReached = () => {
const now = Date.now();
const timeLeft = lastActive + this.idleTimeout - now;

// There is probably an edge case when the timeout is reached and the user
// will move the mouse at the same time?
if (timeLeft <= 0) {
// Send `STOP_MONETIZATION`.
this.logger.debug(
`No activity from the user - stopping monetization` +
` Last active: ${new Date(lastActive).toLocaleString()}`,
);
this.isIdle = true;
}
};

const activityListener = () => {
lastActive = Date.now();
if (this.isIdle) {
this.logger.debug('Detected user activity - resuming monetization');
// Send `RESUME_MONETIZATION`
this.isIdle = false;
clearTimeout(timeoutId);
timeoutId = setTimeout(onInactivityTimeoutReached, this.idleTimeout);
}
};

timeoutId = setTimeout(onInactivityTimeoutReached, this.idleTimeout);

this.registerDocumentEventListeners(activityListener);
this.logger.debug('Started listening for user activity');

// We should probably have a cleanup when the document is not focused?
}

private registerDocumentEventListeners(fn: () => void) {
// Additional events that we might want to register:
// - mousedown
// - touchstart (not relevant at the moment)
// - touchmove (not relevant at the moment)
// - click
// - keydown
// - scroll (will this bubble when scrolling inside a scrollable element?)
// - wheel
this.document.addEventListener('mousemove', fn);
}
}
6 changes: 5 additions & 1 deletion src/content/services/monetizationLinkManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export class MonetizationLinkManager extends EventEmitter {
private document: Cradle['document'];
private logger: Cradle['logger'];
private message: Cradle['message'];
private idleDetection: Cradle['idleDetection'];

private isTopFrame: boolean;
private isFirstLevelFrame: boolean;
Expand All @@ -29,13 +30,14 @@ export class MonetizationLinkManager extends EventEmitter {
{ walletAddress: WalletAddress; requestId: string }
>();

constructor({ window, document, logger, message }: Cradle) {
constructor({ window, document, logger, message, idleDetection }: Cradle) {
super();
Object.assign(this, {
window,
document,
logger,
message,
idleDetection,
});

this.documentObserver = new MutationObserver((records) =>
Expand Down Expand Up @@ -106,6 +108,8 @@ export class MonetizationLinkManager extends EventEmitter {
'visibilitychange',
this.onDocumentVisibilityChange,
);
// I feel like this should be moved in the main service?
this.idleDetection.detectUserInactivity();
this.onFocus();
this.window.addEventListener('focus', this.onFocus);

Expand Down

0 comments on commit 8a4f076

Please sign in to comment.