feat(analytics): local tracks duration event.

This commit is contained in:
Hristo Terezov 2019-05-21 11:32:20 +01:00
parent 371ca4eef1
commit 70921bb6ef
5 changed files with 215 additions and 8 deletions

View File

@ -372,6 +372,28 @@ export function createLiveStreamingDialogEvent(dialogName, buttonName) {
}; };
} }
/**
* Creates an event with the local tracks duration.
*
* @param {Object} duration - The object with the duration of the local tracks.
* @returns {Object} The event in a format suitable for sending via
* sendAnalytics.
*/
export function createLocalTracksDurationEvent(duration) {
const { audio, video, conference } = duration;
const { camera, desktop } = video;
return {
action: 'local.tracks.durations',
attributes: {
audio: audio.value,
camera: camera.value,
conference: conference.value,
desktop: desktop.value
}
};
}
/** /**
* Creates an event which indicates that an action related to recording has * Creates an event which indicates that an action related to recording has
* occured. * occured.

View File

@ -0,0 +1,11 @@
// @flow
/**
* The type of (redux) action which signals that local media duration has changed.
*
* {
* type: UPDATE_LOCAL_TRACKS_DURATION,
* localTracksDuration: Object
* }
*/
export const UPDATE_LOCAL_TRACKS_DURATION = 'UPDATE_LOCAL_TRACKS_DURATION';

View File

@ -2,3 +2,4 @@ export * from './AnalyticsEvents';
export * from './functions'; export * from './functions';
import './middleware'; import './middleware';
import './reducer';

View File

@ -1,8 +1,74 @@
import { SET_ROOM } from '../base/conference'; // @flow
import {
CONFERENCE_JOINED,
CONFERENCE_WILL_LEAVE,
SET_ROOM
} from '../base/conference';
import { SET_CONFIG } from '../base/config'; import { SET_CONFIG } from '../base/config';
import { MiddlewareRegistry } from '../base/redux'; import { MiddlewareRegistry } from '../base/redux';
import {
getLocalAudioTrack,
getLocalVideoTrack,
TRACK_ADDED,
TRACK_REMOVED,
TRACK_UPDATED
} from '../base/tracks';
import { initAnalytics, resetAnalytics } from './functions'; import { UPDATE_LOCAL_TRACKS_DURATION } from './actionTypes';
import { createLocalTracksDurationEvent } from './AnalyticsEvents';
import { initAnalytics, resetAnalytics, sendAnalytics } from './functions';
/**
* Calculates the duration of the local tracks.
*
* @param {Object} state - The redux state.
* @returns {Object} - The local tracks duration.
*/
function calculateLocalTrackDuration(state) {
const now = Date.now();
const { localTracksDuration } = state['features/analytics'];
const { conference } = state['features/base/conference'];
const { audio, video } = localTracksDuration;
const { camera, desktop } = video;
const tracks = state['features/base/tracks'];
const audioTrack = getLocalAudioTrack(tracks);
const videoTrack = getLocalVideoTrack(tracks);
const newDuration = { ...localTracksDuration };
if (!audioTrack || audioTrack.muted || !conference) {
newDuration.audio = {
startedTime: -1,
value: audio.value + (audio.startedTime === -1 ? 0 : now - audio.startedTime)
};
} else if (audio.startedTime === -1) {
newDuration.audio.startedTime = now;
}
if (!videoTrack || videoTrack.muted || !conference) {
newDuration.video = {
camera: {
startedTime: -1,
value: camera.value + (camera.startedTime === -1 ? 0 : now - camera.startedTime)
},
desktop: {
startedTime: -1,
value: desktop.value + (desktop.startedTime === -1 ? 0 : now - desktop.startedTime)
}
};
} else {
const { videoType } = videoTrack;
if (video[videoType].startedTime === -1) {
newDuration.video[videoType].startedTime = now;
}
}
return {
...localTracksDuration,
...newDuration
};
}
/** /**
* Middleware which intercepts config actions to handle evaluating analytics * Middleware which intercepts config actions to handle evaluating analytics
@ -12,25 +78,81 @@ import { initAnalytics, resetAnalytics } from './functions';
* @returns {Function} * @returns {Function}
*/ */
MiddlewareRegistry.register(store => next => action => { MiddlewareRegistry.register(store => next => action => {
switch (action.type) { if (action.type === SET_CONFIG) {
case SET_CONFIG: {
if (navigator.product === 'ReactNative') { if (navigator.product === 'ReactNative') {
// Reseting the analytics is currently not needed for web because // Reseting the analytics is currently not needed for web because
// the user will be redirected to another page and new instance of // the user will be redirected to another page and new instance of
// Analytics will be created and initialized. // Analytics will be created and initialized.
resetAnalytics(); resetAnalytics();
} }
}
const result = next(action);
switch (action.type) {
case CONFERENCE_JOINED: {
const { dispatch, getState } = store;
const state = getState();
dispatch({
type: UPDATE_LOCAL_TRACKS_DURATION,
localTracksDuration: {
...calculateLocalTrackDuration(state),
conference: {
startedTime: Date.now(),
value: 0
}
}
});
break;
}
case CONFERENCE_WILL_LEAVE: {
const { dispatch, getState } = store;
const state = getState();
const { localTracksDuration } = state['features/analytics'];
const newLocalTracksDuration = {
...calculateLocalTrackDuration(state),
conference: {
startedTime: -1,
value: Date.now() - localTracksDuration.conference.startedTime
}
};
sendAnalytics(createLocalTracksDurationEvent(newLocalTracksDuration));
dispatch({
type: UPDATE_LOCAL_TRACKS_DURATION,
localTracksDuration: newLocalTracksDuration
});
break; break;
} }
case SET_ROOM: { case SET_ROOM: {
const result = next(action);
initAnalytics(store); initAnalytics(store);
break;
}
case TRACK_ADDED:
case TRACK_REMOVED:
case TRACK_UPDATED: {
const { dispatch, getState } = store;
const state = getState();
const { localTracksDuration } = state['features/analytics'];
return result; if (localTracksDuration.conference.startedTime === -1) {
// We don't want to track the media duration if the conference is not joined yet because otherwise we won't
// be able to compare them with the conference duration (from conference join to conference will leave).
break;
}
dispatch({
type: UPDATE_LOCAL_TRACKS_DURATION,
localTracksDuration: {
...localTracksDuration,
...calculateLocalTrackDuration(state)
}
});
break;
} }
} }
return next(action); return result;
}); });

View File

@ -0,0 +1,51 @@
// @flow
import { ReducerRegistry } from '../base/redux';
import { UPDATE_LOCAL_TRACKS_DURATION } from './actionTypes';
/**
* Initial state.
*/
const DEFAULT_STATE = {
localTracksDuration: {
audio: {
startedTime: -1,
value: 0
},
video: {
camera: {
startedTime: -1,
value: 0
},
desktop: {
startedTime: -1,
value: 0
}
},
conference: {
startedTime: -1,
value: 0
}
}
};
/**
* Listen for actions which changes the state of the analytics feature.
*
* @param {Object} state - The Redux state of the feature features/analytics.
* @param {Object} action - Action object.
* @param {string} action.type - Type of action.
* @returns {Object}
*/
ReducerRegistry.register('features/analytics', (state = DEFAULT_STATE, action) => {
switch (action.type) {
case UPDATE_LOCAL_TRACKS_DURATION:
return {
...state,
localTracksDuration: action.localTracksDuration
};
default:
return state;
}
});