diff --git a/src/CONST.js b/src/CONST.js index daefbe0ec6be..60d444e65fe5 100644 --- a/src/CONST.js +++ b/src/CONST.js @@ -64,6 +64,7 @@ const CONST = { HOMEPAGE_REPORTS_LOADED: 'homepage_reports_loaded', SWITCH_REPORT: 'switch_report', COLD: 'cold', + REPORT_ACTION_ITEM_LAYOUT_DEBOUNCE_TIME: 1500, }, MESSAGES: { // eslint-disable-next-line max-len diff --git a/src/libs/actions/Timing.js b/src/libs/actions/Timing.js index 4d1eae31a36f..9dd385750d3e 100644 --- a/src/libs/actions/Timing.js +++ b/src/libs/actions/Timing.js @@ -17,10 +17,11 @@ function start(eventName) { * * @param {String} eventName - event name used as timestamp key * @param {String} [secondaryName] - optional secondary event name, passed to grafana + * @param {Number} [offset] - optional param to offset the time */ -function end(eventName, secondaryName = '') { +function end(eventName, secondaryName = '', offset = 0) { if (eventName in timestampData) { - const eventTime = Date.now() - timestampData[eventName]; + const eventTime = Date.now() - timestampData[eventName] - offset; const grafanaEventName = secondaryName ? `expensify.cash.${eventName}.${secondaryName}` : `expensify.cash.${eventName}`; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 7929fd272407..9ff91edb14ac 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -38,6 +38,9 @@ const propTypes = { // Should we display the new indicator on top of the comment? shouldDisplayNewIndicator: PropTypes.bool.isRequired, + + // Runs when the view enclosing the chat message lays out indicating it has rendered + onLayout: PropTypes.func.isRequired, }; const defaultProps = { @@ -122,7 +125,7 @@ class ReportActionItem extends Component { {this.props.shouldDisplayNewIndicator && ( )} - + {!this.props.displayAsGroup ? ( diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 4cb8af7c7071..ae9e4ef1e80e 100644 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -89,6 +89,13 @@ class ReportActionsView extends React.Component { this.loadMoreChats = this.loadMoreChats.bind(this); this.sortedReportActions = []; + // We are debouncing this call with a specific delay so that when all items in the list layout we can measure + // the total time it took to complete. + this.recordTimeToMeasureItemLayout = _.debounce( + this.recordTimeToMeasureItemLayout.bind(this), + CONST.TIMING.REPORT_ACTION_ITEM_LAYOUT_DEBOUNCE_TIME, + ); + this.state = { isLoadingMoreChats: false, }; @@ -113,7 +120,6 @@ class ReportActionsView extends React.Component { setNewMarkerPosition(this.props.reportID, oldestUnreadSequenceNumber); fetchActions(this.props.reportID); - Timing.end(CONST.TIMING.SWITCH_REPORT, CONST.TIMING.COLD); } shouldComponentUpdate(nextProps, nextState) { @@ -187,6 +193,10 @@ class ReportActionsView extends React.Component { this.keyboardEvent.remove(); } + // We must cancel the debounce function so that we do not call the function when switching to a new chat before + // the previous one has finished loading completely. + this.recordTimeToMeasureItemLayout.cancel(); + AppState.removeEventListener('change', this.onVisibilityChange); unsubscribeFromReportChannel(this.props.reportID); @@ -297,6 +307,16 @@ class ReportActionsView extends React.Component { updateLastReadActionID(this.props.reportID); } + /** + * Runs each time a ReportActionItem is laid out. This method is debounced so we wait until the component has + * finished laying out items before recording the chat as switched. + */ + recordTimeToMeasureItemLayout() { + // We are offsetting the time measurement here so that we can subtract our debounce time from the initial time + // and get the actual time it took to load the report + Timing.end(CONST.TIMING.SWITCH_REPORT, CONST.TIMING.COLD, CONST.TIMING.REPORT_ACTION_ITEM_LAYOUT_DEBOUNCE_TIME); + } + /** * This function overrides the CellRendererComponent (defaults to a plain View), giving each ReportActionItem a * higher z-index than the one below it. This prevents issues where the ReportActionContextMenu overlapping between @@ -343,6 +363,7 @@ class ReportActionsView extends React.Component { isMostRecentIOUReportAction={item.action.sequenceNumber === this.mostRecentIOUReportSequenceNumber} iouReportID={this.props.report.iouReportID} hasOutstandingIOU={this.props.report.hasOutstandingIOU} + onLayout={this.recordTimeToMeasureItemLayout} /> ); }