// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. import { IMetricTelemetry } from '@microsoft/applicationinsights-core-js'; import { dateNow } from '@microsoft/applicationinsights-core-js'; import * as React from 'react'; import ReactPlugin from './ReactPlugin'; /** * Higher-order component base class to hook Application Insights tracking * in a React component's lifecycle. */ export abstract class AITrackedComponentBase

extends React.Component

{ protected _mountTimestamp: number = 0; protected _firstActiveTimestamp: number = 0; protected _idleStartTimestamp: number = 0; protected _lastActiveTimestamp: number = 0; protected _totalIdleTime: number = 0; protected _idleCount: number = 0; protected _idleTimeout: number = 5000; protected _intervalId?: any; protected _componentName: string; protected _reactPlugin: ReactPlugin; public constructor(props: P, reactPlugin: ReactPlugin, componentName: string) { super(props); this._reactPlugin = reactPlugin; this._componentName = componentName; } public componentDidMount() { this._mountTimestamp = dateNow(); this._firstActiveTimestamp = 0; this._totalIdleTime = 0; this._lastActiveTimestamp = 0; this._idleStartTimestamp = 0; this._idleCount = 0; this._intervalId = setInterval(() => { if (this._lastActiveTimestamp > 0 && this._idleStartTimestamp === 0 && dateNow() - this._lastActiveTimestamp >= this._idleTimeout) { this._idleStartTimestamp = dateNow(); this._idleCount++; } }, 100); } public componentWillUnmount() { if (this._mountTimestamp === 0) { throw new Error('withAITracking:componentWillUnmount: mountTimestamp is not initialized.'); } if (this._intervalId) { clearInterval(this._intervalId); } if (this._firstActiveTimestamp === 0) { return; } const engagementTime = this.getEngagementTimeSeconds(); const metricData: IMetricTelemetry = { average: engagementTime, name: 'React Component Engaged Time (seconds)', sampleCount: 1 }; const additionalProperties: { [key: string]: any } = { 'Component Name': this._componentName }; this._reactPlugin.trackMetric(metricData, additionalProperties); } protected trackActivity = (e: React.SyntheticEvent): void => { if (this._firstActiveTimestamp === 0) { this._firstActiveTimestamp = dateNow(); this._lastActiveTimestamp = this._firstActiveTimestamp; } else { this._lastActiveTimestamp = dateNow(); } if (this._idleStartTimestamp > 0) { const lastIdleTime = this._lastActiveTimestamp - this._idleStartTimestamp; this._totalIdleTime += lastIdleTime; this._idleStartTimestamp = 0; } } private getEngagementTimeSeconds(): number { return (dateNow() - this._firstActiveTimestamp - this._totalIdleTime - this._idleCount * this._idleTimeout) / 1000; } } /** * Higher-order component function to hook Application Insights tracking * in a React component's lifecycle. * * @param reactPlugin - ReactPlugin instance * @param Component - the React component to be instrumented * @param componentName - (optional) component name * @param className - (optional) className of the HOC div */ export default function withAITracking

(reactPlugin: ReactPlugin, Component: React.ComponentType

, componentName?: string, className?: string): React.ComponentClass

{ if (componentName === undefined || componentName === null || typeof componentName !== 'string') { componentName = Component.prototype && Component.prototype.constructor && Component.prototype.constructor.name || 'Unknown'; } if (className === undefined || className === null || typeof className !== 'string') { className = ''; } return class extends AITrackedComponentBase

{ public constructor(props: P) { super(props, reactPlugin, componentName); } public render() { return (

); } } }