feat(analytics): Adds metric for SS issues.

This commit is contained in:
Hristo Terezov 2020-12-18 14:42:39 -06:00
parent a5fe26bfdb
commit d4596889df
3 changed files with 58 additions and 6 deletions

View File

@ -6,18 +6,21 @@ import ReactDOM from 'react-dom';
import { I18nextProvider } from 'react-i18next'; import { I18nextProvider } from 'react-i18next';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { createScreenSharingIssueEvent, sendAnalytics } from '../../../react/features/analytics';
import { Avatar } from '../../../react/features/base/avatar'; import { Avatar } from '../../../react/features/base/avatar';
import { i18next } from '../../../react/features/base/i18n'; import { i18next } from '../../../react/features/base/i18n';
import { import {
JitsiParticipantConnectionStatus JitsiParticipantConnectionStatus
} from '../../../react/features/base/lib-jitsi-meet'; } from '../../../react/features/base/lib-jitsi-meet';
import { VIDEO_TYPE } from '../../../react/features/base/media'; import { MEDIA_TYPE, VIDEO_TYPE } from '../../../react/features/base/media';
import { getParticipantById } from '../../../react/features/base/participants'; import { getParticipantById } from '../../../react/features/base/participants';
import { getTrackByMediaTypeAndParticipant } from '../../../react/features/base/tracks';
import { CHAT_SIZE } from '../../../react/features/chat'; import { CHAT_SIZE } from '../../../react/features/chat';
import { import {
updateKnownLargeVideoResolution updateKnownLargeVideoResolution
} from '../../../react/features/large-video/actions'; } from '../../../react/features/large-video/actions';
import { PresenceLabel } from '../../../react/features/presence-status'; import { PresenceLabel } from '../../../react/features/presence-status';
import { shouldDisplayTileView } from '../../../react/features/video-layout';
/* eslint-enable no-unused-vars */ /* eslint-enable no-unused-vars */
import UIEvents from '../../../service/UI/UIEvents'; import UIEvents from '../../../service/UI/UIEvents';
import { createDeferred } from '../../util/helpers'; import { createDeferred } from '../../util/helpers';
@ -203,8 +206,7 @@ export default class LargeVideoManager {
// FIXME this does not really make sense, because the videoType // FIXME this does not really make sense, because the videoType
// (camera or desktop) is a completely different thing than // (camera or desktop) is a completely different thing than
// the video container type (Etherpad, SharedVideo, VideoContainer). // the video container type (Etherpad, SharedVideo, VideoContainer).
const isVideoContainer const isVideoContainer = LargeVideoManager.isVideoContainer(videoType);
= LargeVideoManager.isVideoContainer(videoType);
this.newStreamData = null; this.newStreamData = null;
@ -219,14 +221,15 @@ export default class LargeVideoManager {
this.updateAvatar(); this.updateAvatar();
const isVideoMuted = !stream || stream.isMuted(); const isVideoMuted = !stream || stream.isMuted();
const participant = getParticipantById(APP.store.getState(), id); const state = APP.store.getState();
const participant = getParticipantById(state, id);
const connectionStatus = participant?.connectionStatus; const connectionStatus = participant?.connectionStatus;
const isVideoRenderable = !isVideoMuted const isVideoRenderable = !isVideoMuted
&& (APP.conference.isLocalId(id) || connectionStatus === JitsiParticipantConnectionStatus.ACTIVE); && (APP.conference.isLocalId(id) || connectionStatus === JitsiParticipantConnectionStatus.ACTIVE);
const isAudioOnly = APP.conference.isAudioOnly();
const showAvatar const showAvatar
= isVideoContainer = isVideoContainer
&& ((APP.conference.isAudioOnly() && videoType !== VIDEO_TYPE.DESKTOP) || !isVideoRenderable); && ((isAudioOnly && videoType !== VIDEO_TYPE.DESKTOP) || !isVideoRenderable);
let promise; let promise;
@ -238,6 +241,29 @@ export default class LargeVideoManager {
// If the intention of this switch is to show the avatar // If the intention of this switch is to show the avatar
// we need to make sure that the video is hidden // we need to make sure that the video is hidden
promise = container.hide(); promise = container.hide();
if ((!shouldDisplayTileView(state) || participant?.pinned) // In theory the tile view may not be
// enabled yet when we auto pin the participant.
&& participant && !participant.local && !participant.isFakeParticipant) {
// remote participant only
const track = getTrackByMediaTypeAndParticipant(
state['features/base/tracks'], MEDIA_TYPE.VIDEO, id);
const isScreenSharing = track?.videoType === 'desktop';
if (isScreenSharing) {
// send the event
sendAnalytics(createScreenSharingIssueEvent({
source: 'large-video',
connectionStatus,
isVideoMuted,
isAudioOnly,
isVideoContainer,
videoType
}));
}
}
} else { } else {
promise = container.show(); promise = container.show();
} }

View File

@ -8,6 +8,7 @@ import ReactDOM from 'react-dom';
import { I18nextProvider } from 'react-i18next'; import { I18nextProvider } from 'react-i18next';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { createScreenSharingIssueEvent, sendAnalytics } from '../../../react/features/analytics';
import { AudioLevelIndicator } from '../../../react/features/audio-level-indicator'; import { AudioLevelIndicator } from '../../../react/features/audio-level-indicator';
import { Avatar as AvatarDisplay } from '../../../react/features/base/avatar'; import { Avatar as AvatarDisplay } from '../../../react/features/base/avatar';
import { i18next } from '../../../react/features/base/i18n'; import { i18next } from '../../../react/features/base/i18n';
@ -512,6 +513,18 @@ export default class SmallVideo {
if (this.displayMode !== oldDisplayMode) { if (this.displayMode !== oldDisplayMode) {
logger.debug(`Displaying ${displayModeString} for ${this.id}, data: [${JSON.stringify(displayModeInput)}]`); logger.debug(`Displaying ${displayModeString} for ${this.id}, data: [${JSON.stringify(displayModeInput)}]`);
} }
if (this.displayMode !== DISPLAY_VIDEO
&& this.displayMode !== DISPLAY_VIDEO_WITH_NAME
&& displayModeInput.tileViewActive
&& displayModeInput.isScreenSharing
&& !displayModeInput.isAudioOnly) {
// send the event
sendAnalytics(createScreenSharingIssueEvent({
source: 'thumbnail',
...displayModeInput
}));
}
} }
/** /**

View File

@ -588,6 +588,19 @@ export function createScreenSharingEvent(action) {
}; };
} }
/**
* Creates an event which indicates the screen sharing video is not displayed when it needs to be displayed.
*
* @param {Object} attributes - Additional information that describes the issue.
* @returns {Object} The event in a format suitable for sending via sendAnalytics.
*/
export function createScreenSharingIssueEvent(attributes) {
return {
action: 'screen.sharing.issue',
attributes
};
}
/** /**
* The local participant failed to send a "selected endpoint" message to the * The local participant failed to send a "selected endpoint" message to the
* bridge. * bridge.