Skip to content

Commit

Permalink
ref: improve detection of multi clicks
Browse files Browse the repository at this point in the history
  • Loading branch information
mydea committed Jun 14, 2023
1 parent dbd590c commit 5ad616d
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 23 deletions.
32 changes: 17 additions & 15 deletions packages/replay/src/coreHandlers/handleClick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@ import { getClickTargetNode } from './util/domUtils';

type ClickBreadcrumb = Breadcrumb & {
timestamp: number;
data: { nodeId: number };
};

interface Click {
timestamp: number;
nodeId: number;
mutationAfter?: number;
scrollAfter?: number;
clickBreadcrumb: ClickBreadcrumb;
Expand Down Expand Up @@ -121,7 +119,7 @@ export class ClickDetector implements ReplayClickDetector {
return;
}

const click = this._getClick(breadcrumb);
const click = this._getClick(node);

if (click) {
// this means a click on the same element was captured in the last 1s, so we consider this a multi click
Expand All @@ -130,9 +128,9 @@ export class ClickDetector implements ReplayClickDetector {

const newClick: Click = {
timestamp: breadcrumb.timestamp,
nodeId: breadcrumb.data.nodeId,
clickBreadcrumb: breadcrumb,
clickCount: 1,
// Set this to 0 so we know it originates from the click breadcrumb
clickCount: 0,
node,
};
this._clicks.push(newClick);
Expand All @@ -145,27 +143,22 @@ export class ClickDetector implements ReplayClickDetector {

/** Count multiple clicks on elements. */
private _handleMultiClick(node: HTMLElement): void {
const now = nowInSeconds();
const click = this._clicks.find(click => click.node === node && now - click.timestamp < this._multiClickTimeout);
const click = this._getClick(node);

if (!click) {
return;
}

// ignore VERY close timestamps - otherwise we record the initial timestamp twice!
if (click && Math.abs(click.timestamp - nowInSeconds()) > 0.01) {
click.clickCount++;
}
click.clickCount++;
}

/** Try to get an existing click on the given element. */
private _getClick(breadcrumb: ClickBreadcrumb): Click | undefined {
const { nodeId } = breadcrumb.data;
private _getClick(node: HTMLElement): Click | undefined {
const now = nowInSeconds();

// Find any click on the same element in the last second
// If one exists, we consider this click as a double/triple/etc click
return this._clicks.find(click => click.nodeId === nodeId && now - click.timestamp < this._multiClickTimeout);
return this._clicks.find(click => click.node === node && now - click.timestamp < this._multiClickTimeout);
}

/** Check the clicks that happened. */
Expand All @@ -182,6 +175,13 @@ export class ClickDetector implements ReplayClickDetector {
click.scrollAfter = click.timestamp <= this._lastScroll ? this._lastScroll - click.timestamp : undefined;
}

// If an action happens within the multi click threshold, we can skip waiting and handle the click right away
const actionTime = click.scrollAfter || click.mutationAfter || 0;
if (actionTime && actionTime <= this._multiClickTimeout) {
timedOutClicks.push(click);
return;
}

if (click.timestamp + this._timeout <= now) {
timedOutClicks.push(click);
}
Expand Down Expand Up @@ -230,7 +230,9 @@ export class ClickDetector implements ReplayClickDetector {
route: replay.getCurrentRoute(),
timeAfterClickMs,
endReason,
clickCount,
// If clickCount === 0, it means multiClick was not correctly captured here
// - we still want to send 1 in this case
clickCount: clickCount || 1,
},
};

Expand Down
20 changes: 12 additions & 8 deletions packages/replay/test/unit/coreHandlers/handleClick.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,12 @@ describe('Unit | coreHandlers | handleClick', () => {
nodeId: 3,
},
};
const node = document.createElement('button');
detector.handleClick(breadcrumb1, node);
detector.handleClick(breadcrumb2, node);
detector.handleClick(breadcrumb3, node);
const node1 = document.createElement('button');
const node2 = document.createElement('button');
const node3 = document.createElement('button');
detector.handleClick(breadcrumb1, node1);
detector.handleClick(breadcrumb2, node2);
detector.handleClick(breadcrumb3, node3);

expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0);

Expand Down Expand Up @@ -268,10 +270,12 @@ describe('Unit | coreHandlers | handleClick', () => {
nodeId: 3,
},
};
const node = document.createElement('div');
detector.handleClick(breadcrumb1, node);
detector.handleClick(breadcrumb2, node);
detector.handleClick(breadcrumb3, node);
const node1 = document.createElement('div');
const node2 = document.createElement('div');
const node3 = document.createElement('div');
detector.handleClick(breadcrumb1, node1);
detector.handleClick(breadcrumb2, node2);
detector.handleClick(breadcrumb3, node3);

expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0);

Expand Down

0 comments on commit 5ad616d

Please sign in to comment.