jiti-meet/react/features/analytics/functions.js

283 lines
8.5 KiB
JavaScript
Raw Normal View History

2017-10-04 22:36:09 +00:00
// @flow
import { API_ID } from '../../../modules/API/constants';
2020-05-20 10:57:03 +00:00
import {
checkChromeExtensionsInstalled,
isMobileBrowser
} from '../base/environment/utils';
import JitsiMeetJS, {
analytics,
2020-02-25 12:41:13 +00:00
browser,
isAnalyticsEnabled
} from '../base/lib-jitsi-meet';
import { getJitsiMeetGlobalNS, loadScript } from '../base/util';
2020-05-20 10:57:03 +00:00
import { AmplitudeHandler, MatomoHandler } from './handlers';
import logger from './logger';
/**
Restructures the analytics events (#2333) * ref: Restructures the pinned/unpinned events. * ref: Refactors the "audio only disabled" event. * ref: Refactors the "stream switch delay" event. * ref: Refactors the "select participant failed" event. * ref: Refactors the "initially muted" events. * ref: Refactors the screen sharing started/stopped events. * ref: Restructures the "device list changed" events. * ref: Restructures the "shared video" events. * ref: Restructures the "start muted" events. * ref: Restructures the "start audio only" event. * ref: Restructures the "sync track state" event. * ref: Restructures the "callkit" events. * ref: Restructures the "replace track". * ref: Restructures keyboard shortcuts events. * ref: Restructures most of the toolbar events. * ref: Refactors the API events. * ref: Restructures the video quality, profile button and invite dialog events. * ref: Refactors the "device changed" events. * ref: Refactors the page reload event. * ref: Removes an unused function. * ref: Removes a method which is needlessly exposed under a different name. * ref: Refactors the events from the remote video menu. * ref: Refactors the events from the profile pane. * ref: Restructures the recording-related events. Removes events fired when recording with something other than jibri (which isn't currently supported anyway). * ref: Cleans up AnalyticsEvents.js. * ref: Removes an unused function and adds documentation. * feat: Adds events for all API calls. * fix: Addresses feedback. * fix: Brings back mistakenly removed code. * fix: Simplifies code and fixes a bug in toggleFilmstrip when the 'visible' parameter is defined. * feat: Removes the resolution change application log. * ref: Uses consistent naming for events' attributes. Uses "_" as a separator instead of camel case or ".". * ref: Don't add the user agent and conference name as permanent properties. The library does this on its own now. * ref: Adapts the GA handler to changes in lib-jitsi-meet. * ref: Removes unused fields from the analytics handler initializaiton. * ref: Renames the google analytics file and add docs. * fix: Fixes the push-to-talk events and logs. * npm: Updates lib-jitsi-meet to 515374c8d383cb17df8ed76427e6f0fb5ea6ff1e. * fix: Fixes a recently introduced bug in the google analytics handler. * ref: Uses "value" instead of "delay" since this is friendlier to GA.
2018-01-03 21:24:07 +00:00
* Sends an event through the lib-jitsi-meet AnalyticsAdapter interface.
*
Restructures the analytics events (#2333) * ref: Restructures the pinned/unpinned events. * ref: Refactors the "audio only disabled" event. * ref: Refactors the "stream switch delay" event. * ref: Refactors the "select participant failed" event. * ref: Refactors the "initially muted" events. * ref: Refactors the screen sharing started/stopped events. * ref: Restructures the "device list changed" events. * ref: Restructures the "shared video" events. * ref: Restructures the "start muted" events. * ref: Restructures the "start audio only" event. * ref: Restructures the "sync track state" event. * ref: Restructures the "callkit" events. * ref: Restructures the "replace track". * ref: Restructures keyboard shortcuts events. * ref: Restructures most of the toolbar events. * ref: Refactors the API events. * ref: Restructures the video quality, profile button and invite dialog events. * ref: Refactors the "device changed" events. * ref: Refactors the page reload event. * ref: Removes an unused function. * ref: Removes a method which is needlessly exposed under a different name. * ref: Refactors the events from the remote video menu. * ref: Refactors the events from the profile pane. * ref: Restructures the recording-related events. Removes events fired when recording with something other than jibri (which isn't currently supported anyway). * ref: Cleans up AnalyticsEvents.js. * ref: Removes an unused function and adds documentation. * feat: Adds events for all API calls. * fix: Addresses feedback. * fix: Brings back mistakenly removed code. * fix: Simplifies code and fixes a bug in toggleFilmstrip when the 'visible' parameter is defined. * feat: Removes the resolution change application log. * ref: Uses consistent naming for events' attributes. Uses "_" as a separator instead of camel case or ".". * ref: Don't add the user agent and conference name as permanent properties. The library does this on its own now. * ref: Adapts the GA handler to changes in lib-jitsi-meet. * ref: Removes unused fields from the analytics handler initializaiton. * ref: Renames the google analytics file and add docs. * fix: Fixes the push-to-talk events and logs. * npm: Updates lib-jitsi-meet to 515374c8d383cb17df8ed76427e6f0fb5ea6ff1e. * fix: Fixes a recently introduced bug in the google analytics handler. * ref: Uses "value" instead of "delay" since this is friendlier to GA.
2018-01-03 21:24:07 +00:00
* @param {Object} event - The event to send. It should be formatted as
* described in AnalyticsAdapter.js in lib-jitsi-meet.
* @returns {void}
*/
Restructures the analytics events (#2333) * ref: Restructures the pinned/unpinned events. * ref: Refactors the "audio only disabled" event. * ref: Refactors the "stream switch delay" event. * ref: Refactors the "select participant failed" event. * ref: Refactors the "initially muted" events. * ref: Refactors the screen sharing started/stopped events. * ref: Restructures the "device list changed" events. * ref: Restructures the "shared video" events. * ref: Restructures the "start muted" events. * ref: Restructures the "start audio only" event. * ref: Restructures the "sync track state" event. * ref: Restructures the "callkit" events. * ref: Restructures the "replace track". * ref: Restructures keyboard shortcuts events. * ref: Restructures most of the toolbar events. * ref: Refactors the API events. * ref: Restructures the video quality, profile button and invite dialog events. * ref: Refactors the "device changed" events. * ref: Refactors the page reload event. * ref: Removes an unused function. * ref: Removes a method which is needlessly exposed under a different name. * ref: Refactors the events from the remote video menu. * ref: Refactors the events from the profile pane. * ref: Restructures the recording-related events. Removes events fired when recording with something other than jibri (which isn't currently supported anyway). * ref: Cleans up AnalyticsEvents.js. * ref: Removes an unused function and adds documentation. * feat: Adds events for all API calls. * fix: Addresses feedback. * fix: Brings back mistakenly removed code. * fix: Simplifies code and fixes a bug in toggleFilmstrip when the 'visible' parameter is defined. * feat: Removes the resolution change application log. * ref: Uses consistent naming for events' attributes. Uses "_" as a separator instead of camel case or ".". * ref: Don't add the user agent and conference name as permanent properties. The library does this on its own now. * ref: Adapts the GA handler to changes in lib-jitsi-meet. * ref: Removes unused fields from the analytics handler initializaiton. * ref: Renames the google analytics file and add docs. * fix: Fixes the push-to-talk events and logs. * npm: Updates lib-jitsi-meet to 515374c8d383cb17df8ed76427e6f0fb5ea6ff1e. * fix: Fixes a recently introduced bug in the google analytics handler. * ref: Uses "value" instead of "delay" since this is friendlier to GA.
2018-01-03 21:24:07 +00:00
export function sendAnalytics(event: Object) {
try {
analytics.sendEvent(event);
} catch (e) {
logger.warn(`Error sending analytics event: ${e}`);
}
}
/**
* Return saved amplitude identity info such as session id, device id and user id. We assume these do not change for
* the duration of the conference.
*
* @returns {Object}
*/
export function getAmplitudeIdentity() {
return analytics.amplitudeIdentityProps;
}
/**
* Resets the analytics adapter to its initial state - removes handlers, cache,
* disabled state, etc.
*
* @returns {void}
*/
export function resetAnalytics() {
analytics.reset();
}
/**
2020-01-30 16:57:41 +00:00
* Creates the analytics handlers.
*
2020-01-30 16:57:41 +00:00
* @param {Store} store - The redux store in which the specified {@code action} is being dispatched.
* @returns {Promise} Resolves with the handlers that have been successfully loaded.
*/
2020-01-30 16:57:41 +00:00
export function createHandlers({ getState }: { getState: Function }) {
getJitsiMeetGlobalNS().analyticsHandlers = [];
2017-09-25 17:31:19 +00:00
window.analyticsHandlers = []; // Legacy support.
2020-01-30 16:57:41 +00:00
if (!isAnalyticsEnabled(getState)) {
return Promise.resolve([]);
}
const state = getState();
const config = state['features/base/config'];
const { locationURL } = state['features/base/connection'];
const host = locationURL ? locationURL.host : '';
const {
analytics: analyticsConfig = {},
deploymentInfo
} = config;
2019-01-03 13:54:02 +00:00
const {
amplitudeAPPKey,
blackListedEvents,
scriptURLs,
googleAnalyticsTrackingId,
2020-04-28 11:23:04 +00:00
matomoEndpoint,
matomoSiteID,
whiteListedEvents
} = analyticsConfig;
2020-01-30 16:57:41 +00:00
const { group, user } = state['features/base/jwt'];
const handlerConstructorOptions = {
2019-01-03 13:54:02 +00:00
amplitudeAPPKey,
blackListedEvents,
envType: (deploymentInfo && deploymentInfo.envType) || 'dev',
googleAnalyticsTrackingId,
2020-04-28 11:23:04 +00:00
matomoEndpoint,
matomoSiteID,
group,
host,
product: deploymentInfo && deploymentInfo.product,
subproduct: deploymentInfo && deploymentInfo.environment,
user: user && user.id,
version: JitsiMeetJS.version,
whiteListedEvents
};
2020-01-30 16:57:41 +00:00
const handlers = [];
2020-01-30 16:57:41 +00:00
try {
const amplitude = new AmplitudeHandler(handlerConstructorOptions);
analytics.amplitudeIdentityProps = amplitude.getIdentityProps();
2020-01-30 16:57:41 +00:00
handlers.push(amplitude);
// eslint-disable-next-line no-empty
} catch (e) {}
try {
const matomo = new MatomoHandler(handlerConstructorOptions);
handlers.push(matomo);
// eslint-disable-next-line no-empty
} catch (e) {}
2020-01-30 16:57:41 +00:00
return (
_loadHandlers(scriptURLs, handlerConstructorOptions)
.then(externalHandlers => {
handlers.push(...externalHandlers);
if (handlers.length === 0) {
2020-04-28 11:23:04 +00:00
// Throwing an error in order to dispose the analytics in the catch clause due to the lack of any
2020-01-30 16:57:41 +00:00
// analytics handlers.
throw new Error('No analytics handlers created!');
}
2020-01-30 16:57:41 +00:00
return handlers;
})
.catch(e => {
analytics.dispose();
if (handlers.length !== 0) {
logger.error(e);
}
2020-01-30 16:57:41 +00:00
return [];
}));
2020-01-30 16:57:41 +00:00
}
/**
* Inits JitsiMeetJS.analytics by setting permanent properties and setting the handlers from the loaded scripts.
* NOTE: Has to be used after JitsiMeetJS.init. Otherwise analytics will be null.
*
* @param {Store} store - The redux store in which the specified {@code action} is being dispatched.
* @param {Array<Object>} handlers - The analytics handlers.
* @returns {void}
*/
export function initAnalytics({ getState }: { getState: Function }, handlers: Array<Object>) {
if (!isAnalyticsEnabled(getState) || handlers.length === 0) {
return;
}
const state = getState();
const config = state['features/base/config'];
const {
deploymentInfo
} = config;
const { group, server } = state['features/base/jwt'];
const roomName = state['features/base/conference'].room;
const permanentProperties = {};
if (server) {
permanentProperties.server = server;
}
if (group) {
permanentProperties.group = group;
}
// Report if user is using websocket
permanentProperties.websocket = navigator.product !== 'ReactNative' && typeof config.websocket === 'string';
// permanentProperties is external api
permanentProperties.externalApi = typeof API_ID === 'number';
// Report if we are loaded in iframe
permanentProperties.inIframe = _inIframe();
2020-01-30 16:57:41 +00:00
// Optionally, include local deployment information based on the
// contents of window.config.deploymentInfo.
if (deploymentInfo) {
for (const key in deploymentInfo) {
if (deploymentInfo.hasOwnProperty(key)) {
permanentProperties[key] = deploymentInfo[key];
}
2020-01-30 16:57:41 +00:00
}
}
2020-01-30 16:57:41 +00:00
analytics.addPermanentProperties(permanentProperties);
analytics.setConferenceName(roomName);
Restructures the analytics events (#2333) * ref: Restructures the pinned/unpinned events. * ref: Refactors the "audio only disabled" event. * ref: Refactors the "stream switch delay" event. * ref: Refactors the "select participant failed" event. * ref: Refactors the "initially muted" events. * ref: Refactors the screen sharing started/stopped events. * ref: Restructures the "device list changed" events. * ref: Restructures the "shared video" events. * ref: Restructures the "start muted" events. * ref: Restructures the "start audio only" event. * ref: Restructures the "sync track state" event. * ref: Restructures the "callkit" events. * ref: Restructures the "replace track". * ref: Restructures keyboard shortcuts events. * ref: Restructures most of the toolbar events. * ref: Refactors the API events. * ref: Restructures the video quality, profile button and invite dialog events. * ref: Refactors the "device changed" events. * ref: Refactors the page reload event. * ref: Removes an unused function. * ref: Removes a method which is needlessly exposed under a different name. * ref: Refactors the events from the remote video menu. * ref: Refactors the events from the profile pane. * ref: Restructures the recording-related events. Removes events fired when recording with something other than jibri (which isn't currently supported anyway). * ref: Cleans up AnalyticsEvents.js. * ref: Removes an unused function and adds documentation. * feat: Adds events for all API calls. * fix: Addresses feedback. * fix: Brings back mistakenly removed code. * fix: Simplifies code and fixes a bug in toggleFilmstrip when the 'visible' parameter is defined. * feat: Removes the resolution change application log. * ref: Uses consistent naming for events' attributes. Uses "_" as a separator instead of camel case or ".". * ref: Don't add the user agent and conference name as permanent properties. The library does this on its own now. * ref: Adapts the GA handler to changes in lib-jitsi-meet. * ref: Removes unused fields from the analytics handler initializaiton. * ref: Renames the google analytics file and add docs. * fix: Fixes the push-to-talk events and logs. * npm: Updates lib-jitsi-meet to 515374c8d383cb17df8ed76427e6f0fb5ea6ff1e. * fix: Fixes a recently introduced bug in the google analytics handler. * ref: Uses "value" instead of "delay" since this is friendlier to GA.
2018-01-03 21:24:07 +00:00
2020-01-30 16:57:41 +00:00
// Set the handlers last, since this triggers emptying of the cache
analytics.setAnalyticsHandlers(handlers);
2020-02-25 12:41:13 +00:00
if (!isMobileBrowser() && browser.isChrome()) {
const bannerCfg = state['features/base/config'].chromeExtensionBanner;
checkChromeExtensionsInstalled(bannerCfg).then(extensionsInstalled => {
if (extensionsInstalled?.length) {
analytics.addPermanentProperties({
hasChromeExtension: extensionsInstalled.some(ext => ext)
});
}
});
}
}
/**
* Checks whether we are loaded in iframe.
*
* @returns {boolean} Returns {@code true} if loaded in iframe.
* @private
*/
function _inIframe() {
if (navigator.product === 'ReactNative') {
return false;
}
try {
return window.self !== window.top;
} catch (e) {
return true;
}
}
/**
2020-01-30 16:57:41 +00:00
* Tries to load the scripts for the analytics handlers and creates them.
*
* @param {Array} scriptURLs - The array of script urls to load.
2020-01-30 16:57:41 +00:00
* @param {Object} handlerConstructorOptions - The default options to pass when creating handlers.
* @private
2020-01-30 16:57:41 +00:00
* @returns {Promise} Resolves with the handlers that have been successfully loaded and rejects if there are no handlers
* loaded or the analytics is disabled.
*/
function _loadHandlers(scriptURLs = [], handlerConstructorOptions) {
const promises = [];
for (const url of scriptURLs) {
promises.push(
loadScript(url).then(
() => {
return { type: 'success' };
},
error => {
return {
type: 'error',
error,
url
};
}));
}
return Promise.all(promises).then(values => {
for (const el of values) {
if (el.type === 'error') {
logger.warn(`Failed to load ${el.url}: ${el.error}`);
}
}
// analyticsHandlers is the handlers we want to use
// we search for them in the JitsiMeetGlobalNS, but also
// check the old location to provide legacy support
2017-09-04 09:35:09 +00:00
const analyticsHandlers = [
...getJitsiMeetGlobalNS().analyticsHandlers,
2020-01-30 16:57:41 +00:00
...window.analyticsHandlers
];
2017-10-04 22:36:09 +00:00
const handlers = [];
2017-10-04 22:36:09 +00:00
for (const Handler of analyticsHandlers) {
// Catch any error while loading to avoid skipping analytics in case
// of multiple scripts.
try {
handlers.push(new Handler(handlerConstructorOptions));
} catch (error) {
logger.warn(`Error creating analytics handler: ${error}`);
}
}
2017-10-04 22:36:09 +00:00
logger.debug(`Loaded ${handlers.length} analytics handlers`);
return handlers;
});
}