jiti-meet/react/features/analytics/middleware.ts

180 lines
5.8 KiB
TypeScript

import { IReduxState } from '../app/types';
import {
CONFERENCE_JOINED,
CONFERENCE_WILL_LEAVE,
SET_ROOM
} from '../base/conference/actionTypes';
import { SET_CONFIG } from '../base/config/actionTypes';
import { SET_NETWORK_INFO } from '../base/net-info/actionTypes';
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
import {
TRACK_ADDED,
TRACK_REMOVED,
TRACK_UPDATED
} from '../base/tracks/actionTypes';
import {
getLocalAudioTrack,
getLocalVideoTrack
} from '../base/tracks/functions';
import { createLocalTracksDurationEvent, createNetworkInfoEvent } from './AnalyticsEvents';
import { UPDATE_LOCAL_TRACKS_DURATION } from './actionTypes';
import { createHandlers, 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: IReduxState) {
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 as keyof typeof video].startedTime === -1) {
newDuration.video[videoType as keyof typeof video].startedTime = now;
}
}
return {
...localTracksDuration,
...newDuration
};
}
/**
* Middleware which intercepts config actions to handle evaluating analytics
* config based on the config stored in the store.
*
* @param {Store} store - The redux store.
* @returns {Function}
*/
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case SET_CONFIG:
if (navigator.product === 'ReactNative') {
// Resetting the analytics is currently not needed for web because
// the user will be redirected to another page and new instance of
// Analytics will be created and initialized.
resetAnalytics();
}
break;
case SET_ROOM: {
// createHandlers is called before the SET_ROOM action is executed in order for Amplitude to initialize before
// the deeplinking logic is executed (after the SET_ROOM action) so that the Amplitude device id is available
// if needed.
const createHandlersPromise = createHandlers(store);
const result = next(action);
createHandlersPromise.then(handlers => {
initAnalytics(store, handlers);
});
return result;
}
}
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;
}
case SET_NETWORK_INFO:
sendAnalytics(
createNetworkInfoEvent({
isOnline: action.isOnline,
details: action.details,
networkType: action.networkType
}));
break;
case TRACK_ADDED:
case TRACK_REMOVED:
case TRACK_UPDATED: {
const { dispatch, getState } = store;
const state = getState();
const { localTracksDuration } = state['features/analytics'];
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 result;
});