Browse Source

feat(amplitude): add mobile implementation

j8
Hristo Terezov 6 years ago
parent
commit
59fc3642a6

+ 0
- 2
Makefile View File

45
 		$(OUTPUT_DIR)/analytics-ga.js \
45
 		$(OUTPUT_DIR)/analytics-ga.js \
46
 		$(BUILD_DIR)/analytics-ga.min.js \
46
 		$(BUILD_DIR)/analytics-ga.min.js \
47
 		$(BUILD_DIR)/analytics-ga.min.map \
47
 		$(BUILD_DIR)/analytics-ga.min.map \
48
-		$(BUILD_DIR)/analytics-amplitude.min.js \
49
-		$(BUILD_DIR)/analytics-amplitude.min.map \
50
 		$(DEPLOY_DIR)
48
 		$(DEPLOY_DIR)
51
 
49
 
52
 deploy-lib-jitsi-meet:
50
 deploy-lib-jitsi-meet:

+ 1
- 0
android/sdk/build.gradle View File

25
     implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
25
     implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
26
 
26
 
27
     implementation 'org.webkit:android-jsc:+'
27
     implementation 'org.webkit:android-jsc:+'
28
+    implementation 'com.amplitude:android-sdk:2.14.1'
28
     implementation 'com.dropbox.core:dropbox-core-sdk:3.0.8'
29
     implementation 'com.dropbox.core:dropbox-core-sdk:3.0.8'
29
     api 'com.facebook.react:react-native:+'
30
     api 'com.facebook.react:react-native:+'
30
 
31
 

+ 1
- 0
android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java View File

53
                 new PictureInPictureModule(reactContext),
53
                 new PictureInPictureModule(reactContext),
54
                 new ProximityModule(reactContext),
54
                 new ProximityModule(reactContext),
55
                 new WiFiStatsModule(reactContext),
55
                 new WiFiStatsModule(reactContext),
56
+                new org.jitsi.meet.sdk.analytics.AmplitudeModule(reactContext),
56
                 new org.jitsi.meet.sdk.dropbox.Dropbox(reactContext),
57
                 new org.jitsi.meet.sdk.dropbox.Dropbox(reactContext),
57
                 new org.jitsi.meet.sdk.net.NAT64AddrInfoModule(reactContext)));
58
                 new org.jitsi.meet.sdk.net.NAT64AddrInfoModule(reactContext)));
58
 
59
 

+ 87
- 0
android/sdk/src/main/java/org/jitsi/meet/sdk/analytics/AmplitudeModule.java View File

1
+/*
2
+ * Copyright @ 2019-present 8x8, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ *     http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+package org.jitsi.meet.sdk.analytics;
18
+
19
+import com.facebook.react.bridge.ReactApplicationContext;
20
+import com.facebook.react.bridge.ReactContextBaseJavaModule;
21
+import com.facebook.react.bridge.ReactMethod;
22
+import com.facebook.react.bridge.ReadableMap;
23
+
24
+import com.amplitude.api.Amplitude;
25
+
26
+import org.json.JSONException;
27
+import org.json.JSONObject;
28
+
29
+/**
30
+ * Implements the react-native module for the Amplitude integration.
31
+ */
32
+public class AmplitudeModule
33
+        extends ReactContextBaseJavaModule {
34
+
35
+    public AmplitudeModule(ReactApplicationContext reactContext) {
36
+        super(reactContext);
37
+    }
38
+
39
+    /**
40
+     * Initializes the Amplitude SDK.
41
+     *
42
+     * @param instanceName The name of the Amplitude instance. Should
43
+     * be used only for multi-project logging.
44
+     * @param apiKey The API_KEY of the Amplitude project.
45
+     */
46
+    @ReactMethod
47
+    public void init(String instanceName, String apiKey) {
48
+        Amplitude.getInstance(instanceName).initialize(getCurrentActivity(), apiKey);
49
+    }
50
+
51
+    /**
52
+     * Sets the user properties for an Amplitude instance.
53
+     *
54
+     * @param instanceName The name of the Amplitude instance.
55
+     * @param userProps JSON string with user properties to be set.
56
+     */
57
+    @ReactMethod
58
+    public void setUserProperties(String instanceName, ReadableMap userProps) {
59
+        if (userProps != null) {
60
+            Amplitude.getInstance(instanceName).setUserProperties(
61
+                    new JSONObject(userProps.toHashMap()));
62
+        }
63
+    }
64
+
65
+    /**
66
+     * Log an analytics event.
67
+     *
68
+     * @param instanceName The name of the Amplitude instance.
69
+     * @param eventType The event type.
70
+     * @param eventPropsString JSON string with the event properties.
71
+     */
72
+    @ReactMethod
73
+    public void logEvent(String instanceName, String eventType, String eventPropsString) {
74
+        JSONObject eventProps = null;
75
+        try {
76
+            eventProps = new JSONObject(eventPropsString);
77
+            Amplitude.getInstance(instanceName).logEvent(eventType, eventProps);
78
+        } catch (JSONException e) {
79
+            e.printStackTrace();
80
+        }
81
+    }
82
+
83
+    @Override
84
+    public String getName() {
85
+        return "Amplitude";
86
+    }
87
+}

+ 1
- 0
ios/Podfile View File

35
   pod 'Folly',
35
   pod 'Folly',
36
     :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
36
     :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
37
 
37
 
38
+  pod 'Amplitude-iOS', '~> 4.0.4'
38
   pod 'ObjectiveDropboxOfficial'
39
   pod 'ObjectiveDropboxOfficial'
39
 
40
 
40
   pod 'react-native-background-timer',
41
   pod 'react-native-background-timer',

+ 6
- 2
ios/Podfile.lock View File

1
 PODS:
1
 PODS:
2
+  - Amplitude-iOS (4.0.4)
2
   - boost-for-react-native (1.63.0)
3
   - boost-for-react-native (1.63.0)
3
   - BVLinearGradient (2.5.3):
4
   - BVLinearGradient (2.5.3):
4
     - React
5
     - React
154
   - yoga (0.57.8.React)
155
   - yoga (0.57.8.React)
155
 
156
 
156
 DEPENDENCIES:
157
 DEPENDENCIES:
158
+  - Amplitude-iOS (~> 4.0.4)
157
   - BVLinearGradient (from `../node_modules/react-native-linear-gradient`)
159
   - BVLinearGradient (from `../node_modules/react-native-linear-gradient`)
158
   - Crashlytics
160
   - Crashlytics
159
   - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
161
   - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
185
 
187
 
186
 SPEC REPOS:
188
 SPEC REPOS:
187
   https://github.com/cocoapods/specs.git:
189
   https://github.com/cocoapods/specs.git:
190
+    - Amplitude-iOS
188
     - boost-for-react-native
191
     - boost-for-react-native
189
     - Crashlytics
192
     - Crashlytics
190
     - Fabric
193
     - Fabric
235
     :path: "../node_modules/react-native/ReactCommon/yoga"
238
     :path: "../node_modules/react-native/ReactCommon/yoga"
236
 
239
 
237
 SPEC CHECKSUMS:
240
 SPEC CHECKSUMS:
241
+  Amplitude-iOS: 2ad4d7270c99186236c1272a3a9425463b1ae1a7
238
   boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
242
   boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
239
   BVLinearGradient: 0d985ec461359c82bc254f26d11008bdae50d17a
243
   BVLinearGradient: 0d985ec461359c82bc254f26d11008bdae50d17a
240
   Crashlytics: 07fb167b1694128c1c9a5a5cc319b0e9c3ca0933
244
   Crashlytics: 07fb167b1694128c1c9a5a5cc319b0e9c3ca0933
268
   SDWebImage: c5594f1a19c48d526d321e548902b56b479cd508
272
   SDWebImage: c5594f1a19c48d526d321e548902b56b479cd508
269
   yoga: 74cdf036c30820443b25ade59916236b1e95ee93
273
   yoga: 74cdf036c30820443b25ade59916236b1e95ee93
270
 
274
 
271
-PODFILE CHECKSUM: 7d1909450626f31f9ea2de80122a66a50af2e1ea
275
+PODFILE CHECKSUM: b300161e95d65c24b91368803afb8873f4b873cc
272
 
276
 
273
-COCOAPODS: 1.6.0
277
+COCOAPODS: 1.6.1

+ 21
- 0
ios/sdk/sdk.xcodeproj/project.pbxproj View File

34
 		75635B0B20751D6D00F29C9F /* left.wav in Resources */ = {isa = PBXBuildFile; fileRef = 75635B0920751D6D00F29C9F /* left.wav */; };
34
 		75635B0B20751D6D00F29C9F /* left.wav in Resources */ = {isa = PBXBuildFile; fileRef = 75635B0920751D6D00F29C9F /* left.wav */; };
35
 		87FE6F3321E52437004A5DC7 /* incomingMessage.wav in Resources */ = {isa = PBXBuildFile; fileRef = 87FE6F3221E52437004A5DC7 /* incomingMessage.wav */; };
35
 		87FE6F3321E52437004A5DC7 /* incomingMessage.wav in Resources */ = {isa = PBXBuildFile; fileRef = 87FE6F3221E52437004A5DC7 /* incomingMessage.wav */; };
36
 		A4414AE020B37F1A003546E6 /* rejected.wav in Resources */ = {isa = PBXBuildFile; fileRef = A4414ADF20B37F1A003546E6 /* rejected.wav */; };
36
 		A4414AE020B37F1A003546E6 /* rejected.wav in Resources */ = {isa = PBXBuildFile; fileRef = A4414ADF20B37F1A003546E6 /* rejected.wav */; };
37
+		A480429C21EE335600289B73 /* AmplitudeModule.m in Sources */ = {isa = PBXBuildFile; fileRef = A480429B21EE335600289B73 /* AmplitudeModule.m */; };
37
 		A4A934E9212F3ADB001E9388 /* Dropbox.m in Sources */ = {isa = PBXBuildFile; fileRef = A4A934E8212F3ADB001E9388 /* Dropbox.m */; };
38
 		A4A934E9212F3ADB001E9388 /* Dropbox.m in Sources */ = {isa = PBXBuildFile; fileRef = A4A934E8212F3ADB001E9388 /* Dropbox.m */; };
38
 		C6245F5D2053091D0040BE68 /* image-resize@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C6245F5B2053091D0040BE68 /* image-resize@2x.png */; };
39
 		C6245F5D2053091D0040BE68 /* image-resize@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C6245F5B2053091D0040BE68 /* image-resize@2x.png */; };
39
 		C6245F5E2053091D0040BE68 /* image-resize@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = C6245F5C2053091D0040BE68 /* image-resize@3x.png */; };
40
 		C6245F5E2053091D0040BE68 /* image-resize@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = C6245F5C2053091D0040BE68 /* image-resize@3x.png */; };
80
 		98E09B5C73D9036B4ED252FC /* Pods-JitsiMeet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.debug.xcconfig"; sourceTree = "<group>"; };
81
 		98E09B5C73D9036B4ED252FC /* Pods-JitsiMeet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.debug.xcconfig"; sourceTree = "<group>"; };
81
 		9C77CA3CC919B081F1A52982 /* Pods-JitsiMeet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.release.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.release.xcconfig"; sourceTree = "<group>"; };
82
 		9C77CA3CC919B081F1A52982 /* Pods-JitsiMeet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.release.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.release.xcconfig"; sourceTree = "<group>"; };
82
 		A4414ADF20B37F1A003546E6 /* rejected.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = rejected.wav; path = ../../sounds/rejected.wav; sourceTree = "<group>"; };
83
 		A4414ADF20B37F1A003546E6 /* rejected.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = rejected.wav; path = ../../sounds/rejected.wav; sourceTree = "<group>"; };
84
+		A480429B21EE335600289B73 /* AmplitudeModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AmplitudeModule.m; path = src/analytics/AmplitudeModule.m; sourceTree = SOURCE_ROOT; };
83
 		A4A934E8212F3ADB001E9388 /* Dropbox.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Dropbox.m; sourceTree = "<group>"; };
85
 		A4A934E8212F3ADB001E9388 /* Dropbox.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Dropbox.m; sourceTree = "<group>"; };
84
 		A4A934EB21349A06001E9388 /* Dropbox.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Dropbox.h; sourceTree = "<group>"; };
86
 		A4A934EB21349A06001E9388 /* Dropbox.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Dropbox.h; sourceTree = "<group>"; };
85
 		C6245F5B2053091D0040BE68 /* image-resize@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "image-resize@2x.png"; path = "src/picture-in-picture/image-resize@2x.png"; sourceTree = "<group>"; };
87
 		C6245F5B2053091D0040BE68 /* image-resize@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "image-resize@2x.png"; path = "src/picture-in-picture/image-resize@2x.png"; sourceTree = "<group>"; };
148
 		0BD906E71EC0C00300C8C18E /* src */ = {
150
 		0BD906E71EC0C00300C8C18E /* src */ = {
149
 			isa = PBXGroup;
151
 			isa = PBXGroup;
150
 			children = (
152
 			children = (
153
+				A480429821ECE2D800289B73 /* analytics */,
151
 				0BB9AD7C1F60356D001C08DB /* AppInfo.m */,
154
 				0BB9AD7C1F60356D001C08DB /* AppInfo.m */,
152
 				0BCA495C1EC4B6C600B793EE /* AudioMode.m */,
155
 				0BCA495C1EC4B6C600B793EE /* AudioMode.m */,
153
 				C69EFA02209A0EFD0027712B /* callkit */,
156
 				C69EFA02209A0EFD0027712B /* callkit */,
183
 			name = Frameworks;
186
 			name = Frameworks;
184
 			sourceTree = "<group>";
187
 			sourceTree = "<group>";
185
 		};
188
 		};
189
+		A480429821ECE2D800289B73 /* analytics */ = {
190
+			isa = PBXGroup;
191
+			children = (
192
+				A480429B21EE335600289B73 /* AmplitudeModule.m */,
193
+			);
194
+			name = analytics;
195
+			path = "New Group";
196
+			sourceTree = "<group>";
197
+		};
186
 		A4A934E7212F3AB8001E9388 /* dropbox */ = {
198
 		A4A934E7212F3AB8001E9388 /* dropbox */ = {
187
 			isa = PBXGroup;
199
 			isa = PBXGroup;
188
 			children = (
200
 			children = (
374
 			);
386
 			);
375
 			inputPaths = (
387
 			inputPaths = (
376
 				"${PODS_ROOT}/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet-resources.sh",
388
 				"${PODS_ROOT}/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet-resources.sh",
389
+				"${PODS_ROOT}/Amplitude-iOS/Amplitude/api.amplitude.com.der",
390
+				"${PODS_ROOT}/Amplitude-iOS/Amplitude/ComodoCaLimitedRsaCertificationAuthority.der",
391
+				"${PODS_ROOT}/Amplitude-iOS/Amplitude/ComodoRsaCA.der",
392
+				"${PODS_ROOT}/Amplitude-iOS/Amplitude/ComodoRsaDomainValidationCA.der",
377
 				"${PODS_ROOT}/GoogleSignIn/Resources/GoogleSignIn.bundle",
393
 				"${PODS_ROOT}/GoogleSignIn/Resources/GoogleSignIn.bundle",
378
 				"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
394
 				"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
379
 				"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
395
 				"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
393
 			);
409
 			);
394
 			name = "[CP] Copy Pods Resources";
410
 			name = "[CP] Copy Pods Resources";
395
 			outputPaths = (
411
 			outputPaths = (
412
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/api.amplitude.com.der",
413
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ComodoCaLimitedRsaCertificationAuthority.der",
414
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ComodoRsaCA.der",
415
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ComodoRsaDomainValidationCA.der",
396
 				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleSignIn.bundle",
416
 				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleSignIn.bundle",
397
 				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf",
417
 				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf",
398
 				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf",
418
 				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf",
433
 				0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */,
453
 				0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */,
434
 				0B44A0191F902126009D1D64 /* MPVolumeViewManager.m in Sources */,
454
 				0B44A0191F902126009D1D64 /* MPVolumeViewManager.m in Sources */,
435
 				0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */,
455
 				0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */,
456
+				A480429C21EE335600289B73 /* AmplitudeModule.m in Sources */,
436
 				C69EFA0C209A0F660027712B /* JMCallKitEmitter.swift in Sources */,
457
 				C69EFA0C209A0F660027712B /* JMCallKitEmitter.swift in Sources */,
437
 				C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */,
458
 				C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */,
438
 				A4A934E9212F3ADB001E9388 /* Dropbox.m in Sources */,
459
 				A4A934E9212F3ADB001E9388 /* Dropbox.m in Sources */,

+ 55
- 0
ios/sdk/src/analytics/AmplitudeModule.m View File

1
+/*
2
+ * Copyright @ 2018-present 8x8, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ *     http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+#import <React/RCTBridgeModule.h>
18
+#import "Amplitude.h"
19
+
20
+@interface AmplitudeModule : NSObject<RCTBridgeModule>
21
+
22
+@end
23
+
24
+@implementation AmplitudeModule
25
+
26
+RCT_EXPORT_MODULE(Amplitude)
27
+
28
++ (BOOL)requiresMainQueueSetup {
29
+    return NO;
30
+}
31
+
32
+RCT_EXPORT_METHOD(init:(NSString*)instanceName API_KEY:(NSString*)apiKey) {
33
+    [[Amplitude instanceWithName:instanceName] initializeApiKey:apiKey];
34
+}
35
+
36
+RCT_EXPORT_METHOD(setUserProperties:(NSString*)instanceName userPropsString:(NSDictionary*)userProps) {
37
+    if (userProps != nil) {
38
+        [[Amplitude instanceWithName:instanceName] setUserProperties:userProps];
39
+    }
40
+}
41
+
42
+RCT_EXPORT_METHOD(logEvent:(NSString*)instanceName eventType:(NSString*)eventType eventPropsString:(NSString*)eventPropsString) {
43
+    NSError *error;
44
+    NSData *eventPropsData = [eventPropsString dataUsingEncoding:NSUTF8StringEncoding];
45
+    NSDictionary *eventProperties = [NSJSONSerialization JSONObjectWithData:eventPropsData
46
+                                                                   options:NSJSONReadingMutableContainers
47
+                                                                     error:&error];
48
+    if (eventProperties == nil) {
49
+        NSLog(@"[Amplitude handler] Error parsing event properties: %@", error);
50
+    } else {
51
+        [[Amplitude instanceWithName:instanceName] logEvent:eventType withEventProperties:eventProperties];
52
+    }
53
+}
54
+
55
+@end

+ 22
- 6
react/features/analytics/functions.js View File

6
 } from '../base/lib-jitsi-meet';
6
 } from '../base/lib-jitsi-meet';
7
 import { getJitsiMeetGlobalNS, loadScript } from '../base/util';
7
 import { getJitsiMeetGlobalNS, loadScript } from '../base/util';
8
 
8
 
9
+import { AmplitudeHandler } from './handlers';
10
+
9
 const logger = require('jitsi-meet-logger').getLogger(__filename);
11
 const logger = require('jitsi-meet-logger').getLogger(__filename);
10
 
12
 
11
 /**
13
 /**
23
     }
25
     }
24
 }
26
 }
25
 
27
 
28
+/**
29
+ * Resets the analytics adapter to its initial state - removes handlers, cache,
30
+ * disabled state, etc.
31
+ *
32
+ * @returns {void}
33
+ */
34
+export function resetAnalytics() {
35
+    analytics.reset();
36
+}
37
+
26
 /**
38
 /**
27
  * Loads the analytics scripts and inits JitsiMeetJS.analytics by setting
39
  * Loads the analytics scripts and inits JitsiMeetJS.analytics by setting
28
  * permanent properties and setting the handlers from the loaded scripts.
40
  * permanent properties and setting the handlers from the loaded scripts.
43
 
55
 
44
     const state = getState();
56
     const state = getState();
45
     const config = state['features/base/config'];
57
     const config = state['features/base/config'];
58
+    const { locationURL } = state['features/base/connection'];
59
+    const host = locationURL ? locationURL.host : '';
60
+
46
     const {
61
     const {
47
         analytics: analyticsConfig = {},
62
         analytics: analyticsConfig = {},
48
         deploymentInfo
63
         deploymentInfo
58
         envType: (deploymentInfo && deploymentInfo.envType) || 'dev',
73
         envType: (deploymentInfo && deploymentInfo.envType) || 'dev',
59
         googleAnalyticsTrackingId,
74
         googleAnalyticsTrackingId,
60
         group,
75
         group,
76
+        host,
61
         product: deploymentInfo && deploymentInfo.product,
77
         product: deploymentInfo && deploymentInfo.product,
62
         subproduct: deploymentInfo && deploymentInfo.environment,
78
         subproduct: deploymentInfo && deploymentInfo.environment,
63
         user: user && user.id,
79
         user: user && user.id,
109
  * successfully loaded and rejects if there are no handlers loaded or the
125
  * successfully loaded and rejects if there are no handlers loaded or the
110
  * analytics is disabled.
126
  * analytics is disabled.
111
  */
127
  */
112
-function _loadHandlers(scriptURLs, handlerConstructorOptions) {
128
+function _loadHandlers(scriptURLs = [], handlerConstructorOptions) {
113
     const promises = [];
129
     const promises = [];
114
 
130
 
115
     for (const url of scriptURLs) {
131
     for (const url of scriptURLs) {
139
         // check the old location to provide legacy support
155
         // check the old location to provide legacy support
140
         const analyticsHandlers = [
156
         const analyticsHandlers = [
141
             ...getJitsiMeetGlobalNS().analyticsHandlers,
157
             ...getJitsiMeetGlobalNS().analyticsHandlers,
142
-            ...window.analyticsHandlers
143
-        ];
158
+            ...window.analyticsHandlers,
144
 
159
 
145
-        if (analyticsHandlers.length === 0) {
146
-            throw new Error('No analytics handlers available');
147
-        }
160
+            // NOTE: when we add second handler it will be good to put all
161
+            // build-in handlers in an array and destruct it here.
162
+            AmplitudeHandler
163
+        ];
148
 
164
 
149
         const handlers = [];
165
         const handlers = [];
150
 
166
 

+ 12
- 15
react/features/analytics/handlers/AmplitudeHandler.js View File

1
-import amplitude from 'amplitude-js';
2
-
3
-import { getJitsiMeetGlobalNS } from '../../base/util';
4
-
5
 import AbstractHandler from './AbstractHandler';
1
 import AbstractHandler from './AbstractHandler';
2
+import { amplitude } from './amplitude';
6
 
3
 
7
 const logger = require('jitsi-meet-logger').getLogger(__filename);
4
 const logger = require('jitsi-meet-logger').getLogger(__filename);
8
 
5
 
9
 /**
6
 /**
10
  * Analytics handler for Amplitude.
7
  * Analytics handler for Amplitude.
11
  */
8
  */
12
-class AmplitudeHandler extends AbstractHandler {
9
+export default class AmplitudeHandler extends AbstractHandler {
13
     /**
10
     /**
14
      * Creates new instance of the Amplitude analytics handler.
11
      * Creates new instance of the Amplitude analytics handler.
15
      *
12
      *
20
     constructor(options) {
17
     constructor(options) {
21
         super();
18
         super();
22
 
19
 
23
-        const { amplitudeAPPKey } = options;
20
+        const { amplitudeAPPKey, host } = options;
24
 
21
 
25
         if (!amplitudeAPPKey) {
22
         if (!amplitudeAPPKey) {
26
             logger.warn(
23
             logger.warn(
27
-                'Failed to initialize Amplitude handler, no tracking ID');
24
+                'Failed to initialize Amplitude handler, no APP key');
28
 
25
 
29
             return;
26
             return;
30
         }
27
         }
31
 
28
 
32
         this._enabled = true;
29
         this._enabled = true;
33
 
30
 
34
-        amplitude.getInstance().init(amplitudeAPPKey);
31
+        this._amplitudeOptions = {
32
+            host
33
+        };
34
+
35
+        amplitude.getInstance(this._amplitudeOptions).init(amplitudeAPPKey);
35
     }
36
     }
36
 
37
 
37
     /**
38
     /**
42
      */
43
      */
43
     setUserProperties(userProps) {
44
     setUserProperties(userProps) {
44
         if (this._enabled) {
45
         if (this._enabled) {
45
-            amplitude.getInstance().setUserProperties(userProps);
46
+            amplitude.getInstance(this._amplitudeOptions)
47
+                .setUserProperties(userProps);
46
         }
48
         }
47
     }
49
     }
48
 
50
 
59
             return;
61
             return;
60
         }
62
         }
61
 
63
 
62
-        amplitude.getInstance().logEvent(
64
+        amplitude.getInstance(this._amplitudeOptions).logEvent(
63
             this._extractName(event),
65
             this._extractName(event),
64
             event);
66
             event);
65
     }
67
     }
66
 }
68
 }
67
-
68
-const globalNS = getJitsiMeetGlobalNS();
69
-
70
-globalNS.analyticsHandlers = globalNS.analyticsHandlers || [];
71
-globalNS.analyticsHandlers.push(AmplitudeHandler);

+ 97
- 0
react/features/analytics/handlers/amplitude/Amplitude.native.js View File

1
+import { NativeModules } from 'react-native';
2
+
3
+const { Amplitude: AmplitudeNative } = NativeModules;
4
+
5
+/**
6
+ * Wrapper for the Amplitude native module.
7
+ */
8
+class Amplitude {
9
+    /**
10
+     * Create new Amplitude instance.
11
+     *
12
+     * @param {string} instanceName - The name of the Amplitude instance. Should
13
+     * be used only for multi-project logging.
14
+     */
15
+    constructor(instanceName) {
16
+        this._instanceName = instanceName;
17
+    }
18
+
19
+    /**
20
+     * Initializes the Amplitude SDK.
21
+     *
22
+     * @param {string} apiKey - The API_KEY of the Amplitude project.
23
+     * @returns {void}
24
+     */
25
+    init(apiKey) {
26
+        AmplitudeNative.init(this._instanceName, apiKey);
27
+    }
28
+
29
+    /**
30
+     * Sets user properties for the current user.
31
+     *
32
+     * @param {Object} userProperties - The user properties to be set.
33
+     * @returns {void}
34
+     */
35
+    setUserProperties(userProperties) {
36
+        AmplitudeNative.setUserProperties(this._instanceName, userProperties);
37
+    }
38
+
39
+    /**
40
+     * Log an event with eventType and eventProperties.
41
+     *
42
+     * @param {string} eventType - The type of the event.
43
+     * @param {Object} eventProperties - The properties of the event.
44
+     * @returns {void}
45
+     */
46
+    logEvent(eventType, eventProperties) {
47
+        // The event properties are converted to JSON string because of known
48
+        // performance issue when passing objects trough the RN bridge too
49
+        // often (a few times a second).
50
+        AmplitudeNative.logEvent(
51
+            this._instanceName, eventType, JSON.stringify(eventProperties));
52
+    }
53
+
54
+}
55
+
56
+/**
57
+ * Cache of <tt>Amplitude</tt> instances by instanceName.
58
+ */
59
+const instances = {};
60
+
61
+/**
62
+ * The default (with instanceName - undefined) <tt>Amplitude</tt> instance.
63
+ */
64
+let defaultInstance;
65
+
66
+export default {
67
+    /**
68
+     * Returns a <tt>Amplitude</tt> instance.
69
+     *
70
+     * @param {Object} options - Optional parameters.
71
+     * @param {string} options.host - The host property from the current URL.
72
+     * @param {string|undefined} options.instanceName - The name of the
73
+     * amplitude instance. Should be used only for multi-project logging.
74
+     * @returns {Amplitude}
75
+     */
76
+    getInstance(options = {}) {
77
+        let instance;
78
+
79
+        const { host = '', instanceName = '' } = options;
80
+
81
+        let internalInstanceName = host;
82
+
83
+        if (instanceName !== '') {
84
+            internalInstanceName += `-${instanceName}`;
85
+        }
86
+
87
+        if (internalInstanceName === '') {
88
+            instance = defaultInstance = defaultInstance || new Amplitude();
89
+        } else {
90
+            instance = instances[internalInstanceName]
91
+                = instances[internalInstanceName]
92
+                    || new Amplitude(internalInstanceName);
93
+        }
94
+
95
+        return instance;
96
+    }
97
+};

+ 7
- 0
react/features/analytics/handlers/amplitude/Amplitude.web.js View File

1
+import amplitude from 'amplitude-js';
2
+
3
+export default {
4
+    getInstance(options = {}) {
5
+        return amplitude.getInstance(options.instanceName);
6
+    }
7
+};

+ 1
- 0
react/features/analytics/handlers/amplitude/index.js View File

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

+ 1
- 0
react/features/analytics/handlers/index.js View File

1
+export { default as AmplitudeHandler } from './AmplitudeHandler';

+ 12
- 1
react/features/analytics/middleware.js View File

1
 import { SET_ROOM } from '../base/conference';
1
 import { SET_ROOM } from '../base/conference';
2
+import { SET_CONFIG } from '../base/config';
2
 import { MiddlewareRegistry } from '../base/redux';
3
 import { MiddlewareRegistry } from '../base/redux';
3
 
4
 
4
-import { initAnalytics } from './functions';
5
+import { initAnalytics, resetAnalytics } from './functions';
5
 
6
 
6
 /**
7
 /**
7
  * Middleware which intercepts config actions to handle evaluating analytics
8
  * Middleware which intercepts config actions to handle evaluating analytics
12
  */
13
  */
13
 MiddlewareRegistry.register(store => next => action => {
14
 MiddlewareRegistry.register(store => next => action => {
14
     switch (action.type) {
15
     switch (action.type) {
16
+    case SET_CONFIG: {
17
+        if (navigator.product === 'ReactNative') {
18
+            // Reseting the analytics is currently not needed for web because
19
+            // the user will be redirected to another page and new instance of
20
+            // Analytics will be created and initialized.
21
+            resetAnalytics();
22
+        }
23
+
24
+        break;
25
+    }
15
     case SET_ROOM: {
26
     case SET_ROOM: {
16
         const result = next(action);
27
         const result = next(action);
17
 
28
 

+ 1
- 10
react/features/base/lib-jitsi-meet/functions.js View File

43
  * otherwise.
43
  * otherwise.
44
  */
44
  */
45
 export function isAnalyticsEnabled(stateful: Function | Object) {
45
 export function isAnalyticsEnabled(stateful: Function | Object) {
46
-    const {
47
-        analytics = {},
48
-        disableThirdPartyRequests
49
-    } = toState(stateful)['features/base/config'];
50
-    const { scriptURLs } = analytics;
51
-
52
-    return (
53
-        !disableThirdPartyRequests
54
-            && Array.isArray(scriptURLs)
55
-            && Boolean(scriptURLs.length));
46
+    return !toState(stateful)['features/base/config'].disableThirdPartyRequests;
56
 }
47
 }
57
 
48
 
58
 /**
49
 /**

+ 1
- 3
webpack.config.js View File

135
                 './react/features/local-recording/'
135
                 './react/features/local-recording/'
136
                     + 'recording/flac/flacEncodeWorker.js',
136
                     + 'recording/flac/flacEncodeWorker.js',
137
             'analytics-ga':
137
             'analytics-ga':
138
-                './react/features/analytics/handlers/GoogleAnalyticsHandler.js',
139
-            'analytics-amplitude':
140
-                './react/features/analytics/handlers/AmplitudeHandler.js'
138
+                './react/features/analytics/handlers/GoogleAnalyticsHandler.js'
141
         }
139
         }
142
     }),
140
     }),
143
 
141
 

Loading…
Cancel
Save