浏览代码

chrome-banner: add analytics

master
horymury 5 年前
父节点
当前提交
202abf2a9a
没有帐户链接到提交者的电子邮件

+ 17
- 0
react/features/analytics/AnalyticsEvents.js 查看文件

@@ -184,6 +184,23 @@ export function createRecentClickedEvent(eventName, attributes = {}) {
184 184
     };
185 185
 }
186 186
 
187
+/**
188
+ * Creates an event which indicate an action occured in the chrome extension banner.
189
+ *
190
+ * @param {boolean} installPressed - Whether the user pressed install or `x` - cancel.
191
+ * @param {Object} attributes - Attributes to attach to the event.
192
+ * @returns {Object} The event in a format suitable for sending via
193
+ * sendAnalytics.
194
+ */
195
+export function createChromeExtensionBannerEvent(installPressed, attributes = {}) {
196
+    return {
197
+        action: installPressed ? 'install' : 'cancel',
198
+        attributes,
199
+        source: 'chrome.extension.banner',
200
+        type: TYPE_UI
201
+    };
202
+}
203
+
187 204
 /**
188 205
  * Creates an event which indicates that the recent list container is shown and
189 206
  * selected.

+ 17
- 1
react/features/analytics/functions.js 查看文件

@@ -2,10 +2,14 @@
2 2
 
3 3
 import JitsiMeetJS, {
4 4
     analytics,
5
+    browser,
5 6
     isAnalyticsEnabled
6 7
 } from '../base/lib-jitsi-meet';
7 8
 import { getJitsiMeetGlobalNS, loadScript } from '../base/util';
8
-
9
+import {
10
+    checkChromeExtensionsInstalled,
11
+    isMobileBrowser
12
+} from '../base/environment/utils';
9 13
 import { AmplitudeHandler } from './handlers';
10 14
 import logger from './logger';
11 15
 
@@ -154,6 +158,18 @@ export function initAnalytics({ getState }: { getState: Function }, handlers: Ar
154 158
 
155 159
     // Set the handlers last, since this triggers emptying of the cache
156 160
     analytics.setAnalyticsHandlers(handlers);
161
+
162
+    if (!isMobileBrowser() && browser.isChrome()) {
163
+        const bannerCfg = state['features/base/config'].chromeExtensionBanner;
164
+
165
+        checkChromeExtensionsInstalled(bannerCfg).then(extensionsInstalled => {
166
+            if (extensionsInstalled?.length) {
167
+                analytics.addPermanentProperties({
168
+                    hasChromeExtension: extensionsInstalled.some(ext => ext)
169
+                });
170
+            }
171
+        });
172
+    }
157 173
 }
158 174
 
159 175
 /**

+ 28
- 0
react/features/base/environment/utils.js 查看文件

@@ -1,3 +1,5 @@
1
+// @flow
2
+
1 3
 import Platform from '../react/Platform';
2 4
 
3 5
 /**
@@ -8,3 +10,29 @@ import Platform from '../react/Platform';
8 10
 export function isMobileBrowser() {
9 11
     return Platform.OS === 'android' || Platform.OS === 'ios';
10 12
 }
13
+
14
+/**
15
+ * Checks whether the chrome extensions defined in the config file are installed or not.
16
+ *
17
+ * @param {Object} config - Objects containing info about the configured extensions.
18
+ *
19
+ * @returns {Promise[]}
20
+ */
21
+export function checkChromeExtensionsInstalled(config: Object = {}) {
22
+    const isExtensionInstalled = info => new Promise(resolve => {
23
+        const img = new Image();
24
+
25
+        img.src = `chrome-extension://${info.id}/${info.path}`;
26
+        img.onload = function() {
27
+            resolve(true);
28
+        };
29
+        img.onerror = function() {
30
+            resolve(false);
31
+        };
32
+    });
33
+    const extensionInstalledFunction = info => isExtensionInstalled(info);
34
+
35
+    return Promise.all(
36
+        (config.chromeExtensionsInfo || []).map(info => extensionInstalledFunction(info))
37
+    );
38
+}

+ 23
- 46
react/features/chrome-extension-banner/components/ChromeExtensionBanner.web.js 查看文件

@@ -5,8 +5,15 @@ import { Icon, IconClose } from '../../base/icons';
5 5
 import { translate } from '../../base/i18n';
6 6
 import { getCurrentConference } from '../../base/conference/functions';
7 7
 import { browser } from '../../base/lib-jitsi-meet';
8
-import { isMobileBrowser } from '../../base/environment/utils';
8
+import {
9
+    checkChromeExtensionsInstalled,
10
+    isMobileBrowser
11
+} from '../../base/environment/utils';
9 12
 import logger from '../logger';
13
+import {
14
+    createChromeExtensionBannerEvent,
15
+    sendAnalytics
16
+} from '../../analytics';
10 17
 
11 18
 declare var interfaceConfig: Object;
12 19
 
@@ -23,19 +30,14 @@ const DONT_SHOW_AGAIN_CHECKED = 'hide_chrome_extension_banner';
23 30
 type Props = {
24 31
 
25 32
     /**
26
-     * Conference data, if any
27
-     */
28
-    conference: Object,
29
-
30
-    /**
31
-     * The url of the chrome extension
33
+     * Contains info about installed/to be installed chrome extension(s).
32 34
      */
33
-    chromeExtensionUrl: string,
35
+    bannerCfg: Object,
34 36
 
35 37
     /**
36
-     * An array containing info for identifying a chrome extension
38
+     * Conference data, if any
37 39
      */
38
-    chromeExtensionsInfo: Array<Object>,
40
+    conference: Object,
39 41
 
40 42
     /**
41 43
      * Whether I am the current recorder.
@@ -91,7 +93,6 @@ class ChromeExtensionBanner extends PureComponent<Props, State> {
91 93
 
92 94
         this._onClosePressed = this._onClosePressed.bind(this);
93 95
         this._onInstallExtensionClick = this._onInstallExtensionClick.bind(this);
94
-        this._checkExtensionsInstalled = this._checkExtensionsInstalled.bind(this);
95 96
         this._shouldNotRender = this._shouldNotRender.bind(this);
96 97
         this._onDontShowAgainChange = this._onDontShowAgainChange.bind(this);
97 98
     }
@@ -107,15 +108,18 @@ class ChromeExtensionBanner extends PureComponent<Props, State> {
107 108
             return;
108 109
         }
109 110
 
110
-        if (this.props.chromeExtensionUrl && !prevProps.chromeExtensionUrl) {
111
+        const { bannerCfg } = this.props;
112
+        const prevBannerCfg = prevProps.bannerCfg;
113
+
114
+        if (bannerCfg.url && !prevBannerCfg.url) {
111 115
             logger.info('Chrome extension URL found.');
112 116
         }
113 117
 
114
-        if (this.props.chromeExtensionsInfo.length && !prevProps.chromeExtensionsInfo.length) {
118
+        if ((bannerCfg.chromeExtensionsInfo || []).length && !(prevBannerCfg.chromeExtensionsInfo || []).length) {
115 119
             logger.info('Chrome extension(s) info found.');
116 120
         }
117 121
 
118
-        const hasExtensions = await this._checkExtensionsInstalled();
122
+        const hasExtensions = await checkChromeExtensionsInstalled(this.props.bannerCfg);
119 123
 
120 124
         if (
121 125
             hasExtensions
@@ -147,6 +151,7 @@ class ChromeExtensionBanner extends PureComponent<Props, State> {
147 151
      * @returns {void}
148 152
      */
149 153
     _onClosePressed() {
154
+        sendAnalytics(createChromeExtensionBannerEvent(false));
150 155
         this.setState({ closePressed: true });
151 156
     }
152 157
 
@@ -158,36 +163,11 @@ class ChromeExtensionBanner extends PureComponent<Props, State> {
158 163
      * @returns {void}
159 164
      */
160 165
     _onInstallExtensionClick() {
161
-        window.open(this.props.chromeExtensionUrl);
166
+        sendAnalytics(createChromeExtensionBannerEvent(true));
167
+        window.open(this.props.bannerCfg.url);
162 168
         this.setState({ closePressed: true });
163 169
     }
164 170
 
165
-    _checkExtensionsInstalled: () => Promise<*>;
166
-
167
-    /**
168
-     * Checks whether the chrome extensions defined in the config file are installed or not.
169
-     *
170
-     * @returns {Promise[]}
171
-     */
172
-    _checkExtensionsInstalled() {
173
-        const isExtensionInstalled = info => new Promise(resolve => {
174
-            const img = new Image();
175
-
176
-            img.src = `chrome-extension://${info.id}/${info.path}`;
177
-            img.onload = function() {
178
-                resolve(true);
179
-            };
180
-            img.onerror = function() {
181
-                resolve(false);
182
-            };
183
-        });
184
-        const extensionInstalledFunction = info => isExtensionInstalled(info);
185
-
186
-        return Promise.all(
187
-            this.props.chromeExtensionsInfo.map(info => extensionInstalledFunction(info))
188
-        );
189
-    }
190
-
191 171
     _shouldNotRender: () => boolean;
192 172
 
193 173
     /**
@@ -202,7 +182,7 @@ class ChromeExtensionBanner extends PureComponent<Props, State> {
202 182
 
203 183
         const dontShowAgain = localStorage.getItem(DONT_SHOW_AGAIN_CHECKED) === 'true';
204 184
 
205
-        return !this.props.chromeExtensionUrl
185
+        return !this.props.bannerCfg.url
206 186
             || dontShowAgain
207 187
             || this.state.closePressed
208 188
             || !this.state.shouldShow
@@ -290,11 +270,8 @@ class ChromeExtensionBanner extends PureComponent<Props, State> {
290 270
  * @returns {Object}
291 271
  */
292 272
 const _mapStateToProps = state => {
293
-    const bannerCfg = state['features/base/config'].chromeExtensionBanner || {};
294
-
295 273
     return {
296
-        chromeExtensionUrl: bannerCfg.url,
297
-        chromeExtensionsInfo: bannerCfg.chromeExtensionsInfo || [],
274
+        bannerCfg: state['features/base/config'].chromeExtensionBanner || {},
298 275
         conference: getCurrentConference(state),
299 276
         iAmRecorder: state['features/base/config'].iAmRecorder
300 277
     };

正在加载...
取消
保存