import { addEventListeners, addTelemetryDebug, DOM_EVENT, throttle } from '@flashcatcloud/browser-core' import type { RumConfiguration } from '@flashcatcloud/browser-rum-core' import { getSerializedNodeId, hasSerializedNode } from '../serialization' import type { BrowserIncrementalSnapshotRecord, MousemoveData, MousePosition } from '../../../types' import { IncrementalSource } from '../../../types' import { getEventTarget, isTouchEvent } from '../eventsUtils' import { convertMouseEventToLayoutCoordinates } from '../viewports' import { assembleIncrementalSnapshot } from '../assembly' import type { Tracker } from './tracker.types' const MOUSE_MOVE_OBSERVER_THRESHOLD = 50 export type MousemoveCallBack = (incrementalSnapshotRecord: BrowserIncrementalSnapshotRecord) => void export function trackMove(configuration: RumConfiguration, moveCb: MousemoveCallBack): Tracker { const { throttled: updatePosition, cancel: cancelThrottle } = throttle( (event: MouseEvent | TouchEvent) => { const target = getEventTarget(event) if (hasSerializedNode(target)) { const coordinates = tryToComputeCoordinates(event) if (!coordinates) { return } const position: MousePosition = { id: getSerializedNodeId(target), timeOffset: 0, x: coordinates.x, y: coordinates.y, } moveCb( assembleIncrementalSnapshot( isTouchEvent(event) ? IncrementalSource.TouchMove : IncrementalSource.MouseMove, { positions: [position] } ) ) } }, MOUSE_MOVE_OBSERVER_THRESHOLD, { trailing: false, } ) const { stop: removeListener } = addEventListeners( configuration, document, [DOM_EVENT.MOUSE_MOVE, DOM_EVENT.TOUCH_MOVE], updatePosition, { capture: true, passive: true, } ) return { stop: () => { removeListener() cancelThrottle() }, } } export function tryToComputeCoordinates(event: MouseEvent | TouchEvent) { let { clientX: x, clientY: y } = isTouchEvent(event) ? event.changedTouches[0] : event if (window.visualViewport) { const { visualViewportX, visualViewportY } = convertMouseEventToLayoutCoordinates(x, y) x = visualViewportX y = visualViewportY } if (!Number.isFinite(x) || !Number.isFinite(y)) { if (event.isTrusted) { addTelemetryDebug('mouse/touch event without x/y') } return undefined } return { x, y } }