소스 검색

ref(Amplitude): device id syncing

master
Hristo Terezov 5 년 전
부모
커밋
b64260e554

+ 82
- 54
react/features/analytics/functions.js 파일 보기

35
 }
35
 }
36
 
36
 
37
 /**
37
 /**
38
- * Loads the analytics scripts and inits JitsiMeetJS.analytics by setting
39
- * permanent properties and setting the handlers from the loaded scripts.
40
- * NOTE: Has to be used after JitsiMeetJS.init. Otherwise analytics will be
41
- * null.
38
+ * Creates the analytics handlers.
42
  *
39
  *
43
- * @param {Store} store - The redux store in which the specified {@code action}
44
- * is being dispatched.
45
- * @returns {void}
40
+ * @param {Store} store - The redux store in which the specified {@code action} is being dispatched.
41
+ * @returns {Promise} Resolves with the handlers that have been successfully loaded.
46
  */
42
  */
47
-export function initAnalytics({ getState }: { getState: Function }) {
43
+export function createHandlers({ getState }: { getState: Function }) {
48
     getJitsiMeetGlobalNS().analyticsHandlers = [];
44
     getJitsiMeetGlobalNS().analyticsHandlers = [];
49
     window.analyticsHandlers = []; // Legacy support.
45
     window.analyticsHandlers = []; // Legacy support.
50
 
46
 
51
-    if (!analytics || !isAnalyticsEnabled(getState)) {
52
-        return;
47
+    if (!isAnalyticsEnabled(getState)) {
48
+        return Promise.resolve([]);
53
     }
49
     }
54
 
50
 
55
     const state = getState();
51
     const state = getState();
56
     const config = state['features/base/config'];
52
     const config = state['features/base/config'];
57
     const { locationURL } = state['features/base/connection'];
53
     const { locationURL } = state['features/base/connection'];
58
     const host = locationURL ? locationURL.host : '';
54
     const host = locationURL ? locationURL.host : '';
59
-
60
     const {
55
     const {
61
         analytics: analyticsConfig = {},
56
         analytics: analyticsConfig = {},
62
         deploymentInfo
57
         deploymentInfo
68
         googleAnalyticsTrackingId,
63
         googleAnalyticsTrackingId,
69
         whiteListedEvents
64
         whiteListedEvents
70
     } = analyticsConfig;
65
     } = analyticsConfig;
71
-    const { group, server, user } = state['features/base/jwt'];
66
+    const { group, user } = state['features/base/jwt'];
72
     const handlerConstructorOptions = {
67
     const handlerConstructorOptions = {
73
         amplitudeAPPKey,
68
         amplitudeAPPKey,
74
         blackListedEvents,
69
         blackListedEvents,
82
         version: JitsiMeetJS.version,
77
         version: JitsiMeetJS.version,
83
         whiteListedEvents
78
         whiteListedEvents
84
     };
79
     };
80
+    const handlers = [];
85
 
81
 
86
-    _loadHandlers(scriptURLs, handlerConstructorOptions)
87
-        .then(handlers => {
88
-            const roomName = state['features/base/conference'].room;
89
-            const permanentProperties = {};
82
+    try {
83
+        const amplitude = new AmplitudeHandler(handlerConstructorOptions);
84
+
85
+        handlers.push(amplitude);
86
+    // eslint-disable-next-line no-empty
87
+    } catch (e) {}
88
+
89
+    return (
90
+        _loadHandlers(scriptURLs, handlerConstructorOptions)
91
+            .then(externalHandlers => {
92
+                handlers.push(...externalHandlers);
93
+                if (handlers.length === 0) {
94
+                    // Throwing an error in  order to dispose the analytics in the catch clause due to the lack of any
95
+                    // analytics handlers.
96
+                    throw new Error('No analytics handlers created!');
97
+                }
90
 
98
 
91
-            if (server) {
92
-                permanentProperties.server = server;
93
-            }
94
-            if (group) {
95
-                permanentProperties.group = group;
96
-            }
99
+                return handlers;
100
+            })
101
+            .catch(e => {
102
+                analytics.dispose();
103
+                logger.error(e);
97
 
104
 
98
-            //  Report if user is using websocket
99
-            permanentProperties.websocket = navigator.product !== 'ReactNative' && typeof config.websocket === 'string';
105
+                return [];
106
+            }));
100
 
107
 
101
-            // Optionally, include local deployment information based on the
102
-            // contents of window.config.deploymentInfo.
103
-            if (deploymentInfo) {
104
-                for (const key in deploymentInfo) {
105
-                    if (deploymentInfo.hasOwnProperty(key)) {
106
-                        permanentProperties[key] = deploymentInfo[key];
107
-                    }
108
-                }
108
+}
109
+
110
+/**
111
+ * Inits JitsiMeetJS.analytics by setting permanent properties and setting the handlers from the loaded scripts.
112
+ * NOTE: Has to be used after JitsiMeetJS.init. Otherwise analytics will be null.
113
+ *
114
+ * @param {Store} store - The redux store in which the specified {@code action} is being dispatched.
115
+ * @param {Array<Object>} handlers - The analytics handlers.
116
+ * @returns {void}
117
+ */
118
+export function initAnalytics({ getState }: { getState: Function }, handlers: Array<Object>) {
119
+    if (!isAnalyticsEnabled(getState) || handlers.length === 0) {
120
+        return;
121
+    }
122
+
123
+    const state = getState();
124
+    const config = state['features/base/config'];
125
+    const {
126
+        deploymentInfo
127
+    } = config;
128
+    const { group, server } = state['features/base/jwt'];
129
+    const roomName = state['features/base/conference'].room;
130
+    const permanentProperties = {};
131
+
132
+    if (server) {
133
+        permanentProperties.server = server;
134
+    }
135
+    if (group) {
136
+        permanentProperties.group = group;
137
+    }
138
+
139
+    //  Report if user is using websocket
140
+    permanentProperties.websocket = navigator.product !== 'ReactNative' && typeof config.websocket === 'string';
141
+
142
+    // Optionally, include local deployment information based on the
143
+    // contents of window.config.deploymentInfo.
144
+    if (deploymentInfo) {
145
+        for (const key in deploymentInfo) {
146
+            if (deploymentInfo.hasOwnProperty(key)) {
147
+                permanentProperties[key] = deploymentInfo[key];
109
             }
148
             }
149
+        }
150
+    }
110
 
151
 
111
-            analytics.addPermanentProperties(permanentProperties);
112
-            analytics.setConferenceName(roomName);
152
+    analytics.addPermanentProperties(permanentProperties);
153
+    analytics.setConferenceName(roomName);
113
 
154
 
114
-            // Set the handlers last, since this triggers emptying of the cache
115
-            analytics.setAnalyticsHandlers(handlers);
116
-        })
117
-        .catch(error => {
118
-            analytics.dispose();
119
-            logger.error(error);
120
-        });
155
+    // Set the handlers last, since this triggers emptying of the cache
156
+    analytics.setAnalyticsHandlers(handlers);
121
 }
157
 }
122
 
158
 
123
 /**
159
 /**
124
- * Tries to load the scripts for the analytics handlers.
160
+ * Tries to load the scripts for the analytics handlers and creates them.
125
  *
161
  *
126
  * @param {Array} scriptURLs - The array of script urls to load.
162
  * @param {Array} scriptURLs - The array of script urls to load.
127
- * @param {Object} handlerConstructorOptions - The default options to pass when
128
- * creating handlers.
163
+ * @param {Object} handlerConstructorOptions - The default options to pass when creating handlers.
129
  * @private
164
  * @private
130
- * @returns {Promise} Resolves with the handlers that have been
131
- * successfully loaded and rejects if there are no handlers loaded or the
132
- * analytics is disabled.
165
+ * @returns {Promise} Resolves with the handlers that have been successfully loaded and rejects if there are no handlers
166
+ * loaded or the analytics is disabled.
133
  */
167
  */
134
 function _loadHandlers(scriptURLs = [], handlerConstructorOptions) {
168
 function _loadHandlers(scriptURLs = [], handlerConstructorOptions) {
135
     const promises = [];
169
     const promises = [];
161
         // check the old location to provide legacy support
195
         // check the old location to provide legacy support
162
         const analyticsHandlers = [
196
         const analyticsHandlers = [
163
             ...getJitsiMeetGlobalNS().analyticsHandlers,
197
             ...getJitsiMeetGlobalNS().analyticsHandlers,
164
-            ...window.analyticsHandlers,
165
-
166
-            // NOTE: when we add second handler it will be good to put all
167
-            // build-in handlers in an array and destruct it here.
168
-            AmplitudeHandler
198
+            ...window.analyticsHandlers
169
         ];
199
         ];
170
-
171
         const handlers = [];
200
         const handlers = [];
172
 
201
 
173
         for (const Handler of analyticsHandlers) {
202
         for (const Handler of analyticsHandlers) {
179
                 logger.warn(`Error creating analytics handler: ${error}`);
208
                 logger.warn(`Error creating analytics handler: ${error}`);
180
             }
209
             }
181
         }
210
         }
182
-
183
         logger.debug(`Loaded ${handlers.length} analytics handlers`);
211
         logger.debug(`Loaded ${handlers.length} analytics handlers`);
184
 
212
 
185
         return handlers;
213
         return handlers;

+ 2
- 2
react/features/analytics/handlers/AmplitudeHandler.js 파일 보기

1
 import AbstractHandler from './AbstractHandler';
1
 import AbstractHandler from './AbstractHandler';
2
-import { amplitude } from './amplitude';
2
+import { amplitude, fixDeviceID } from './amplitude';
3
 
3
 
4
 /**
4
 /**
5
  * Analytics handler for Amplitude.
5
  * Analytics handler for Amplitude.
28
         };
28
         };
29
 
29
 
30
         amplitude.getInstance(this._amplitudeOptions).init(amplitudeAPPKey, undefined, { includeReferrer: true });
30
         amplitude.getInstance(this._amplitudeOptions).init(amplitudeAPPKey, undefined, { includeReferrer: true });
31
-        amplitude.fixDeviceID(this._amplitudeOptions);
31
+        fixDeviceID(amplitude.getInstance(this._amplitudeOptions));
32
 
32
 
33
         if (user) {
33
         if (user) {
34
             amplitude.getInstance(this._amplitudeOptions).setUserId(user);
34
             amplitude.getInstance(this._amplitudeOptions).setUserId(user);

+ 1
- 8
react/features/analytics/handlers/amplitude/Amplitude.native.js 파일 보기

111
         }
111
         }
112
 
112
 
113
         return instance;
113
         return instance;
114
-    },
115
-
116
-    /**
117
-     * Currently not implemented.
118
-     *
119
-     * @returns {void}
120
-     */
121
-    fixDeviceID() { } // eslint-disable-line no-empty-function
114
+    }
122
 };
115
 };

+ 0
- 24
react/features/analytics/handlers/amplitude/Amplitude.web.js 파일 보기

10
      */
10
      */
11
     getInstance(options = {}) {
11
     getInstance(options = {}) {
12
         return amplitude.getInstance(options.instanceName);
12
         return amplitude.getInstance(options.instanceName);
13
-    },
14
-
15
-    /**
16
-     * Sets the device id to the value of __AMDID cookie or sets the __AMDID cookie value to the current device id in
17
-     * case the __AMDID cookie is not set.
18
-     *
19
-     * @param {*} options - Optional parameters.
20
-     * @property {string} options.instanceName - The name of the AmplitudeClient instance.
21
-     * @property {string} options.host - The host from the original URL.
22
-     * @returns {void}
23
-     */
24
-    fixDeviceID(options) {
25
-        const deviceId = document.cookie.replace(/(?:(?:^|.*;\s*)__AMDID\s*=\s*([^;]*).*$)|^.*$/, '$1');
26
-        const instance = this.getInstance(options);
27
-
28
-        if (deviceId === '') {
29
-            const { host = '' } = options;
30
-
31
-            document.cookie
32
-                = `__AMDID=${instance.options.deviceId};max-age=630720000${
33
-                    host === '' ? '' : `;domain=.${host}`}`; // max-age=10 years
34
-        } else {
35
-            instance.setDeviceId(deviceId);
36
-        }
37
     }
13
     }
38
 };
14
 };

+ 9
- 0
react/features/analytics/handlers/amplitude/fixDeviceID.native.js 파일 보기

1
+/**
2
+ * Custom logic for setting the correct device id.
3
+ *
4
+ * @param {AmplitudeClient} amplitude - The amplitude instance.
5
+ * @returns {void}
6
+ */
7
+export function fixDeviceID(amplitude) { // eslint-disable-line no-unused-vars
8
+
9
+}

+ 9
- 0
react/features/analytics/handlers/amplitude/fixDeviceID.web.js 파일 보기

1
+/**
2
+ * Custom logic for setting the correct device id.
3
+ *
4
+ * @param {AmplitudeClient} amplitude - The amplitude instance.
5
+ * @returns {void}
6
+ */
7
+export function fixDeviceID(amplitude) { // eslint-disable-line no-unused-vars
8
+
9
+}

+ 1
- 0
react/features/analytics/handlers/amplitude/index.js 파일 보기

1
 export { default as amplitude } from './Amplitude';
1
 export { default as amplitude } from './Amplitude';
2
+export * from './fixDeviceID';

+ 17
- 6
react/features/analytics/middleware.js 파일 보기

18
 
18
 
19
 import { UPDATE_LOCAL_TRACKS_DURATION } from './actionTypes';
19
 import { UPDATE_LOCAL_TRACKS_DURATION } from './actionTypes';
20
 import { createLocalTracksDurationEvent, createNetworkInfoEvent } from './AnalyticsEvents';
20
 import { createLocalTracksDurationEvent, createNetworkInfoEvent } from './AnalyticsEvents';
21
-import { initAnalytics, resetAnalytics, sendAnalytics } from './functions';
21
+import { createHandlers, initAnalytics, resetAnalytics, sendAnalytics } from './functions';
22
 
22
 
23
 /**
23
 /**
24
  * Calculates the duration of the local tracks.
24
  * Calculates the duration of the local tracks.
79
  * @returns {Function}
79
  * @returns {Function}
80
  */
80
  */
81
 MiddlewareRegistry.register(store => next => action => {
81
 MiddlewareRegistry.register(store => next => action => {
82
-    if (action.type === SET_CONFIG) {
82
+    switch (action.type) {
83
+    case SET_CONFIG:
83
         if (navigator.product === 'ReactNative') {
84
         if (navigator.product === 'ReactNative') {
84
             // Reseting the analytics is currently not needed for web because
85
             // Reseting the analytics is currently not needed for web because
85
             // the user will be redirected to another page and new instance of
86
             // the user will be redirected to another page and new instance of
86
             // Analytics will be created and initialized.
87
             // Analytics will be created and initialized.
87
             resetAnalytics();
88
             resetAnalytics();
88
         }
89
         }
90
+        break;
91
+    case SET_ROOM: {
92
+        // createHandlers is called before the SET_ROOM action is executed in order for Amplitude to initialize before
93
+        // the deeplinking logic is executed (after the SET_ROOM action) so that the Amplitude device id is available
94
+        // if needed.
95
+        const createHandlersPromise = createHandlers(store);
96
+        const result = next(action);
97
+
98
+        createHandlersPromise.then(handlers => {
99
+            initAnalytics(store, handlers);
100
+        });
101
+
102
+        return result;
103
+    }
89
     }
104
     }
90
 
105
 
91
     const result = next(action);
106
     const result = next(action);
136
                 networkType: action.networkType
151
                 networkType: action.networkType
137
             }));
152
             }));
138
         break;
153
         break;
139
-    case SET_ROOM: {
140
-        initAnalytics(store);
141
-        break;
142
-    }
143
     case TRACK_ADDED:
154
     case TRACK_ADDED:
144
     case TRACK_REMOVED:
155
     case TRACK_REMOVED:
145
     case TRACK_UPDATED: {
156
     case TRACK_UPDATED: {

Loading…
취소
저장