Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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