diff --git a/react/features/analytics/AnalyticsEvents.js b/react/features/analytics/AnalyticsEvents.js index c16ba61fd..26e861151 100644 --- a/react/features/analytics/AnalyticsEvents.js +++ b/react/features/analytics/AnalyticsEvents.js @@ -884,3 +884,18 @@ export function createScreensharingCaptureTakenEvent() { action: 'screen.sharing.capture.taken' }; } + +/** + * Creates an event for an action on breakout rooms. + * + * @param {string} actionSubject - The subject that was acted upon. + * @returns {Object} The event in a format suitable for sending via + * sendAnalytics. + */ +export function createBreakoutRoomsEvent(actionSubject) { + return { + action: 'clicked', + actionSubject: `${actionSubject}.button`, + source: 'breakout.rooms' + }; +} diff --git a/react/features/breakout-rooms/actions.js b/react/features/breakout-rooms/actions.js index 09a012295..5d4374cbd 100644 --- a/react/features/breakout-rooms/actions.js +++ b/react/features/breakout-rooms/actions.js @@ -4,6 +4,7 @@ import i18next from 'i18next'; import _ from 'lodash'; import type { Dispatch } from 'redux'; +import { createBreakoutRoomsEvent, sendAnalytics } from '../analytics'; import { conferenceLeft, conferenceWillLeave, @@ -36,6 +37,8 @@ export function createBreakoutRoom(name?: string) { const index = Object.keys(rooms).length; const subject = name || i18next.t('breakoutRooms.defaultName', { index }); + sendAnalytics(createBreakoutRoomsEvent('create')); + // $FlowExpectedError getCurrentConference(getState)?.getBreakoutRooms() ?.createBreakoutRoom(subject); @@ -54,6 +57,8 @@ export function closeBreakoutRoom(roomId: string) { const room = rooms[roomId]; const mainRoom = getMainRoom(getState); + sendAnalytics(createBreakoutRoomsEvent('close')); + if (room && mainRoom) { Object.values(room.participants).forEach(p => { @@ -72,6 +77,8 @@ export function closeBreakoutRoom(roomId: string) { */ export function removeBreakoutRoom(breakoutRoomJid: string) { return (dispatch: Dispatch, getState: Function) => { + sendAnalytics(createBreakoutRoomsEvent('remove')); + // $FlowExpectedError getCurrentConference(getState)?.getBreakoutRooms() ?.removeBreakoutRoom(breakoutRoomJid); @@ -89,6 +96,7 @@ export function autoAssignToBreakoutRooms() { const breakoutRooms = _.filter(rooms, (room: Object) => !room.isMainRoom); if (breakoutRooms) { + sendAnalytics(createBreakoutRoomsEvent('auto.assign')); const participantIds = Array.from(getRemoteParticipants(getState).keys()); const length = Math.ceil(participantIds.length / breakoutRooms.length); diff --git a/react/features/breakout-rooms/components/native/BreakoutRoomContextMenu.js b/react/features/breakout-rooms/components/native/BreakoutRoomContextMenu.js index 7a70a636e..d0755c7c2 100644 --- a/react/features/breakout-rooms/components/native/BreakoutRoomContextMenu.js +++ b/react/features/breakout-rooms/components/native/BreakoutRoomContextMenu.js @@ -6,6 +6,7 @@ import { TouchableOpacity } from 'react-native'; import { Text } from 'react-native-paper'; import { useDispatch, useSelector } from 'react-redux'; +import { createBreakoutRoomsEvent, sendAnalytics } from '../../../analytics'; import { hideDialog } from '../../../base/dialog/actions'; import BottomSheet from '../../../base/dialog/components/native/BottomSheet'; import { @@ -32,6 +33,7 @@ const BreakoutRoomContextMenu = ({ room }: Props) => { const { t } = useTranslation(); const onJoinRoom = useCallback(() => { + sendAnalytics(createBreakoutRoomsEvent('join')); dispatch(moveToRoom(room.jid)); closeDialog(); }, [ dispatch, room ]); diff --git a/react/features/breakout-rooms/components/native/LeaveBreakoutRoomButton.js b/react/features/breakout-rooms/components/native/LeaveBreakoutRoomButton.js index 73142d599..46f64a840 100644 --- a/react/features/breakout-rooms/components/native/LeaveBreakoutRoomButton.js +++ b/react/features/breakout-rooms/components/native/LeaveBreakoutRoomButton.js @@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next'; import { Button } from 'react-native-paper'; import { useDispatch } from 'react-redux'; +import { createBreakoutRoomsEvent, sendAnalytics } from '../../../analytics'; import { moveToRoom } from '../../actions'; import styles from './styles'; @@ -13,8 +14,10 @@ const LeaveBreakoutRoomButton = () => { const { t } = useTranslation(); const dispatch = useDispatch(); - const onLeave = useCallback(() => - dispatch(moveToRoom()) + const onLeave = useCallback(() => { + sendAnalytics(createBreakoutRoomsEvent('leave')); + dispatch(moveToRoom()); + } , [ dispatch ]); return ( diff --git a/react/features/breakout-rooms/components/web/JoinQuickActionButton.js b/react/features/breakout-rooms/components/web/JoinQuickActionButton.js index 6dc1cc993..9ceb51577 100644 --- a/react/features/breakout-rooms/components/web/JoinQuickActionButton.js +++ b/react/features/breakout-rooms/components/web/JoinQuickActionButton.js @@ -5,6 +5,7 @@ import React, { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { useDispatch } from 'react-redux'; +import { createBreakoutRoomsEvent, sendAnalytics } from '../../../analytics'; import { QuickActionButton } from '../../../base/components'; import { moveToRoom } from '../../actions'; @@ -31,6 +32,7 @@ const JoinActionButton = ({ room }: Props) => { const onJoinRoom = useCallback(e => { e.stopPropagation(); + sendAnalytics(createBreakoutRoomsEvent('join')); dispatch(moveToRoom(room.jid)); }, [ dispatch, room ]); diff --git a/react/features/breakout-rooms/components/web/LeaveButton.js b/react/features/breakout-rooms/components/web/LeaveButton.js index d402868d5..5d68e2556 100644 --- a/react/features/breakout-rooms/components/web/LeaveButton.js +++ b/react/features/breakout-rooms/components/web/LeaveButton.js @@ -5,6 +5,7 @@ import React, { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { useDispatch } from 'react-redux'; +import { createBreakoutRoomsEvent, sendAnalytics } from '../../../analytics'; import ParticipantPaneBaseButton from '../../../participants-pane/components/web/ParticipantPaneBaseButton'; import { moveToRoom } from '../../actions'; @@ -28,6 +29,7 @@ export const LeaveButton = () => { const styles = useStyles(); const onLeave = useCallback(() => { + sendAnalytics(createBreakoutRoomsEvent('leave')); dispatch(moveToRoom()); }, [ dispatch ]); diff --git a/react/features/breakout-rooms/components/web/RoomContextMenu.js b/react/features/breakout-rooms/components/web/RoomContextMenu.js index b119285bb..1230c5a50 100644 --- a/react/features/breakout-rooms/components/web/RoomContextMenu.js +++ b/react/features/breakout-rooms/components/web/RoomContextMenu.js @@ -4,6 +4,7 @@ import React, { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { useDispatch, useSelector } from 'react-redux'; +import { createBreakoutRoomsEvent, sendAnalytics } from '../../../analytics'; import { ContextMenu, ContextMenuItemGroup } from '../../../base/components'; import { IconClose, @@ -54,6 +55,7 @@ export const RoomContextMenu = ({ const _overflowDrawer = useSelector(showOverflowDrawer); const onJoinRoom = useCallback(() => { + sendAnalytics(createBreakoutRoomsEvent('join')); dispatch(moveToRoom(room.id)); }, [ dispatch, room ]); diff --git a/react/features/participants-pane/components/web/MeetingParticipantContextMenu.js b/react/features/participants-pane/components/web/MeetingParticipantContextMenu.js index 10f2d3208..379fe0f9c 100644 --- a/react/features/participants-pane/components/web/MeetingParticipantContextMenu.js +++ b/react/features/participants-pane/components/web/MeetingParticipantContextMenu.js @@ -2,6 +2,7 @@ import { withStyles } from '@material-ui/styles'; import React, { Component } from 'react'; +import { createBreakoutRoomsEvent, sendAnalytics } from '../../../analytics'; import { approveParticipant } from '../../../av-moderation/actions'; import { Avatar } from '../../../base/avatar'; import { ContextMenu, ContextMenuItemGroup } from '../../../base/components'; @@ -305,6 +306,7 @@ class MeetingParticipantContextMenu extends Component { return () => { const { _participant, dispatch } = this.props; + sendAnalytics(createBreakoutRoomsEvent('send.participant.to.room')); dispatch(sendParticipantToRoom(_participant.id, room.id)); }; } diff --git a/react/features/video-menu/components/native/SendToBreakoutRoom.js b/react/features/video-menu/components/native/SendToBreakoutRoom.js index 676341582..ffbe01363 100644 --- a/react/features/video-menu/components/native/SendToBreakoutRoom.js +++ b/react/features/video-menu/components/native/SendToBreakoutRoom.js @@ -1,5 +1,6 @@ // @flow +import { createBreakoutRoomsEvent, sendAnalytics } from '../../../analytics'; import { translate } from '../../../base/i18n'; import { IconRingGroup } from '../../../base/icons'; import { isLocalParticipantModerator } from '../../../base/participants'; @@ -57,6 +58,7 @@ class SendToBreakoutRoom extends AbstractButton { _handleClick() { const { dispatch, participantID, room } = this.props; + sendAnalytics(createBreakoutRoomsEvent('send.participant.to.room')); dispatch(sendParticipantToRoom(participantID, room.id)); } }