ref(Amplitude): device id syncing
This commit is contained in:
parent
7d67cb583e
commit
b64260e554
|
@ -35,28 +35,23 @@ export function resetAnalytics() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the analytics scripts and inits JitsiMeetJS.analytics by setting
|
* Creates the analytics handlers.
|
||||||
* 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}
|
* @param {Store} store - The redux store in which the specified {@code action} is being dispatched.
|
||||||
* is being dispatched.
|
* @returns {Promise} Resolves with the handlers that have been successfully loaded.
|
||||||
* @returns {void}
|
|
||||||
*/
|
*/
|
||||||
export function initAnalytics({ getState }: { getState: Function }) {
|
export function createHandlers({ getState }: { getState: Function }) {
|
||||||
getJitsiMeetGlobalNS().analyticsHandlers = [];
|
getJitsiMeetGlobalNS().analyticsHandlers = [];
|
||||||
window.analyticsHandlers = []; // Legacy support.
|
window.analyticsHandlers = []; // Legacy support.
|
||||||
|
|
||||||
if (!analytics || !isAnalyticsEnabled(getState)) {
|
if (!isAnalyticsEnabled(getState)) {
|
||||||
return;
|
return Promise.resolve([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const config = state['features/base/config'];
|
const config = state['features/base/config'];
|
||||||
const { locationURL } = state['features/base/connection'];
|
const { locationURL } = state['features/base/connection'];
|
||||||
const host = locationURL ? locationURL.host : '';
|
const host = locationURL ? locationURL.host : '';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
analytics: analyticsConfig = {},
|
analytics: analyticsConfig = {},
|
||||||
deploymentInfo
|
deploymentInfo
|
||||||
|
@ -68,7 +63,7 @@ export function initAnalytics({ getState }: { getState: Function }) {
|
||||||
googleAnalyticsTrackingId,
|
googleAnalyticsTrackingId,
|
||||||
whiteListedEvents
|
whiteListedEvents
|
||||||
} = analyticsConfig;
|
} = analyticsConfig;
|
||||||
const { group, server, user } = state['features/base/jwt'];
|
const { group, user } = state['features/base/jwt'];
|
||||||
const handlerConstructorOptions = {
|
const handlerConstructorOptions = {
|
||||||
amplitudeAPPKey,
|
amplitudeAPPKey,
|
||||||
blackListedEvents,
|
blackListedEvents,
|
||||||
|
@ -82,54 +77,93 @@ export function initAnalytics({ getState }: { getState: Function }) {
|
||||||
version: JitsiMeetJS.version,
|
version: JitsiMeetJS.version,
|
||||||
whiteListedEvents
|
whiteListedEvents
|
||||||
};
|
};
|
||||||
|
const handlers = [];
|
||||||
|
|
||||||
_loadHandlers(scriptURLs, handlerConstructorOptions)
|
try {
|
||||||
.then(handlers => {
|
const amplitude = new AmplitudeHandler(handlerConstructorOptions);
|
||||||
const roomName = state['features/base/conference'].room;
|
|
||||||
const permanentProperties = {};
|
|
||||||
|
|
||||||
if (server) {
|
handlers.push(amplitude);
|
||||||
permanentProperties.server = server;
|
// eslint-disable-next-line no-empty
|
||||||
}
|
} catch (e) {}
|
||||||
if (group) {
|
|
||||||
permanentProperties.group = group;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Report if user is using websocket
|
return (
|
||||||
permanentProperties.websocket = navigator.product !== 'ReactNative' && typeof config.websocket === 'string';
|
_loadHandlers(scriptURLs, handlerConstructorOptions)
|
||||||
|
.then(externalHandlers => {
|
||||||
// Optionally, include local deployment information based on the
|
handlers.push(...externalHandlers);
|
||||||
// contents of window.config.deploymentInfo.
|
if (handlers.length === 0) {
|
||||||
if (deploymentInfo) {
|
// Throwing an error in order to dispose the analytics in the catch clause due to the lack of any
|
||||||
for (const key in deploymentInfo) {
|
// analytics handlers.
|
||||||
if (deploymentInfo.hasOwnProperty(key)) {
|
throw new Error('No analytics handlers created!');
|
||||||
permanentProperties[key] = deploymentInfo[key];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
analytics.addPermanentProperties(permanentProperties);
|
return handlers;
|
||||||
analytics.setConferenceName(roomName);
|
})
|
||||||
|
.catch(e => {
|
||||||
|
analytics.dispose();
|
||||||
|
logger.error(e);
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}));
|
||||||
|
|
||||||
// Set the handlers last, since this triggers emptying of the cache
|
|
||||||
analytics.setAnalyticsHandlers(handlers);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
analytics.dispose();
|
|
||||||
logger.error(error);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to load the scripts for the analytics handlers.
|
* 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';
|
||||||
|
|
||||||
|
// 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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
analytics.addPermanentProperties(permanentProperties);
|
||||||
|
analytics.setConferenceName(roomName);
|
||||||
|
|
||||||
|
// Set the handlers last, since this triggers emptying of the cache
|
||||||
|
analytics.setAnalyticsHandlers(handlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to load the scripts for the analytics handlers and creates them.
|
||||||
*
|
*
|
||||||
* @param {Array} scriptURLs - The array of script urls to load.
|
* @param {Array} scriptURLs - The array of script urls to load.
|
||||||
* @param {Object} handlerConstructorOptions - The default options to pass when
|
* @param {Object} handlerConstructorOptions - The default options to pass when creating handlers.
|
||||||
* creating handlers.
|
|
||||||
* @private
|
* @private
|
||||||
* @returns {Promise} Resolves with the handlers that have been
|
* @returns {Promise} Resolves with the handlers that have been successfully loaded and rejects if there are no handlers
|
||||||
* successfully loaded and rejects if there are no handlers loaded or the
|
* loaded or the analytics is disabled.
|
||||||
* analytics is disabled.
|
|
||||||
*/
|
*/
|
||||||
function _loadHandlers(scriptURLs = [], handlerConstructorOptions) {
|
function _loadHandlers(scriptURLs = [], handlerConstructorOptions) {
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
@ -161,13 +195,8 @@ function _loadHandlers(scriptURLs = [], handlerConstructorOptions) {
|
||||||
// check the old location to provide legacy support
|
// check the old location to provide legacy support
|
||||||
const analyticsHandlers = [
|
const analyticsHandlers = [
|
||||||
...getJitsiMeetGlobalNS().analyticsHandlers,
|
...getJitsiMeetGlobalNS().analyticsHandlers,
|
||||||
...window.analyticsHandlers,
|
...window.analyticsHandlers
|
||||||
|
|
||||||
// NOTE: when we add second handler it will be good to put all
|
|
||||||
// build-in handlers in an array and destruct it here.
|
|
||||||
AmplitudeHandler
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const handlers = [];
|
const handlers = [];
|
||||||
|
|
||||||
for (const Handler of analyticsHandlers) {
|
for (const Handler of analyticsHandlers) {
|
||||||
|
@ -179,7 +208,6 @@ function _loadHandlers(scriptURLs = [], handlerConstructorOptions) {
|
||||||
logger.warn(`Error creating analytics handler: ${error}`);
|
logger.warn(`Error creating analytics handler: ${error}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(`Loaded ${handlers.length} analytics handlers`);
|
logger.debug(`Loaded ${handlers.length} analytics handlers`);
|
||||||
|
|
||||||
return handlers;
|
return handlers;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import AbstractHandler from './AbstractHandler';
|
import AbstractHandler from './AbstractHandler';
|
||||||
import { amplitude } from './amplitude';
|
import { amplitude, fixDeviceID } from './amplitude';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Analytics handler for Amplitude.
|
* Analytics handler for Amplitude.
|
||||||
|
@ -28,7 +28,7 @@ export default class AmplitudeHandler extends AbstractHandler {
|
||||||
};
|
};
|
||||||
|
|
||||||
amplitude.getInstance(this._amplitudeOptions).init(amplitudeAPPKey, undefined, { includeReferrer: true });
|
amplitude.getInstance(this._amplitudeOptions).init(amplitudeAPPKey, undefined, { includeReferrer: true });
|
||||||
amplitude.fixDeviceID(this._amplitudeOptions);
|
fixDeviceID(amplitude.getInstance(this._amplitudeOptions));
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
amplitude.getInstance(this._amplitudeOptions).setUserId(user);
|
amplitude.getInstance(this._amplitudeOptions).setUserId(user);
|
||||||
|
|
|
@ -111,12 +111,5 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Currently not implemented.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
fixDeviceID() { } // eslint-disable-line no-empty-function
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,29 +10,5 @@ export default {
|
||||||
*/
|
*/
|
||||||
getInstance(options = {}) {
|
getInstance(options = {}) {
|
||||||
return amplitude.getInstance(options.instanceName);
|
return amplitude.getInstance(options.instanceName);
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the device id to the value of __AMDID cookie or sets the __AMDID cookie value to the current device id in
|
|
||||||
* case the __AMDID cookie is not set.
|
|
||||||
*
|
|
||||||
* @param {*} options - Optional parameters.
|
|
||||||
* @property {string} options.instanceName - The name of the AmplitudeClient instance.
|
|
||||||
* @property {string} options.host - The host from the original URL.
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
fixDeviceID(options) {
|
|
||||||
const deviceId = document.cookie.replace(/(?:(?:^|.*;\s*)__AMDID\s*=\s*([^;]*).*$)|^.*$/, '$1');
|
|
||||||
const instance = this.getInstance(options);
|
|
||||||
|
|
||||||
if (deviceId === '') {
|
|
||||||
const { host = '' } = options;
|
|
||||||
|
|
||||||
document.cookie
|
|
||||||
= `__AMDID=${instance.options.deviceId};max-age=630720000${
|
|
||||||
host === '' ? '' : `;domain=.${host}`}`; // max-age=10 years
|
|
||||||
} else {
|
|
||||||
instance.setDeviceId(deviceId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
/**
|
||||||
|
* Custom logic for setting the correct device id.
|
||||||
|
*
|
||||||
|
* @param {AmplitudeClient} amplitude - The amplitude instance.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function fixDeviceID(amplitude) { // eslint-disable-line no-unused-vars
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
/**
|
||||||
|
* Custom logic for setting the correct device id.
|
||||||
|
*
|
||||||
|
* @param {AmplitudeClient} amplitude - The amplitude instance.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function fixDeviceID(amplitude) { // eslint-disable-line no-unused-vars
|
||||||
|
|
||||||
|
}
|
|
@ -1 +1,2 @@
|
||||||
export { default as amplitude } from './Amplitude';
|
export { default as amplitude } from './Amplitude';
|
||||||
|
export * from './fixDeviceID';
|
||||||
|
|
|
@ -18,7 +18,7 @@ import {
|
||||||
|
|
||||||
import { UPDATE_LOCAL_TRACKS_DURATION } from './actionTypes';
|
import { UPDATE_LOCAL_TRACKS_DURATION } from './actionTypes';
|
||||||
import { createLocalTracksDurationEvent, createNetworkInfoEvent } from './AnalyticsEvents';
|
import { createLocalTracksDurationEvent, createNetworkInfoEvent } from './AnalyticsEvents';
|
||||||
import { initAnalytics, resetAnalytics, sendAnalytics } from './functions';
|
import { createHandlers, initAnalytics, resetAnalytics, sendAnalytics } from './functions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the duration of the local tracks.
|
* Calculates the duration of the local tracks.
|
||||||
|
@ -79,13 +79,28 @@ function calculateLocalTrackDuration(state) {
|
||||||
* @returns {Function}
|
* @returns {Function}
|
||||||
*/
|
*/
|
||||||
MiddlewareRegistry.register(store => next => action => {
|
MiddlewareRegistry.register(store => next => action => {
|
||||||
if (action.type === SET_CONFIG) {
|
switch (action.type) {
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
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);
|
const result = next(action);
|
||||||
|
@ -136,10 +151,6 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
networkType: action.networkType
|
networkType: action.networkType
|
||||||
}));
|
}));
|
||||||
break;
|
break;
|
||||||
case SET_ROOM: {
|
|
||||||
initAnalytics(store);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case TRACK_ADDED:
|
case TRACK_ADDED:
|
||||||
case TRACK_REMOVED:
|
case TRACK_REMOVED:
|
||||||
case TRACK_UPDATED: {
|
case TRACK_UPDATED: {
|
||||||
|
|
Loading…
Reference in New Issue