diff --git a/react/features/analytics/AnalyticsEvents.js b/react/features/analytics/AnalyticsEvents.js index 049bffe83f..f0bfc96bfa 100644 --- a/react/features/analytics/AnalyticsEvents.js +++ b/react/features/analytics/AnalyticsEvents.js @@ -129,16 +129,21 @@ export function createFeedbackOpenEvent() { } /** - * Creates an event which indicates that the invite dialog was closed. This is - * not a TYPE_UI event, since it is not necessarily the result of a user - * interaction. + * Creates an event for an action regarding the AddPeopleDialog (invites). * + * @param {string} action - The action that the event represents. + * @param {string} actionSubject - The subject that was acted upon. + * @param {boolean} attributes - Additional attributes to attach to the event. * @returns {Object} The event in a format suitable for sending via * sendAnalytics. */ -export function createInviteDialogClosedEvent() { +export function createInviteDialogEvent( + action, actionSubject, attributes = {}) { return { - action: 'invite.dialog.closed' + action, + actionSubject, + attributes, + source: 'inviteDialog' }; } diff --git a/react/features/invite/components/AddPeopleDialog.web.js b/react/features/invite/components/AddPeopleDialog.web.js index 5541066735..29a86dcdb0 100644 --- a/react/features/invite/components/AddPeopleDialog.web.js +++ b/react/features/invite/components/AddPeopleDialog.web.js @@ -6,6 +6,7 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { connect } from 'react-redux'; +import { createInviteDialogEvent, sendAnalytics } from '../../analytics'; import { getInviteURL } from '../../base/connection'; import { Dialog, hideDialog } from '../../base/dialog'; import { translate } from '../../base/i18n'; @@ -143,6 +144,17 @@ class AddPeopleDialog extends Component<*, *> { }; } + /** + * Sends an analytics event to record the dialog has been shown. + * + * @inheritdoc + * @returns {void} + */ + componentDidMount() { + sendAnalytics(createInviteDialogEvent( + 'invite.dialog.opened', 'dialog')); + } + /** * React Component method that executes once component is updated. * @@ -162,6 +174,17 @@ class AddPeopleDialog extends Component<*, *> { } } + /** + * Sends an analytics event to record the dialog has been closed. + * + * @inheritdoc + * @returns {void} + */ + componentWillUnmount() { + sendAnalytics(createInviteDialogEvent( + 'invite.dialog.closed', 'dialog')); + } + /** * Renders the content of this component. * @@ -231,6 +254,32 @@ class AddPeopleDialog extends Component<*, *> { return text.replace(/\D/g, ''); } + /** + * Helper for determining how many of each type of user is being invited. + * Used for logging and sending analytics related to invites. + * + * @param {Array} inviteItems - An array with the invite items, as created + * in {@link _parseQueryResults}. + * @private + * @returns {Object} An object with keys as user types and values as the + * number of invites for that type. + */ + _getInviteTypeCounts(inviteItems = []) { + const inviteTypeCounts = {}; + + inviteItems.forEach(i => { + const type = i.item.type; + + if (!inviteTypeCounts[type]) { + inviteTypeCounts[type] = 0; + } + + inviteTypeCounts[type]++; + }); + + return inviteTypeCounts; + } + _isAddDisabled: () => boolean; /** @@ -313,6 +362,15 @@ class AddPeopleDialog extends Component<*, *> { * @returns {void} */ _onSubmit() { + const inviteTypeCounts + = this._getInviteTypeCounts(this.state.inviteItems); + + sendAnalytics(createInviteDialogEvent( + 'clicked', 'inviteButton', { + ...inviteTypeCounts, + inviteAllowed: this._isAddDisabled() + })); + if (this._isAddDisabled()) { return; } @@ -393,7 +451,16 @@ class AddPeopleDialog extends Component<*, *> { // If any invites are left that means something failed to send // so treat it as an error. if (invitesLeftToSend.length) { - logger.error(`${invitesLeftToSend.length} invites failed`); + const erroredInviteTypeCounts + = this._getInviteTypeCounts(invitesLeftToSend); + + logger.error(`${invitesLeftToSend.length} invites failed`, + erroredInviteTypeCounts); + + sendAnalytics(createInviteDialogEvent( + 'error', 'invite', { + ...erroredInviteTypeCounts + })); this.setState({ addToCallInProgress: false,