@datadog/mobile-react-native
Version:
A client-side React Native module to interact with Datadog
126 lines (122 loc) • 5.19 kB
JavaScript
/*
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
* This product includes software developed at Datadog (https://www.datadoghq.com/).
* Copyright 2016-Present Datadog, Inc.
*/
import React from 'react';
import { InternalLog } from '../../../InternalLog';
import { SdkVerbosity } from '../../../SdkVerbosity';
import { DdSdk } from '../../../sdk/DdSdk';
import { getErrorMessage } from '../../../utils/errorUtils';
import { BABEL_PLUGIN_TELEMETRY } from '../../constants';
import { DdBabelInteractionTracking } from './DdBabelInteractionTracking';
import { DdEventsInterceptor } from './DdEventsInterceptor';
import { NoOpEventsInterceptor } from './NoOpEventsInterceptor';
import { areObjectShallowEqual } from './ShallowObjectEqualityChecker';
import { getJsxRuntimes } from './getJsxRuntime';
/**
* Provides RUM auto-instrumentation feature to track user interaction as RUM events.
* For now we are only covering the "onPress" events.
*/
export class DdRumUserInteractionTracking {
static isTracking = false;
static eventsInterceptor = new NoOpEventsInterceptor();
static originalCreateElement = React.createElement;
static originalMemo = React.memo;
static originalJsx = null;
static originalDevJsx = null;
static patchCreateElementFunction = (originalFunction, [element, props, ...rest]) => {
if (props && typeof props.onPress === 'function') {
const originalOnPress = props // eslint-disable-next-line @typescript-eslint/ban-types
.onPress;
props.onPress = (...args) => {
DdRumUserInteractionTracking.eventsInterceptor.interceptOnPress(...args);
return originalOnPress(...args);
};
// we store the original onPress prop so we can keep memoization working
props.__DATADOG_INTERNAL_ORIGINAL_ON_PRESS__ = originalOnPress;
}
return originalFunction(element, props, ...rest);
};
/**
* Starts tracking user interactions and sends a RUM Action event every time a new interaction was detected.
* Please note that we are only considering as valid - for - tracking only the user interactions that have
* a visible output (either an UI state change or a Resource request)
*/
static startTracking(options) {
// extra safety to avoid wrapping more than 1 time this function
if (DdRumUserInteractionTracking.isTracking) {
InternalLog.log('Datadog SDK is already tracking interactions', SdkVerbosity.WARN);
return;
}
DdSdk?.sendTelemetryLog(BABEL_PLUGIN_TELEMETRY, DdBabelInteractionTracking.getTelemetryConfig(), {
onlyOnce: true
});
DdRumUserInteractionTracking.eventsInterceptor = new DdEventsInterceptor(options);
const original = React.createElement;
React.createElement = (...args) => {
return this.patchCreateElementFunction(original, args);
};
try {
const [jsxRuntime, jsxDevRuntime] = getJsxRuntimes();
const originalJsx = jsxRuntime?.jsx;
const originalDevJsx = jsxDevRuntime?.jsxDEV;
this.originalJsx = originalJsx;
this.originalDevJsx = originalDevJsx;
if (originalJsx) {
jsxRuntime.jsx = (...args) => {
return this.patchCreateElementFunction(originalJsx, args);
};
}
if (originalDevJsx) {
jsxRuntime.jsxDEV = (...args) => {
return this.patchCreateElementFunction(originalDevJsx, args);
};
}
} catch (e) {
DdSdk.telemetryDebug(getErrorMessage(e));
}
const originalMemo = React.memo;
React.memo = (component, propsAreEqual) => {
return originalMemo(component, (prev, next) => {
if (!next.onPress || !prev.onPress) {
return propsAreEqual ? propsAreEqual(prev, next) : areObjectShallowEqual(prev, next);
}
// we replace "our" onPress from the props by the original for comparison
const {
onPress: _prevOnPress,
...partialPrevProps
} = prev;
const prevProps = {
...partialPrevProps,
onPress: prev.__DATADOG_INTERNAL_ORIGINAL_ON_PRESS__
};
const {
onPress: _nextOnPress,
...partialNextProps
} = next;
const nextProps = {
...partialNextProps,
onPress: next.__DATADOG_INTERNAL_ORIGINAL_ON_PRESS__
};
// if no comparison function is provided we do shallow comparison
return propsAreEqual ? propsAreEqual(prevProps, nextProps) : areObjectShallowEqual(nextProps, prevProps);
});
};
DdRumUserInteractionTracking.isTracking = true;
InternalLog.log('Datadog SDK is tracking interactions', SdkVerbosity.INFO);
}
static stopTracking() {
React.createElement = this.originalCreateElement;
React.memo = this.originalMemo;
DdRumUserInteractionTracking.isTracking = false;
if (this.originalJsx || this.originalDevJsx) {
const [jsxRuntime, jsxDevRuntime] = getJsxRuntimes();
jsxRuntime.jsx = this.originalJsx;
jsxDevRuntime.jsxDEV = this.originalDevJsx;
this.originalJsx = null;
this.originalDevJsx = null;
}
}
}
//# sourceMappingURL=DdRumUserInteractionTracking.js.map